@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 @@
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,120 @@
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 (Array.isArray(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
+ * Parse a custom JSDoc tag value into JSON-compatible data.
92
+ * Values that begin with a JSON token character ({, [, ", true, false, null) or look like a
93
+ * number are parsed with JSON.parse. All other values are returned as plain strings.
94
+ * @param value The raw value text.
95
+ * @returns The parsed value.
96
+ */
97
+ static parseTagValue(value) {
98
+ const trimmedValue = value.trim();
99
+ if (trimmedValue.length === 0) {
100
+ return "";
101
+ }
102
+ const startsWithJsonToken = trimmedValue.startsWith("{") ||
103
+ trimmedValue.startsWith("[") ||
104
+ trimmedValue.startsWith('"') ||
105
+ trimmedValue === "true" ||
106
+ trimmedValue === "false" ||
107
+ trimmedValue === "null" ||
108
+ /^-?\d+(\.\d+)?$/u.test(trimmedValue);
109
+ if (!startsWithJsonToken) {
110
+ return trimmedValue;
111
+ }
112
+ try {
113
+ return JSON.parse(trimmedValue);
114
+ }
115
+ catch {
116
+ return trimmedValue;
117
+ }
118
+ }
119
+ }
120
+ //# 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 (Array.isArray(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 * Parse a custom JSDoc tag value into JSON-compatible data.\n\t * Values that begin with a JSON token character ({, [, \", 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"]}