@twin.org/tools-core 0.0.3-next.9 → 0.0.3

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 (138) hide show
  1. package/README.md +2 -2
  2. package/dist/es/index.js +21 -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/models/embeddedSchemaMode.js +17 -0
  11. package/dist/es/models/embeddedSchemaMode.js.map +1 -0
  12. package/dist/es/utils/constants.js +43 -0
  13. package/dist/es/utils/constants.js.map +1 -0
  14. package/dist/es/utils/diagnosticReporter.js +32 -0
  15. package/dist/es/utils/diagnosticReporter.js.map +1 -0
  16. package/dist/es/utils/disallowedTypeGuard.js +151 -0
  17. package/dist/es/utils/disallowedTypeGuard.js.map +1 -0
  18. package/dist/es/utils/enum.js +152 -0
  19. package/dist/es/utils/enum.js.map +1 -0
  20. package/dist/es/utils/fileUtils.js +132 -0
  21. package/dist/es/utils/fileUtils.js.map +1 -0
  22. package/dist/es/utils/importTypeQuerySchemaResolver.js +363 -0
  23. package/dist/es/utils/importTypeQuerySchemaResolver.js.map +1 -0
  24. package/dist/es/utils/indexSignaturePatternResolver.js +94 -0
  25. package/dist/es/utils/indexSignaturePatternResolver.js.map +1 -0
  26. package/dist/es/utils/intersectionSchemaMerger.js +85 -0
  27. package/dist/es/utils/intersectionSchemaMerger.js.map +1 -0
  28. package/dist/es/utils/jsDoc.js +138 -0
  29. package/dist/es/utils/jsDoc.js.map +1 -0
  30. package/dist/es/utils/jsonSchemaBuilder.js +3415 -0
  31. package/dist/es/utils/jsonSchemaBuilder.js.map +1 -0
  32. package/dist/es/utils/mappedTypeSchemaResolver.js +265 -0
  33. package/dist/es/utils/mappedTypeSchemaResolver.js.map +1 -0
  34. package/dist/es/utils/objectTransformer.js +161 -0
  35. package/dist/es/utils/objectTransformer.js.map +1 -0
  36. package/dist/es/utils/regEx.js +128 -0
  37. package/dist/es/utils/regEx.js.map +1 -0
  38. package/dist/es/utils/resolver.js +177 -0
  39. package/dist/es/utils/resolver.js.map +1 -0
  40. package/dist/es/utils/templateLiteralPatternBuilder.js +94 -0
  41. package/dist/es/utils/templateLiteralPatternBuilder.js.map +1 -0
  42. package/dist/es/utils/typeScriptToSchema.js +319 -0
  43. package/dist/es/utils/typeScriptToSchema.js.map +1 -0
  44. package/dist/es/utils/utilityTypeSchemaMapper.js +475 -0
  45. package/dist/es/utils/utilityTypeSchemaMapper.js.map +1 -0
  46. package/dist/types/index.d.ts +21 -11
  47. package/dist/types/models/ITypeScriptToSchemaContext.d.ts +71 -0
  48. package/dist/types/models/ITypeScriptToSchemaDiagnostics.d.ts +31 -0
  49. package/dist/types/models/ITypeScriptToSchemaOptions.d.ts +22 -0
  50. package/dist/types/models/embeddedSchemaMode.d.ts +17 -0
  51. package/dist/types/utils/constants.d.ts +13 -0
  52. package/dist/types/utils/diagnosticReporter.d.ts +17 -0
  53. package/dist/types/utils/disallowedTypeGuard.d.ts +16 -0
  54. package/dist/types/utils/enum.d.ts +42 -0
  55. package/dist/types/utils/fileUtils.d.ts +66 -0
  56. package/dist/types/utils/importTypeQuerySchemaResolver.d.ts +87 -0
  57. package/dist/types/utils/indexSignaturePatternResolver.d.ts +21 -0
  58. package/dist/types/utils/intersectionSchemaMerger.d.ts +16 -0
  59. package/dist/types/utils/jsDoc.d.ts +53 -0
  60. package/dist/types/utils/jsonSchemaBuilder.d.ts +671 -0
  61. package/dist/types/utils/mappedTypeSchemaResolver.d.ts +81 -0
  62. package/dist/types/utils/objectTransformer.d.ts +33 -0
  63. package/dist/types/utils/regEx.d.ts +24 -0
  64. package/dist/types/utils/resolver.d.ts +22 -0
  65. package/dist/types/utils/templateLiteralPatternBuilder.d.ts +12 -0
  66. package/dist/types/utils/typeScriptToSchema.d.ts +92 -0
  67. package/dist/types/utils/utilityTypeSchemaMapper.d.ts +141 -0
  68. package/docs/changelog.md +337 -40
  69. package/docs/examples.md +87 -1
  70. package/docs/reference/classes/Constants.md +29 -0
  71. package/docs/reference/classes/DiagnosticReporter.md +49 -0
  72. package/docs/reference/classes/DisallowedTypeGuard.md +35 -0
  73. package/docs/reference/classes/Enum.md +93 -0
  74. package/docs/reference/classes/FileUtils.md +237 -0
  75. package/docs/reference/classes/ImportTypeQuerySchemaResolver.md +109 -0
  76. package/docs/reference/classes/IndexSignaturePatternResolver.md +69 -0
  77. package/docs/reference/classes/IntersectionSchemaMerger.md +48 -0
  78. package/docs/reference/classes/JsDoc.md +169 -0
  79. package/docs/reference/classes/JsonSchemaBuilder.md +2578 -0
  80. package/docs/reference/classes/MappedTypeSchemaResolver.md +275 -0
  81. package/docs/reference/classes/ObjectTransformer.md +119 -0
  82. package/docs/reference/classes/RegEx.md +99 -0
  83. package/docs/reference/classes/Resolver.md +52 -0
  84. package/docs/reference/classes/TemplateLiteralPatternBuilder.md +35 -0
  85. package/docs/reference/classes/TypeScriptToSchema.md +91 -0
  86. package/docs/reference/classes/UtilityTypeSchemaMapper.md +343 -0
  87. package/docs/reference/index.md +25 -11
  88. package/docs/reference/interfaces/ITypeScriptToSchemaContext.md +125 -0
  89. package/docs/reference/interfaces/ITypeScriptToSchemaDiagnostics.md +55 -0
  90. package/docs/reference/interfaces/ITypeScriptToSchemaOptions.md +44 -0
  91. package/docs/reference/type-aliases/EmbeddedSchemaMode.md +5 -0
  92. package/docs/reference/variables/EmbeddedSchemaMode.md +19 -0
  93. package/locales/en.json +32 -1
  94. package/package.json +8 -7
  95. package/dist/es/models/IJsonSchema.js +0 -2
  96. package/dist/es/models/IJsonSchema.js.map +0 -1
  97. package/dist/es/models/IOpenApi.js +0 -2
  98. package/dist/es/models/IOpenApi.js.map +0 -1
  99. package/dist/es/models/IOpenApiExample.js +0 -4
  100. package/dist/es/models/IOpenApiExample.js.map +0 -1
  101. package/dist/es/models/IOpenApiHeader.js +0 -4
  102. package/dist/es/models/IOpenApiHeader.js.map +0 -1
  103. package/dist/es/models/IOpenApiPathMethod.js +0 -2
  104. package/dist/es/models/IOpenApiPathMethod.js.map +0 -1
  105. package/dist/es/models/IOpenApiResponse.js +0 -2
  106. package/dist/es/models/IOpenApiResponse.js.map +0 -1
  107. package/dist/es/models/IOpenApiSecurityScheme.js +0 -4
  108. package/dist/es/models/IOpenApiSecurityScheme.js.map +0 -1
  109. package/dist/es/models/IPackageJson.js +0 -4
  110. package/dist/es/models/IPackageJson.js.map +0 -1
  111. package/dist/es/models/jsonTypeName.js +0 -2
  112. package/dist/es/models/jsonTypeName.js.map +0 -1
  113. package/dist/es/utils/jsonSchemaHelper.js +0 -258
  114. package/dist/es/utils/jsonSchemaHelper.js.map +0 -1
  115. package/dist/es/utils/openApiHelper.js +0 -12
  116. package/dist/es/utils/openApiHelper.js.map +0 -1
  117. package/dist/types/models/IJsonSchema.d.ts +0 -5
  118. package/dist/types/models/IOpenApi.d.ts +0 -54
  119. package/dist/types/models/IOpenApiExample.d.ts +0 -13
  120. package/dist/types/models/IOpenApiHeader.d.ts +0 -19
  121. package/dist/types/models/IOpenApiPathMethod.d.ts +0 -65
  122. package/dist/types/models/IOpenApiResponse.d.ts +0 -32
  123. package/dist/types/models/IOpenApiSecurityScheme.d.ts +0 -25
  124. package/dist/types/models/IPackageJson.d.ts +0 -15
  125. package/dist/types/models/jsonTypeName.d.ts +0 -5
  126. package/dist/types/utils/jsonSchemaHelper.d.ts +0 -78
  127. package/dist/types/utils/openApiHelper.d.ts +0 -9
  128. package/docs/reference/classes/JsonSchemaHelper.md +0 -233
  129. package/docs/reference/classes/OpenApiHelper.md +0 -21
  130. package/docs/reference/interfaces/IOpenApi.md +0 -103
  131. package/docs/reference/interfaces/IOpenApiExample.md +0 -19
  132. package/docs/reference/interfaces/IOpenApiHeader.md +0 -31
  133. package/docs/reference/interfaces/IOpenApiPathMethod.md +0 -119
  134. package/docs/reference/interfaces/IOpenApiResponse.md +0 -35
  135. package/docs/reference/interfaces/IOpenApiSecurityScheme.md +0 -43
  136. package/docs/reference/interfaces/IPackageJson.md +0 -23
  137. package/docs/reference/type-aliases/IJsonSchema.md +0 -5
  138. package/docs/reference/type-aliases/JsonTypeName.md +0 -5
@@ -0,0 +1,94 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import * as ts from "typescript";
4
+ import { TemplateLiteralPatternBuilder } from "./templateLiteralPatternBuilder.js";
5
+ /**
6
+ * Resolves regex patterns for index signature key types.
7
+ */
8
+ export class IndexSignaturePatternResolver {
9
+ /**
10
+ * Determine whether an index signature can be represented as JSON object additionalProperties.
11
+ * @param member The index signature declaration.
12
+ * @returns True if supported.
13
+ */
14
+ static isSupportedIndexSignature(member) {
15
+ // [key: string]: Value (index signature, first parameter is the key)
16
+ const indexParameter = member.parameters[0];
17
+ if (!indexParameter?.type) {
18
+ return false;
19
+ }
20
+ // [key: string]: Value or [key: number]: Value (string or numeric key)
21
+ return (indexParameter.type.kind === ts.SyntaxKind.StringKeyword ||
22
+ indexParameter.type.kind === ts.SyntaxKind.NumberKeyword);
23
+ }
24
+ /**
25
+ * Extract a regex pattern for an index signature key type when representable.
26
+ * @param context The generation context.
27
+ * @param member The index signature declaration.
28
+ * @param getTypeParameterBinding Callback for resolving generic bindings.
29
+ * @returns The property-name pattern, if derivable.
30
+ */
31
+ static extractIndexSignaturePattern(context, member, getTypeParameterBinding) {
32
+ const indexParameterType = member.parameters[0]?.type;
33
+ if (!indexParameterType) {
34
+ return undefined;
35
+ }
36
+ const resolvedTemplateType = IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(context, indexParameterType, new Set(), getTypeParameterBinding);
37
+ return resolvedTemplateType
38
+ ? TemplateLiteralPatternBuilder.buildTemplateLiteralPattern(resolvedTemplateType)
39
+ : undefined;
40
+ }
41
+ /**
42
+ * Resolve a template literal type node from a key-type expression.
43
+ * Handles parenthesised, readonly-wrapped, and type-aliased variants by recursing.
44
+ * A cycle guard via resolvingTypeNames prevents unbounded recursion on self-referential types.
45
+ * @param context The generation context.
46
+ * @param typeNode The key type node.
47
+ * @param resolvingTypeNames Type names currently being resolved.
48
+ * @param getTypeParameterBinding Callback for resolving generic bindings.
49
+ * @returns A template literal type node when resolvable.
50
+ * @internal
51
+ */
52
+ static resolveTemplateLiteralTypeNode(context, typeNode, resolvingTypeNames, getTypeParameterBinding) {
53
+ // `prefix-${T}` or `${K}` (already a template literal type)
54
+ if (ts.isTemplateLiteralTypeNode(typeNode)) {
55
+ return typeNode;
56
+ }
57
+ // (KeyType) (parenthesised type, unwrap and recurse)
58
+ if (ts.isParenthesizedTypeNode(typeNode)) {
59
+ return IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(context, typeNode.type, resolvingTypeNames, getTypeParameterBinding);
60
+ }
61
+ // readonly KeyType (readonly-wrapped type operator, strip modifier and recurse)
62
+ if (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) {
63
+ return IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(context, typeNode.type, resolvingTypeNames, getTypeParameterBinding);
64
+ }
65
+ // MyKeyType (named type reference, resolve via binding or type alias in source file)
66
+ if (ts.isTypeReferenceNode(typeNode)) {
67
+ // Namespace.TypeName (qualified name) or TypeName (plain identifier)
68
+ const typeName = ts.isIdentifier(typeNode.typeName)
69
+ ? typeNode.typeName.text
70
+ : typeNode.typeName.right.text;
71
+ if (resolvingTypeNames.has(typeName)) {
72
+ return undefined;
73
+ }
74
+ const typeParameterBinding = getTypeParameterBinding(context, typeName);
75
+ if (typeParameterBinding) {
76
+ return IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(context, typeParameterBinding, resolvingTypeNames, getTypeParameterBinding);
77
+ }
78
+ const sourceFile = context.activeSourceFile;
79
+ if (!sourceFile) {
80
+ return undefined;
81
+ }
82
+ // type MyKeyType = `prefix-${string}` (look up a type alias in the active source file)
83
+ const typeAlias = sourceFile.statements.find((statement) => ts.isTypeAliasDeclaration(statement) && statement.name.text === typeName);
84
+ if (typeAlias) {
85
+ resolvingTypeNames.add(typeName);
86
+ const resolvedTemplateType = IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(context, typeAlias.type, resolvingTypeNames, getTypeParameterBinding);
87
+ resolvingTypeNames.delete(typeName);
88
+ return resolvedTemplateType;
89
+ }
90
+ }
91
+ return undefined;
92
+ }
93
+ }
94
+ //# sourceMappingURL=indexSignaturePatternResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexSignaturePatternResolver.js","sourceRoot":"","sources":["../../../src/utils/indexSignaturePatternResolver.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport * as ts from \"typescript\";\nimport { TemplateLiteralPatternBuilder } from \"./templateLiteralPatternBuilder.js\";\nimport type { ITypeScriptToSchemaContext } from \"../models/ITypeScriptToSchemaContext.js\";\n\n/**\n * Resolves regex patterns for index signature key types.\n */\nexport class IndexSignaturePatternResolver {\n\t/**\n\t * Determine whether an index signature can be represented as JSON object additionalProperties.\n\t * @param member The index signature declaration.\n\t * @returns True if supported.\n\t */\n\tpublic static isSupportedIndexSignature(member: ts.IndexSignatureDeclaration): boolean {\n\t\t// [key: string]: Value (index signature, first parameter is the key)\n\t\tconst indexParameter = member.parameters[0];\n\t\tif (!indexParameter?.type) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// [key: string]: Value or [key: number]: Value (string or numeric key)\n\t\treturn (\n\t\t\tindexParameter.type.kind === ts.SyntaxKind.StringKeyword ||\n\t\t\tindexParameter.type.kind === ts.SyntaxKind.NumberKeyword\n\t\t);\n\t}\n\n\t/**\n\t * Extract a regex pattern for an index signature key type when representable.\n\t * @param context The generation context.\n\t * @param member The index signature declaration.\n\t * @param getTypeParameterBinding Callback for resolving generic bindings.\n\t * @returns The property-name pattern, if derivable.\n\t */\n\tpublic static extractIndexSignaturePattern(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tmember: ts.IndexSignatureDeclaration,\n\t\tgetTypeParameterBinding: (\n\t\t\tcontext: ITypeScriptToSchemaContext,\n\t\t\ttypeName: string\n\t\t) => ts.TypeNode | null | undefined\n\t): string | undefined {\n\t\tconst indexParameterType = member.parameters[0]?.type;\n\t\tif (!indexParameterType) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst resolvedTemplateType = IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(\n\t\t\tcontext,\n\t\t\tindexParameterType,\n\t\t\tnew Set<string>(),\n\t\t\tgetTypeParameterBinding\n\t\t);\n\n\t\treturn resolvedTemplateType\n\t\t\t? TemplateLiteralPatternBuilder.buildTemplateLiteralPattern(resolvedTemplateType)\n\t\t\t: undefined;\n\t}\n\n\t/**\n\t * Resolve a template literal type node from a key-type expression.\n\t * Handles parenthesised, readonly-wrapped, and type-aliased variants by recursing.\n\t * A cycle guard via resolvingTypeNames prevents unbounded recursion on self-referential types.\n\t * @param context The generation context.\n\t * @param typeNode The key type node.\n\t * @param resolvingTypeNames Type names currently being resolved.\n\t * @param getTypeParameterBinding Callback for resolving generic bindings.\n\t * @returns A template literal type node when resolvable.\n\t * @internal\n\t */\n\tprivate static resolveTemplateLiteralTypeNode(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\ttypeNode: ts.TypeNode,\n\t\tresolvingTypeNames: Set<string>,\n\t\tgetTypeParameterBinding: (\n\t\t\tcontext: ITypeScriptToSchemaContext,\n\t\t\ttypeName: string\n\t\t) => ts.TypeNode | null | undefined\n\t): ts.TemplateLiteralTypeNode | undefined {\n\t\t// `prefix-${T}` or `${K}` (already a template literal type)\n\t\tif (ts.isTemplateLiteralTypeNode(typeNode)) {\n\t\t\treturn typeNode;\n\t\t}\n\n\t\t// (KeyType) (parenthesised type, unwrap and recurse)\n\t\tif (ts.isParenthesizedTypeNode(typeNode)) {\n\t\t\treturn IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(\n\t\t\t\tcontext,\n\t\t\t\ttypeNode.type,\n\t\t\t\tresolvingTypeNames,\n\t\t\t\tgetTypeParameterBinding\n\t\t\t);\n\t\t}\n\n\t\t// readonly KeyType (readonly-wrapped type operator, strip modifier and recurse)\n\t\tif (ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) {\n\t\t\treturn IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(\n\t\t\t\tcontext,\n\t\t\t\ttypeNode.type,\n\t\t\t\tresolvingTypeNames,\n\t\t\t\tgetTypeParameterBinding\n\t\t\t);\n\t\t}\n\n\t\t// MyKeyType (named type reference, resolve via binding or type alias in source file)\n\t\tif (ts.isTypeReferenceNode(typeNode)) {\n\t\t\t// Namespace.TypeName (qualified name) or TypeName (plain identifier)\n\t\t\tconst typeName = ts.isIdentifier(typeNode.typeName)\n\t\t\t\t? typeNode.typeName.text\n\t\t\t\t: typeNode.typeName.right.text;\n\n\t\t\tif (resolvingTypeNames.has(typeName)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst typeParameterBinding = getTypeParameterBinding(context, typeName);\n\t\t\tif (typeParameterBinding) {\n\t\t\t\treturn IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(\n\t\t\t\t\tcontext,\n\t\t\t\t\ttypeParameterBinding,\n\t\t\t\t\tresolvingTypeNames,\n\t\t\t\t\tgetTypeParameterBinding\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst sourceFile = context.activeSourceFile;\n\t\t\tif (!sourceFile) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// type MyKeyType = `prefix-${string}` (look up a type alias in the active source file)\n\t\t\tconst typeAlias = sourceFile.statements.find(\n\t\t\t\t(statement): statement is ts.TypeAliasDeclaration =>\n\t\t\t\t\tts.isTypeAliasDeclaration(statement) && statement.name.text === typeName\n\t\t\t);\n\n\t\t\tif (typeAlias) {\n\t\t\t\tresolvingTypeNames.add(typeName);\n\t\t\t\tconst resolvedTemplateType = IndexSignaturePatternResolver.resolveTemplateLiteralTypeNode(\n\t\t\t\t\tcontext,\n\t\t\t\t\ttypeAlias.type,\n\t\t\t\t\tresolvingTypeNames,\n\t\t\t\t\tgetTypeParameterBinding\n\t\t\t\t);\n\t\t\t\tresolvingTypeNames.delete(typeName);\n\t\t\t\treturn resolvedTemplateType;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -0,0 +1,85 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { Is, ObjectHelper } from "@twin.org/core";
4
+ /**
5
+ * Merges compatible object intersections into a single JSON schema object.
6
+ */
7
+ export class IntersectionSchemaMerger {
8
+ /**
9
+ * Merge simple object intersection parts into a single object schema.
10
+ * Supports local/known object refs by expanding them before merge.
11
+ * @param context The generation context.
12
+ * @param schemas The mapped intersection schemas.
13
+ * @param toInlineUtilityObjectSchema Callback for converting referenced schemas to inline forms.
14
+ * @returns The merged schema, or undefined when merge is not safe.
15
+ */
16
+ static mergeIntersectionObjectSchemas(context, schemas, toInlineUtilityObjectSchema) {
17
+ const mergedProperties = {};
18
+ const mergedRequired = new Set();
19
+ const preservedRefs = new Set();
20
+ for (const schema of schemas) {
21
+ const isRefSchema = Is.stringValue(schema.$ref);
22
+ if (isRefSchema && schema.$ref) {
23
+ preservedRefs.add(schema.$ref);
24
+ }
25
+ const objectSchema = IntersectionSchemaMerger.resolveIntersectionObjectSchema(context, schema, toInlineUtilityObjectSchema);
26
+ if (!objectSchema) {
27
+ return undefined;
28
+ }
29
+ if (objectSchema.$ref ||
30
+ objectSchema.allOf ||
31
+ objectSchema.anyOf ||
32
+ objectSchema.oneOf ||
33
+ objectSchema.items) {
34
+ return undefined;
35
+ }
36
+ if (!isRefSchema) {
37
+ if (Is.object(objectSchema.properties)) {
38
+ for (const [propertyName, propertySchema] of Object.entries(objectSchema.properties)) {
39
+ mergedProperties[propertyName] = propertySchema;
40
+ }
41
+ }
42
+ if (Is.array(objectSchema.required)) {
43
+ for (const propertyName of objectSchema.required) {
44
+ mergedRequired.add(propertyName);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ return {
50
+ type: "object",
51
+ properties: Object.keys(mergedProperties).length > 0 ? mergedProperties : undefined,
52
+ required: mergedRequired.size > 0 ? [...mergedRequired] : undefined,
53
+ allOf: preservedRefs.size > 0
54
+ ? [...preservedRefs].map(schemaRef => ({
55
+ $ref: schemaRef
56
+ }))
57
+ : undefined
58
+ };
59
+ }
60
+ /**
61
+ * Resolve an intersection component to a plain object schema when possible.
62
+ * @param context The generation context.
63
+ * @param schema The mapped intersection component schema.
64
+ * @returns The resolved object schema.
65
+ * @internal
66
+ */
67
+ static resolveIntersectionObjectSchema(context, schema, toInlineUtilityObjectSchema) {
68
+ if (schema.type === "object" && Is.object(schema.properties)) {
69
+ return ObjectHelper.clone(schema);
70
+ }
71
+ if (schema.$ref) {
72
+ for (const packageSchemas of Object.values(context.schemas)) {
73
+ for (const referencedSchema of Object.values(packageSchemas)) {
74
+ if (referencedSchema.$id === schema.$ref &&
75
+ referencedSchema.type === "object" &&
76
+ Is.object(referencedSchema.properties)) {
77
+ return toInlineUtilityObjectSchema(referencedSchema);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ }
85
+ //# sourceMappingURL=intersectionSchemaMerger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intersectionSchemaMerger.js","sourceRoot":"","sources":["../../../src/utils/intersectionSchemaMerger.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\";\nimport type { ITypeScriptToSchemaContext } from \"../models/ITypeScriptToSchemaContext.js\";\n\n/**\n * Merges compatible object intersections into a single JSON schema object.\n */\nexport class IntersectionSchemaMerger {\n\t/**\n\t * Merge simple object intersection parts into a single object schema.\n\t * Supports local/known object refs by expanding them before merge.\n\t * @param context The generation context.\n\t * @param schemas The mapped intersection schemas.\n\t * @param toInlineUtilityObjectSchema Callback for converting referenced schemas to inline forms.\n\t * @returns The merged schema, or undefined when merge is not safe.\n\t */\n\tpublic static mergeIntersectionObjectSchemas(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tschemas: IJsonSchema[],\n\t\ttoInlineUtilityObjectSchema: (schema: IJsonSchema) => IJsonSchema\n\t): IJsonSchema | undefined {\n\t\tconst mergedProperties: { [id: string]: IJsonSchema } = {};\n\t\tconst mergedRequired = new Set<string>();\n\t\tconst preservedRefs = new Set<string>();\n\n\t\tfor (const schema of schemas) {\n\t\t\tconst isRefSchema = Is.stringValue(schema.$ref);\n\t\t\tif (isRefSchema && schema.$ref) {\n\t\t\t\tpreservedRefs.add(schema.$ref);\n\t\t\t}\n\n\t\t\tconst objectSchema = IntersectionSchemaMerger.resolveIntersectionObjectSchema(\n\t\t\t\tcontext,\n\t\t\t\tschema,\n\t\t\t\ttoInlineUtilityObjectSchema\n\t\t\t);\n\t\t\tif (!objectSchema) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tobjectSchema.$ref ||\n\t\t\t\tobjectSchema.allOf ||\n\t\t\t\tobjectSchema.anyOf ||\n\t\t\t\tobjectSchema.oneOf ||\n\t\t\t\tobjectSchema.items\n\t\t\t) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tif (!isRefSchema) {\n\t\t\t\tif (Is.object(objectSchema.properties)) {\n\t\t\t\t\tfor (const [propertyName, propertySchema] of Object.entries(objectSchema.properties)) {\n\t\t\t\t\t\tmergedProperties[propertyName] = propertySchema;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (Is.array(objectSchema.required)) {\n\t\t\t\t\tfor (const propertyName of objectSchema.required) {\n\t\t\t\t\t\tmergedRequired.add(propertyName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"object\",\n\t\t\tproperties: Object.keys(mergedProperties).length > 0 ? mergedProperties : undefined,\n\t\t\trequired: mergedRequired.size > 0 ? [...mergedRequired] : undefined,\n\t\t\tallOf:\n\t\t\t\tpreservedRefs.size > 0\n\t\t\t\t\t? [...preservedRefs].map(schemaRef => ({\n\t\t\t\t\t\t\t$ref: schemaRef\n\t\t\t\t\t\t}))\n\t\t\t\t\t: undefined\n\t\t};\n\t}\n\n\t/**\n\t * Resolve an intersection component to a plain object schema when possible.\n\t * @param context The generation context.\n\t * @param schema The mapped intersection component schema.\n\t * @returns The resolved object schema.\n\t * @internal\n\t */\n\tprivate static resolveIntersectionObjectSchema(\n\t\tcontext: ITypeScriptToSchemaContext,\n\t\tschema: IJsonSchema,\n\t\ttoInlineUtilityObjectSchema: (schema: IJsonSchema) => IJsonSchema\n\t): IJsonSchema | undefined {\n\t\tif (schema.type === \"object\" && Is.object(schema.properties)) {\n\t\t\treturn ObjectHelper.clone(schema);\n\t\t}\n\n\t\tif (schema.$ref) {\n\t\t\tfor (const packageSchemas of Object.values(context.schemas)) {\n\t\t\t\tfor (const referencedSchema of Object.values(packageSchemas)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\treferencedSchema.$id === schema.$ref &&\n\t\t\t\t\t\treferencedSchema.type === \"object\" &&\n\t\t\t\t\t\tIs.object(referencedSchema.properties)\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn toInlineUtilityObjectSchema(referencedSchema);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -0,0 +1,138 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { Is } from "@twin.org/core";
4
+ import * as ts from "typescript";
5
+ /**
6
+ * General-purpose utility methods for working with JsDoc.
7
+ */
8
+ export class JsDoc {
9
+ /**
10
+ * Extract the JSDoc description comment for an AST node.
11
+ * Only top-level JSDoc block comments are considered; inline tags are ignored.
12
+ * @param node The node to inspect.
13
+ * @returns The trimmed description text, or undefined when absent.
14
+ */
15
+ static getNodeJsDocDescription(node) {
16
+ // /** Description text */ (JSDoc block attached to a declaration)
17
+ const jsDocNodes = ts
18
+ .getJSDocCommentsAndTags(node)
19
+ .filter(commentOrTag => ts.isJSDoc(commentOrTag));
20
+ const description = jsDocNodes
21
+ .map(jsDocNode => ts.getTextOfJSDocComment(jsDocNode.comment)?.trim())
22
+ .find((value) => Boolean(value));
23
+ if (description) {
24
+ return description;
25
+ }
26
+ return undefined;
27
+ }
28
+ /**
29
+ * Convert a JSDoc tag's comment portion to a plain text string.
30
+ * JSDoc tag comments may be plain strings or arrays of link/text parts.
31
+ * @param jsDocTag The JSDoc tag.
32
+ * @returns The comment text, or undefined when absent.
33
+ */
34
+ static getJSDocTagCommentText(jsDocTag) {
35
+ if (Is.string(jsDocTag.comment)) {
36
+ return jsDocTag.comment;
37
+ }
38
+ if (Is.array(jsDocTag.comment)) {
39
+ return jsDocTag.comment
40
+ .map(part => part.text)
41
+ .join("")
42
+ .trim();
43
+ }
44
+ return undefined;
45
+ }
46
+ /**
47
+ * Read all custom JSDoc tags matching a tag name from a node and convert them to key/value pairs.
48
+ * Each matching tag's comment must be in the form "key: value"; entries that do not follow this
49
+ * convention are silently skipped.
50
+ * @param node The node to inspect.
51
+ * @param tagName The tag name to filter by (e.g., 'json-schema').
52
+ * @returns The extracted key/value pairs.
53
+ */
54
+ static getNodeTags(node, tagName) {
55
+ const output = {};
56
+ for (const jsDocTag of ts.getJSDocTags(node)) {
57
+ if (jsDocTag.tagName.text === tagName) {
58
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag);
59
+ if (commentText) {
60
+ const separatorIndex = commentText.indexOf(":");
61
+ if (separatorIndex > 0) {
62
+ const key = commentText.slice(0, separatorIndex).trim();
63
+ const value = commentText.slice(separatorIndex + 1).trim();
64
+ if (key.length > 0) {
65
+ output[key] = value;
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return output;
72
+ }
73
+ /**
74
+ * Read the plain comment text for the first matching JSDoc tag on a node.
75
+ * @param node The node to inspect.
76
+ * @param tagName The tag name to filter by (e.g., 'default').
77
+ * @returns The trimmed comment text, or undefined when absent.
78
+ */
79
+ static getNodeTagComment(node, tagName) {
80
+ for (const jsDocTag of ts.getJSDocTags(node)) {
81
+ if (jsDocTag.tagName.text === tagName) {
82
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();
83
+ if (commentText) {
84
+ return commentText;
85
+ }
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+ /**
91
+ * Read the plain comment text for all matching JSDoc tags on a node.
92
+ * @param node The node to inspect.
93
+ * @param tagName The tag name to filter by (e.g., 'example').
94
+ * @returns An array of trimmed comment texts for all matching tags.
95
+ */
96
+ static getNodeTagComments(node, tagName) {
97
+ const results = [];
98
+ for (const jsDocTag of ts.getJSDocTags(node)) {
99
+ if (jsDocTag.tagName.text === tagName) {
100
+ const commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();
101
+ if (commentText) {
102
+ results.push(commentText);
103
+ }
104
+ }
105
+ }
106
+ return results;
107
+ }
108
+ /**
109
+ * Parse a custom JSDoc tag value into JSON-compatible data.
110
+ * Values that begin with a JSON token character (open brace, open bracket, double quote, true, false, null) or look like a
111
+ * number are parsed with JSON.parse. All other values are returned as plain strings.
112
+ * @param value The raw value text.
113
+ * @returns The parsed value.
114
+ */
115
+ static parseTagValue(value) {
116
+ const trimmedValue = value.trim();
117
+ if (trimmedValue.length === 0) {
118
+ return "";
119
+ }
120
+ const startsWithJsonToken = trimmedValue.startsWith("{") ||
121
+ trimmedValue.startsWith("[") ||
122
+ trimmedValue.startsWith('"') ||
123
+ trimmedValue === "true" ||
124
+ trimmedValue === "false" ||
125
+ trimmedValue === "null" ||
126
+ /^-?\d+(\.\d+)?$/u.test(trimmedValue);
127
+ if (!startsWithJsonToken) {
128
+ return trimmedValue;
129
+ }
130
+ try {
131
+ return JSON.parse(trimmedValue);
132
+ }
133
+ catch {
134
+ return trimmedValue;
135
+ }
136
+ }
137
+ }
138
+ //# sourceMappingURL=jsDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsDoc.js","sourceRoot":"","sources":["../../../src/utils/jsDoc.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is } from \"@twin.org/core\";\nimport * as ts from \"typescript\";\n\n/**\n * General-purpose utility methods for working with JsDoc.\n */\nexport class JsDoc {\n\t/**\n\t * Extract the JSDoc description comment for an AST node.\n\t * Only top-level JSDoc block comments are considered; inline tags are ignored.\n\t * @param node The node to inspect.\n\t * @returns The trimmed description text, or undefined when absent.\n\t */\n\tpublic static getNodeJsDocDescription(node: ts.Node): string | undefined {\n\t\t// /** Description text */ (JSDoc block attached to a declaration)\n\t\tconst jsDocNodes = ts\n\t\t\t.getJSDocCommentsAndTags(node)\n\t\t\t.filter(commentOrTag => ts.isJSDoc(commentOrTag));\n\t\tconst description = jsDocNodes\n\t\t\t.map(jsDocNode => ts.getTextOfJSDocComment(jsDocNode.comment)?.trim())\n\t\t\t.find((value): value is string => Boolean(value));\n\n\t\tif (description) {\n\t\t\treturn description;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Convert a JSDoc tag's comment portion to a plain text string.\n\t * JSDoc tag comments may be plain strings or arrays of link/text parts.\n\t * @param jsDocTag The JSDoc tag.\n\t * @returns The comment text, or undefined when absent.\n\t */\n\tpublic static getJSDocTagCommentText(jsDocTag: ts.JSDocTag): string | undefined {\n\t\tif (Is.string(jsDocTag.comment)) {\n\t\t\treturn jsDocTag.comment;\n\t\t}\n\n\t\tif (Is.array(jsDocTag.comment)) {\n\t\t\treturn jsDocTag.comment\n\t\t\t\t.map(part => part.text)\n\t\t\t\t.join(\"\")\n\t\t\t\t.trim();\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Read all custom JSDoc tags matching a tag name from a node and convert them to key/value pairs.\n\t * Each matching tag's comment must be in the form \"key: value\"; entries that do not follow this\n\t * convention are silently skipped.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'json-schema').\n\t * @returns The extracted key/value pairs.\n\t */\n\tpublic static getNodeTags(node: ts.Node, tagName: string): { [id: string]: string } {\n\t\tconst output: { [id: string]: string } = {};\n\t\tfor (const jsDocTag of ts.getJSDocTags(node)) {\n\t\t\tif (jsDocTag.tagName.text === tagName) {\n\t\t\t\tconst commentText = JsDoc.getJSDocTagCommentText(jsDocTag);\n\t\t\t\tif (commentText) {\n\t\t\t\t\tconst separatorIndex = commentText.indexOf(\":\");\n\t\t\t\t\tif (separatorIndex > 0) {\n\t\t\t\t\t\tconst key = commentText.slice(0, separatorIndex).trim();\n\t\t\t\t\t\tconst value = commentText.slice(separatorIndex + 1).trim();\n\t\t\t\t\t\tif (key.length > 0) {\n\t\t\t\t\t\t\toutput[key] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn output;\n\t}\n\n\t/**\n\t * Read the plain comment text for the first matching JSDoc tag on a node.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'default').\n\t * @returns The trimmed comment text, or undefined when absent.\n\t */\n\tpublic static getNodeTagComment(node: ts.Node, tagName: string): string | undefined {\n\t\tfor (const jsDocTag of ts.getJSDocTags(node)) {\n\t\t\tif (jsDocTag.tagName.text === tagName) {\n\t\t\t\tconst commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();\n\t\t\t\tif (commentText) {\n\t\t\t\t\treturn commentText;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Read the plain comment text for all matching JSDoc tags on a node.\n\t * @param node The node to inspect.\n\t * @param tagName The tag name to filter by (e.g., 'example').\n\t * @returns An array of trimmed comment texts for all matching tags.\n\t */\n\tpublic static getNodeTagComments(node: ts.Node, tagName: string): string[] {\n\t\tconst results: string[] = [];\n\t\tfor (const jsDocTag of ts.getJSDocTags(node)) {\n\t\t\tif (jsDocTag.tagName.text === tagName) {\n\t\t\t\tconst commentText = JsDoc.getJSDocTagCommentText(jsDocTag)?.trim();\n\t\t\t\tif (commentText) {\n\t\t\t\t\tresults.push(commentText);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\t/**\n\t * Parse a custom JSDoc tag value into JSON-compatible data.\n\t * Values that begin with a JSON token character (open brace, open bracket, double quote, true, false, null) or look like a\n\t * number are parsed with JSON.parse. All other values are returned as plain strings.\n\t * @param value The raw value text.\n\t * @returns The parsed value.\n\t */\n\tpublic static parseTagValue(value: string): unknown {\n\t\tconst trimmedValue = value.trim();\n\t\tif (trimmedValue.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst startsWithJsonToken =\n\t\t\ttrimmedValue.startsWith(\"{\") ||\n\t\t\ttrimmedValue.startsWith(\"[\") ||\n\t\t\ttrimmedValue.startsWith('\"') ||\n\t\t\ttrimmedValue === \"true\" ||\n\t\t\ttrimmedValue === \"false\" ||\n\t\t\ttrimmedValue === \"null\" ||\n\t\t\t/^-?\\d+(\\.\\d+)?$/u.test(trimmedValue);\n\n\t\tif (!startsWithJsonToken) {\n\t\t\treturn trimmedValue;\n\t\t}\n\n\t\ttry {\n\t\t\treturn JSON.parse(trimmedValue);\n\t\t} catch {\n\t\t\treturn trimmedValue;\n\t\t}\n\t}\n}\n"]}