Repository: hackolade/GraphQL
Pull request #94: HCK-10333: add object type definitions RE
Author: login: taras-dubyk
Created: 2025-03-21T18:05:24Z | Merged: 2025-03-24T14:20:55Z

<!--do not remove this marker, its needed to replace info when ticket title is updated -->
<!--jira-description-action-hidden-marker-start-->

<table>
<td>
  <a href="https://hackolade.atlassian.net/browse/HCK-10333" title="HCK-10333" target="_blank"><img alt="Sub-task" src="https://hackolade.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium" />HCK-10333</a>  Add object type definitions RE
  </td></table>
  <br />
 

<!--jira-description-action-hidden-marker-end-->
## Content

This PR adds reverse engineering for object type definitions.


Diff:
diff --git a/forward_engineering/mappers/fieldDefaultValue.js b/forward_engineering/mappers/fieldDefaultValue.js
index 6b1169c..97b985d 100644
--- a/forward_engineering/mappers/fieldDefaultValue.js
+++ b/forward_engineering/mappers/fieldDefaultValue.js
@@ -1,12 +1,12 @@
 /**
- * @import {FieldData} from "../../shared/types/types"
+ * @import {DirectivePropertyData, FieldData} from "../../shared/types/types"
  */
 
 /**
  * Generates the default value statement for a field.
  *
  * @param {object} param0
- * @param {FieldData} param0.field - The field object.
+ * @param {FieldData<DirectivePropertyData>} param0.field - The field object.
  * @returns {string} - The default value statement.
  */
 function getFieldDefaultValueStatement({ field }) {
diff --git a/forward_engineering/mappers/fields.js b/forward_engineering/mappers/fields.js
index 39a4e4e..ab2d49a 100644
--- a/forward_engineering/mappers/fields.js
+++ b/forward_engineering/mappers/fields.js
@@ -1,5 +1,5 @@
 /**
- * @import {FEStatement, BaseGetFieldParams, GetFieldsParams, ArrayItems, FieldData, ArrayItem, IdToNameMap} from "../../shared/types/types"
+ * @import {FEStatement, BaseGetFieldParams, GetFieldsParams, ArrayItems, FieldData, ArrayItem, IdToNameMap, DirectivePropertyData} from "../../shared/types/types"
  */
 
 const { joinInlineStatements } = require('../helpers/feStatementJoinHelper');
@@ -30,7 +30,7 @@ function getFields({ fields = {}, requiredFields = [], definitionsIdToNameMap, a
 
 /**
  * @param {object} params
- * @param {FieldData} params.fieldData
+ * @param {FieldData<DirectivePropertyData>} params.fieldData
  * @returns {string}
  */
 function getFieldDescription({ fieldData }) {
@@ -48,7 +48,7 @@ function getFieldDescription({ fieldData }) {
  *
  * @param {object} param0
  * @param {string} param0.name - The name of the field.
- * @param {FieldData} param0.fieldData - The field data object.
+ * @param {FieldData<DirectivePropertyData>} param0.fieldData - The field data object.
  * @param {boolean} param0.required - Indicates if the field is required.
  * @param {IdToNameMap} param0.definitionsIdToNameMap - The definitions id to name map.
  * @param {boolean} param0.addArguments - Indicates if arguments should be added.
@@ -79,7 +79,7 @@ function mapField({ name, fieldData, required, definitionsIdToNameMap, addArgume
  * Gets the field type.
  *
  * @param {object} param0
- * @param {FieldData} param0.field - The field data object.
+ * @param {FieldData<DirectivePropertyData>} param0.field - The field data object.
  * @param {boolean} [param0.required] - Indicates if the field is required.
  * @returns {string} - The field type.
  */
@@ -116,8 +116,8 @@ function getFieldType({ field, required }) {
  * Gets the field from array items.
  *
  * @param {object} param0
- * @param {ArrayItems} [param0.items] - The array items.
- * @returns {ArrayItem | undefined} - The field.
+ * @param {ArrayItems<DirectivePropertyData>} [param0.items] - The array items.
+ * @returns {ArrayItem<DirectivePropertyData> | undefined} - The field.
  */
 function getFieldFromArrayItems({ items }) {
 	if (Array.isArray(items)) {
diff --git a/reverse_engineering/constants/types.js b/reverse_engineering/constants/types.js
new file mode 100644
index 0000000..3adae48
--- /dev/null
+++ b/reverse_engineering/constants/types.js
@@ -0,0 +1,14 @@
+// graphql types
+const BUILT_IN_SCALAR = {
+	String: 'String',
+	Int: 'Int',
+	Float: 'Float',
+	Boolean: 'Boolean',
+	ID: 'ID',
+};
+
+const BUILT_IN_SCALAR_LIST = Object.values(BUILT_IN_SCALAR);
+
+module.exports = {
+	BUILT_IN_SCALAR_LIST,
+};
diff --git a/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js b/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js
new file mode 100644
index 0000000..021fbae
--- /dev/null
+++ b/reverse_engineering/helpers/getDefinitionCategoryByNameMap.js
@@ -0,0 +1,38 @@
+/**
+ * @import {DefinitionNode} from "graphql"
+ * @import {DefinitionNameToTypeNameMap} from "../../shared/types/re"
+ */
+
+const { astNodeKind } = require('../constants/graphqlAST');
+
+const kindToDefinitionTypeName = {
+	[astNodeKind.SCALAR_TYPE_DEFINITION]: 'Scalars',
+	[astNodeKind.ENUM_TYPE_DEFINITION]: 'Enums',
+	[astNodeKind.OBJECT_TYPE_DEFINITION]: 'Objects',
+	[astNodeKind.INTERFACE_TYPE_DEFINITION]: 'Interfaces',
+	[astNodeKind.UNION_TYPE_DEFINITION]: 'Unions',
+	[astNodeKind.INPUT_OBJECT_TYPE_DEFINITION]: 'Input objects',
+	[astNodeKind.DIRECTIVE_DEFINITION]: 'Directives',
+};
+
+/**
+ * Find nodes by kind with proper typing
+ *
+ * @param {object} options
+ * @param {DefinitionNode[]} options.nodes - The nodes to search
+ * @returns {DefinitionNameToTypeNameMap} The found nodes with proper type
+ */
+function getDefinitionCategoryByNameMap({ nodes }) {
+	return nodes
+		.filter(node => kindToDefinitionTypeName[node.kind])
+		.reduce((acc, node) => {
+			if ('name' in node && node.name !== undefined) {
+				acc[node.name.value] = kindToDefinitionTypeName[node.kind];
+			}
+			return acc;
+		}, {});
+}
+
+module.exports = {
+	getDefinitionCategoryByNameMap,
+};
diff --git a/reverse_engineering/helpers/sortByName.js b/reverse_engineering/helpers/sortByName.js
index 0402b1e..4832d28 100644
--- a/reverse_engineering/helpers/sortByName.js
+++ b/reverse_engineering/helpers/sortByName.js
@@ -1,14 +1,14 @@
 /**
- * @import {FieldsOrder, REDefinition} from "../../shared/types/types"
+ * @import {FieldsOrder, PreProcessedFieldData, REDefinition} from "../../shared/types/types"
  */
 
 /**
  * Sorts an array of objects by the name according to the fields order option
  *
  * @param {object} params
- * @param {REDefinition[]} params.items - The items to sort
+ * @param {REDefinition[] | PreProcessedFieldData[]} params.items - The items to sort
  * @param {FieldsOrder} params.fieldsOrder - The fields order
- * @returns {REDefinition[]} The sorted items
+ * @returns {REDefinition[] | PreProcessedFieldData[]} The sorted items
  */
 function sortByName({ items, fieldsOrder }) {
 	if (!Array.isArray(items)) {
diff --git a/reverse_engineering/mappers/field.js b/reverse_engineering/mappers/field.js
new file mode 100644
index 0000000..337735f
--- /dev/null
+++ b/reverse_engineering/mappers/field.js
@@ -0,0 +1,108 @@
+/**
+ * @import {FieldDefinitionNode, TypeNode} from "graphql"
+ * @import {DefinitionNameToTypeNameMap, FieldTypeProperties, PreProcessedFieldData} from "./../../shared/types/types"
+ */
+
+const { mapDirectivesUsage } = require('./directiveUsage');
+const { astNodeKind } = require('../constants/graphqlAST');
+const { BUILT_IN_SCALAR_LIST } = require('../constants/types');
+
+/**
+ * Maps a field
+ *
+ * @param {object} params
+ * @param {FieldDefinitionNode} params.field - The field to map
+ * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
+ * @returns {PreProcessedFieldData} The mapped field
+ */
+function mapField({ field, definitionCategoryByNameMap }) {
+	const fieldTypeProperties = getTypeProperties({ type: field.type, definitionCategoryByNameMap });
+	const sharedProperties = {
+		name: field.name.value,
+		fieldDirectives: mapDirectivesUsage({ directives: [...(field.directives || [])] }),
+		...fieldTypeProperties,
+	};
+	const description = field.description?.value;
+
+	if ('$ref' in fieldTypeProperties) {
+		return {
+			...sharedProperties,
+			refDescription: description,
+			// TODO: add arguments
+		};
+	}
+	return {
+		...sharedProperties,
+		description,
+		// TODO: add arguments
+	};
+}
+
+/**
+ * Recursively maps the type properties unwrapping non-null and list types and resolving named types to references
+ *
+ * @param {object} params
+ * @param {TypeNode} params.type - The GraphQL type node to process
+ * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
+ * @returns {FieldTypeProperties} JSON schema representation of the type
+ */
+function getTypeProperties({ type, definitionCategoryByNameMap }) {
+	// unwrap required type
+	if (type.kind === astNodeKind.NON_NULL_TYPE) {
+		const innerType = getTypeProperties({ type: type.type, definitionCategoryByNameMap });
+		return {
+			...innerType,
+			required: true,
+		};
+	}
+
+	if (type.kind === astNodeKind.LIST_TYPE) {
+		const innerType = getTypeProperties({ type: type.type, definitionCategoryByNameMap });
+		return {
+			type: 'List',
+			items: [innerType],
+			required: false,
+		};
+	}
+
+	if (type.kind === astNodeKind.NAMED_TYPE) {
+		const typeName = type.name.value;
+		const isScalar = isBuiltInScalar({ typeName });
+
+		if (isScalar) {
+			return {
+				type: typeName,
+				required: false,
+			};
+		}
+
+		const definitionCategoryName = definitionCategoryByNameMap[typeName];
+		if (definitionCategoryName) {
+			return {
+				'$ref': `#model/definitions/${definitionCategoryName}/${typeName}`,
+				required: false,
+			};
+		}
+	}
+
+	// fallback
+	return {
+		type: 'string',
+		required: false,
+	};
+}
+
+/**
+ * Checks if a type name is a built-in scalar
+ *
+ * @param {object} params
+ * @param {string} params.typeName - The type name to check
+ * @returns {boolean} True if the type is a built-in scalar, false otherwise
+ */
+function isBuiltInScalar({ typeName }) {
+	return BUILT_IN_SCALAR_LIST.includes(typeName);
+}
+
+module.exports = {
+	mapField,
+};
diff --git a/reverse_engineering/mappers/schema.js b/reverse_engineering/mappers/schema.js
index 1d9989d..7d0956c 100644
--- a/reverse_engineering/mappers/schema.js
+++ b/reverse_engineering/mappers/schema.js
@@ -27,8 +27,13 @@ function getMappedSchema({ schemaItems, graphName, logger, fieldsOrder }) {
 			rootSchemaNode: findNodesByKind({ nodes: schemaItems, kind: Kind.SCHEMA_DEFINITION })[0],
 			graphName,
 		});
+		const rootTypeNames = [
+			container.schemaRootTypes?.rootQuery || 'Query',
+			container.schemaRootTypes?.rootMutation || 'Mutation',
+			container.schemaRootTypes?.rootSubscription || 'Subscription',
+		];
 
-		const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder });
+		const typeDefinitions = getTypeDefinitions({ typeDefinitions: schemaItems, fieldsOrder, rootTypeNames });
 
 		return [
 			// TODO: remove test collection
diff --git a/reverse_engineering/mappers/typeDefinitions/objectType.js b/reverse_engineering/mappers/typeDefinitions/objectType.js
new file mode 100644
index 0000000..6b07b5d
--- /dev/null
+++ b/reverse_engineering/mappers/typeDefinitions/objectType.js
@@ -0,0 +1,58 @@
+/**
+ * @import {ObjectTypeDefinitionNode} from "graphql"
+ * @import {DefinitionNameToTypeNameMap, FieldsOrder, REObjectTypeDefinition, REPropertiesSchema} from "../../../shared/types/types"
+ */
+
+const { sortByName } = require('../../helpers/sortByName');
+const { mapDirectivesUsage } = require('../directiveUsage');
+const { mapField } = require('../field');
+
+/**
+ * Maps object type definitions
+ *
+ * @param {object} params
+ * @param {ObjectTypeDefinitionNode[]} params.objectTypes - The object types
+ * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
+ * @param {FieldsOrder} params.fieldsOrder - The fields order
+ * @returns {REObjectTypeDefinition[]} The mapped object type definitions
+ */
+function getObjectTypeDefinitions({ objectTypes = [], definitionCategoryByNameMap, fieldsOrder }) {
+	return objectTypes.map(objectType => mapObjectType({ objectType, definitionCategoryByNameMap, fieldsOrder }));
+}
+
+/**
+ * Maps a single object type definition
+ *
+ * @param {object} params
+ * @param {ObjectTypeDefinitionNode} params.objectType - The object type to map
+ * @param {DefinitionNameToTypeNameMap} params.definitionCategoryByNameMap - The definition category by name map
+ * @param {FieldsOrder} params.fieldsOrder - The fields order
+ * @returns {REObjectTypeDefinition} The mapped object type definition
+ */
+function mapObjectType({ objectType, definitionCategoryByNameMap, fieldsOrder }) {
+	const properties = objectType.fields
+		? objectType.fields.map(field => mapField({ field, definitionCategoryByNameMap }))
+		: [];
+	const required = properties.filter(property => property.required).map(property => property.name);
+	const convertedProperties = sortByName({ items: properties, fieldsOrder }).reduce(
+		(acc, property) => {
+			acc[property.name] = property;
+			return acc;
+		},
+		/** @type {REPropertiesSchema} */ {},
+	);
+
+	return {
+		type: 'object',
+		name: objectType.name.value,
+		properties: convertedProperties,
+		required,
+		description: objectType.description?.value || '',
+		typeDirectives: mapDirectivesUsage({ directives: [...(objectType.directives || [])] }),
+		// TODO: add interfaces
+	};
+}
+
+module.exports = {
+	getObjectTypeDefinitions,
+};
diff --git a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js
index cb82d3f..ed33aa9 100644
--- a/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js
+++ b/reverse_engineering/mappers/typeDefinitions/typeDefinitions.js
@@ -10,14 +10,18 @@
  * 		DirectiveStructureType,
  * 		REEnumDefinition,
  * 		EnumStructureType,
- * 		ScalarStructureType} from "../../../shared/types/types"
+ * 		ObjectStructureType,
+ * 		ScalarStructureType,
+ * REObjectTypeDefinition} from "../../../shared/types/types"
  */
 
 const { astNodeKind } = require('../../constants/graphqlAST');
 const { findNodesByKind } = require('../../helpers/findNodesByKind');
+const { getDefinitionCategoryByNameMap } = require('../../helpers/getDefinitionCategoryByNameMap');
 const { sortByName } = require('../../helpers/sortByName');
 const { getCustomScalarTypeDefinitions } = require('./customScalar');
 const { getDirectiveTypeDefinitions } = require('./directive');
+const { getObjectTypeDefinitions } = require('./objectType');
 const { getEnumTypeDefinitions } = require('./enum');
 
 /**
@@ -26,20 +30,33 @@ const { getEnumTypeDefinitions } = require('./enum');
  * @param {object} params
  * @param {DefinitionNode[]} params.typeDefinitions - The type definitions nodes
  * @param {FieldsOrder} params.fieldsOrder - The fields order
+ * @param {string[]} params.rootTypeNames - The root type names
  * @returns {REModelDefinitionsSchema} The mapped type definitions
  */
-function getTypeDefinitions({ typeDefinitions, fieldsOrder }) {
+function getTypeDefinitions({ typeDefinitions, fieldsOrder, rootTypeNames }) {
+	const definitionCategoryByNameMap = getDefinitionCategoryByNameMap({ nodes: typeDefinitions });
+
 	const directives = getDirectiveTypeDefinitions({
 		directives: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.DIRECTIVE_DEFINITION }),
 	});
 	const customScalars = getCustomScalarTypeDefinitions({
 		customScalars: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.SCALAR_TYPE_DEFINITION }),
 	});
+
 	const enums = getEnumTypeDefinitions({
 		enums: findNodesByKind({ nodes: typeDefinitions, kind: astNodeKind.ENUM_TYPE_DEFINITION }),
 	});
+	const objectDefinitionNodes = findNodesByKind({
+		nodes: typeDefinitions,
+		kind: astNodeKind.OBJECT_TYPE_DEFINITION,
+	}).filter(node => !rootTypeNames.includes(node.name.value));
+	const objectTypes = getObjectTypeDefinitions({
+		objectTypes: objectDefinitionNodes,
+		definitionCategoryByNameMap,
+		fieldsOrder,
+	});
 
-	const definitions = getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, enums });
+	const definitions = getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, enums, objectTypes });
 
 	return definitions;
 }
@@ -52,9 +69,10 @@ function getTypeDefinitions({ typeDefinitions, fieldsOrder }) {
  * @param {REDirectiveDefinition[]} params.directives - The directive definitions
  * @param {RECustomScalarDefinition[]} params.customScalars - The custom scalar definitions
  * @param {REEnumDefinition[]} params.enums - The enum definitions
+ * @param {REObjectTypeDefinition[]} params.objectTypes - The object type definitions
  * @returns {REModelDefinitionsSchema} The type definitions structure
  */
-function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, enums }) {
+function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, enums, objectTypes }) {
 	const definitions = {
 		['Directives']: /** @type {DirectiveStructureType} */ (
 			getDefinitionCategoryStructure({
@@ -70,6 +88,13 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, e
 				properties: customScalars,
 			})
 		),
+		['Objects']: /** @type {ObjectStructureType} */ (
+			getDefinitionCategoryStructure({
+				fieldsOrder,
+				subtype: 'object',
+				properties: objectTypes,
+			})
+		),
 		['Enums']: /** @type {EnumStructureType} */ (
 			getDefinitionCategoryStructure({
 				fieldsOrder,
@@ -96,17 +121,18 @@ function getTypeDefinitionsStructure({ fieldsOrder, directives, customScalars, e
 function getDefinitionCategoryStructure({ fieldsOrder, subtype, properties }) {
 	const sortedFields = sortByName({ items: properties, fieldsOrder });
 
+	const convertedProperties = sortedFields.reduce((acc, prop) => {
+		const processedProp = { ...prop };
+
+		acc[processedProp.name] = processedProp;
+		return acc;
+	}, {});
+
 	return {
 		type: 'type',
 		subtype,
 		structureType: true,
-		properties: sortedFields.reduce(
-			(acc, prop) => {
-				acc[prop.name] = prop;
-				return acc;
-			},
-			/** @type {REDefinitionsSchema} */ {},
-		),
+		properties: convertedProperties,
 	};
 }
 
diff --git a/shared/types/fe.d.ts b/shared/types/fe.d.ts
index 21d290d..c2d354a 100644
--- a/shared/types/fe.d.ts
+++ b/shared/types/fe.d.ts
@@ -1,9 +1,12 @@
 import {
+	Argument,
 	ContainerSchemaRootTypes,
 	CustomScalarDefinition,
 	DirectiveDefinition,
 	DirectiveLocations,
 	DirectivePropertyData,
+	FieldSchema,
+	ObjectLikeDefinition,
 	EnumDefinition,
 	EnumValue,
 } from './shared';
@@ -44,61 +47,7 @@ export type FEEnumDefinitionsSchema = Record<string, FEEnumDefinition>;
 // Object type definition
 export type FEObjectLikeDefinitionsSchema = Record<string, FEObjectLikeDefinition>;
 
-export type FEObjectLikeDefinition = {
-	description?: string; // Description of the object type
-	isActivated?: boolean; // If the object type is activated
-	implementsInterfaces?: ImplementsInterface[]; // Interfaces that the object type implements
-	typeDirectives?: DirectivePropertyData[]; // Directives for the type
-	properties: Record<string, FieldData>; // Properties of the object type
-	required?: boolean;
-};
-
-// Field data type
-export type FieldData = RegularFieldData | ReferenceFieldData;
-
-export type FieldSchema = Record<string, FieldData>;
-
-export type ArrayItems = ArrayItem | ArrayItem[];
-
-type RegularFieldData = {
-	type: string; // Type of the field
-	isActivated?: boolean; // If the field is activated
-	description?: string; // Description of the field
-	fieldDirectives?: DirectivePropertyData[]; // Directives for the field
-	items?: ArrayItems; // Items of the List type
-	arguments?: Argument[]; // Arguments of the field
-	default?: string; // Default value of the field
-};
-
-type ReferenceFieldData = {
-	$ref: string; // Reference path to the type definition
-	isActivated?: boolean; // If the field is activated
-	refDescription?: string; // Description of the reference
-	fieldDirectives?: DirectivePropertyData[]; // Directives for the field
-	arguments?: Argument[]; // Arguments of the field
-	default?: string; // Default value of the reference
-};
-
-export type ArrayItem = FieldData & {
-	required?: boolean; // If the array item is required
-};
-
-// Field arguments
-type ArgumentListItem = {
-	type?: string;
-	required?: boolean;
-};
-
-export type Argument = {
-	id: string;
-	type: string;
-	name: string;
-	default?: string;
-	description?: string;
-	directives?: DirectivePropertyData[];
-	required?: boolean;
-	listItems?: ArgumentListItem[];
-};
+export type FEObjectLikeDefinition = ObjectLikeDefinition<ImplementsInterface, DirectivePropertyData>;
 
 export type ArgumentsResultStatement = {
 	argumentsStatement: string; // The formatted arguments string.
@@ -232,7 +181,7 @@ export type ValidateScriptCallback = (
 ) => void;
 
 export type BaseGetFieldParams = {
-	fields?: FieldSchema; // The fields to get
+	fields?: FieldSchema<DirectivePropertyData>; // The fields to get
 	requiredFields?: string[]; // The required fields list
 	definitionsIdToNameMap: IdToNameMap; // The definitions id to name map
 };
diff --git a/shared/types/re.d.ts b/shared/types/re.d.ts
index 9295b8c..cf67764 100644
--- a/shared/types/re.d.ts
+++ b/shared/types/re.d.ts
@@ -2,6 +2,8 @@ import {
 	ContainerSchemaRootTypes,
 	CustomScalarDefinition,
 	DirectiveDefinition,
+	FieldData,
+	ObjectLikeDefinition,
 	EnumDefinition,
 	EnumValue,
 	StructuredDirective,
@@ -120,18 +122,25 @@ export type REDefinitionsSchema = REDirectiveDefinitionsSchema | RECustomScalarD
 
 export type REDirectiveDefinitionsSchema = Record<string, REDirectiveDefinition>;
 export type RECustomScalarDefinitionsSchema = Record<string, RECustomScalarDefinition>;
+export type REObjectDefinitionsSchema = Record<string, REObjectTypeDefinition>;
 export type REEnumDefinitionsSchema = Record<string, REEnumDefinition>;
 
-export type REDefinition = RECustomScalarDefinition | REDirectiveDefinition | REEnumDefinition;
+export type REDefinition = RECustomScalarDefinition | REDirectiveDefinition | REEnumDefinition | REObjectTypeDefinition;
 
 export type REModelDefinitionsSchema = {
 	definitions: {
 		Directives: DirectiveStructureType;
 		Scalars: ScalarStructureType;
+		Objects: ObjectStructureType;
+		Enums: EnumStructureType;
 	};
 };
 
-export type DefinitionREStructure = DirectiveStructureType | ScalarStructureType | EnumStructureType;
+export type DefinitionREStructure =
+	| DirectiveStructureType
+	| ScalarStructureType
+	| EnumStructureType
+	| ObjectStructureType;
 
 type StructureType<T> = {
 	type: 'type';
@@ -163,5 +172,54 @@ export type REEnumDefinition = EnumDefinition<StructuredDirective> & {
 
 export type REEnumValue = EnumValue<StructuredDirective>;
 
+export type ObjectStructureType = StructureType<REObjectDefinitionsSchema> & {
+	subtype: 'object';
+};
+
+export type REImplementsInterface = {
+	interface: string; // Name of the interface
+};
+
+export type REPropertiesSchema = Record<string, FieldData<StructuredDirective>>;
+type REObjectLikeDefinition = ObjectLikeDefinition<REImplementsInterface, StructuredDirective>;
+
+export type REObjectTypeDefinition = REObjectLikeDefinition & {
+	type: 'object';
+	name: string;
+};
+
+export type PreProcessedFieldData = FieldData<StructuredDirective> &
+	FieldTypeProperties & {
+		name: string;
+	};
+
+export type FieldTypeProperties = RegularFieldTypeProperties | ArrayFieldTypeProperties | ReferenceFieldTypeProperties;
+
+export type RegularFieldTypeProperties = {
+	type: string;
+	required: boolean;
+};
+
+export type ArrayFieldTypeProperties = {
+	type: 'List';
+	items?: [FieldTypeProperties];
+	required: boolean;
+};
+
+export type ReferenceFieldTypeProperties = {
+	$ref: string;
+	required: boolean;
+};
+
+export type DefinitionTypeName =
+	| 'Scalars'
+	| 'Enums'
+	| 'Objects'
+	| 'Interfaces'
+	| 'Unions'
+	| 'Input objects'
+	| 'Directives';
+export type DefinitionNameToTypeNameMap = Record<string, DefinitionTypeName>;
+
 export type TestConnectionCallback = (err?: Error | unknown) => void;
 export type DisconnectCallback = TestConnectionCallback;
diff --git a/shared/types/shared.d.ts b/shared/types/shared.d.ts
index 836b259..a3a3beb 100644
--- a/shared/types/shared.d.ts
+++ b/shared/types/shared.d.ts
@@ -47,7 +47,7 @@ type RawDirective = {
 	rawDirective: string; // Raw directive string
 };
 
-type StructuredDirective = {
+export type StructuredDirective = {
 	directiveFormat: 'Structured'; // Format of the directive
 	directiveName: string; // Name of a built-in directive or GUID of a custom directive
 	argumentValueFormat: 'Raw'; // Format of the argument values
@@ -68,6 +68,63 @@ export type DirectiveDefinition<T, D = DirectiveLocations> = {
 	directiveLocations: D;
 };
 
+export type ObjectLikeDefinition<Interface, DirectiveUsage> = {
+	description?: string; // Description of the object type
+	isActivated?: boolean; // If the object type is activated
+	implementsInterfaces?: Interface[]; // Interfaces that the object type implements
+	typeDirectives?: DirectiveUsage[]; // Directives for the type
+	properties: Record<string, FieldData<DirectiveUsage>>; // Properties of the object type
+	required?: string[];
+};
+
+// Field data type
+export type FieldData<DirectiveUsage> = RegularFieldData<DirectiveUsage> | ReferenceFieldData<DirectiveUsage>;
+
+export type FieldSchema<DirectiveUsage> = Record<string, FieldData<DirectiveUsage>>;
+
+export type ArrayItems<DirectiveUsage> = ArrayItem<DirectiveUsage> | ArrayItem<DirectiveUsage>[];
+
+type RegularFieldData<DirectiveUsage> = {
+	type: string; // Type of the field
+	isActivated?: boolean; // If the field is activated
+	description?: string; // Description of the field
+	fieldDirectives?: DirectiveUsage[]; // Directives for the field
+	items?: ArrayItems<DirectiveUsage>; // Items of the List type
+	arguments?: Argument[]; // Arguments of the field
+	default?: string; // Default value of the field
+};
+
+type ReferenceFieldData<DirectiveUsage> = {
+	$ref: string; // Reference path to the type definition
+	isActivated?: boolean; // If the field is activated
+	refDescription?: string; // Description of the reference
+	fieldDirectives?: DirectiveUsage[]; // Directives for the field
+	arguments?: Argument[]; // Arguments of the field
+	default?: string; // Default value of the reference
+};
+
+export type ArrayItem<DirectiveUsage> = FieldData<DirectiveUsage> & {
+	required?: boolean; // If the array item is required
+	$ref?: string; // Reference path to the type definition
+};
+
+// Field arguments
+type ArgumentListItem = {
+	type?: string;
+	required?: boolean;
+};
+
+export type Argument = {
+	id: string;
+	type: string;
+	name: string;
+	default?: string;
+	description?: string;
+	directives?: DirectivePropertyData[];
+	required?: boolean;
+	listItems?: ArgumentListItem[];
+};
+
 // Enum
 export type EnumValue<T> = {
 	value: string; // The name of the enum value
diff --git a/test/reverse_engineering/mappers/field.spec.js b/test/reverse_engineering/mappers/field.spec.js
new file mode 100644
index 0000000..4d60cef
--- /dev/null
+++ b/test/reverse_engineering/mappers/field.spec.js
@@ -0,0 +1,271 @@
+const { describe, it, mock, afterEach } = require('node:test');
+const assert = require('assert');
+
+// Mock dependencies
+const mapDirectivesUsageMock = mock.fn(() => []);
+
+mock.module('../../../reverse_engineering/mappers/directiveUsage', {
+	namedExports: {
+		mapDirectivesUsage: mapDirectivesUsageMock,
+	},
+});
+
+const astNodeKindMock = {
+	NAMED_TYPE: 'NamedType',
+	NON_NULL_TYPE: 'NonNullType',
+	LIST_TYPE: 'ListType',
+};
+
+const { mapField } = require('../../../reverse_engineering/mappers/field');
+
+describe('field', () => {
+	afterEach(() => {
+		mapDirectivesUsageMock.mock.resetCalls();
+	});
+
+	describe('mapField', () => {
+		it('should map basic field with scalar type', () => {
+			const field = {
+				name: { value: 'name' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'String' },
+				},
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'name',
+				type: 'String',
+				required: false,
+				fieldDirectives: [],
+				description: undefined,
+			});
+			assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+		});
+
+		it('should map field with description', () => {
+			const field = {
+				name: { value: 'name' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'String' },
+				},
+				description: { value: 'User name' },
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'name',
+				type: 'String',
+				required: false,
+				fieldDirectives: [],
+				description: 'User name',
+			});
+		});
+
+		it('should map field with directives', () => {
+			const mockDirectives = [{ name: { value: 'deprecated' }, arguments: [] }];
+			const mockMappedDirectives = [{ directiveName: '@deprecated', rawArgumentValues: '' }];
+			mapDirectivesUsageMock.mock.mockImplementationOnce(() => mockMappedDirectives);
+
+			const field = {
+				name: { value: 'oldField' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'String' },
+				},
+				directives: mockDirectives,
+			};
+			const definitionCategoryByNameMap = {};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'oldField',
+				type: 'String',
+				required: false,
+				fieldDirectives: mockMappedDirectives,
+				description: undefined,
+			});
+			assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+			assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], {
+				directives: mockDirectives,
+			});
+		});
+
+		it('should map field with reference type', () => {
+			const field = {
+				name: { value: 'user' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'User' },
+				},
+				description: { value: 'The user' },
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {
+				'User': 'Objects',
+			};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'user',
+				$ref: '#model/definitions/Objects/User',
+				required: false,
+				fieldDirectives: [],
+				refDescription: 'The user',
+			});
+		});
+
+		it('should map field with required type', () => {
+			const field = {
+				name: { value: 'id' },
+				type: {
+					kind: astNodeKindMock.NON_NULL_TYPE,
+					type: {
+						kind: astNodeKindMock.NAMED_TYPE,
+						name: { value: 'ID' },
+					},
+				},
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'id',
+				type: 'ID',
+				required: true,
+				fieldDirectives: [],
+				description: undefined,
+			});
+		});
+
+		it('should map field with list type', () => {
+			const field = {
+				name: { value: 'friends' },
+				type: {
+					kind: astNodeKindMock.LIST_TYPE,
+					type: {
+						kind: astNodeKindMock.NAMED_TYPE,
+						name: { value: 'User' },
+					},
+				},
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {
+				'User': 'Objects',
+			};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'friends',
+				type: 'List',
+				items: [
+					{
+						$ref: '#model/definitions/Objects/User',
+						required: false,
+					},
+				],
+				required: false,
+				fieldDirectives: [],
+				description: undefined,
+			});
+		});
+
+		it('should map field with required list of required items', () => {
+			const field = {
+				name: { value: 'requiredFriends' },
+				type: {
+					kind: astNodeKindMock.NON_NULL_TYPE,
+					type: {
+						kind: astNodeKindMock.LIST_TYPE,
+						type: {
+							kind: astNodeKindMock.NON_NULL_TYPE,
+							type: {
+								kind: astNodeKindMock.NAMED_TYPE,
+								name: { value: 'User' },
+							},
+						},
+					},
+				},
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {
+				'User': 'Objects',
+			};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'requiredFriends',
+				type: 'List',
+				items: [
+					{
+						$ref: '#model/definitions/Objects/User',
+						required: true,
+					},
+				],
+				required: true,
+				fieldDirectives: [],
+				description: undefined,
+			});
+		});
+
+		it('should handle undefined directives', () => {
+			const field = {
+				name: { value: 'name' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'String' },
+				},
+				// directives property is omitted
+			};
+			const definitionCategoryByNameMap = {};
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'name',
+				type: 'String',
+				required: false,
+				fieldDirectives: [],
+				description: undefined,
+			});
+			assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+			assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []);
+		});
+
+		it('should fallback to string type for unknown named types', () => {
+			const field = {
+				name: { value: 'customType' },
+				type: {
+					kind: astNodeKindMock.NAMED_TYPE,
+					name: { value: 'UnknownType' },
+				},
+				directives: [],
+			};
+			const definitionCategoryByNameMap = {}; // UnknownType not in map
+
+			const result = mapField({ field, definitionCategoryByNameMap });
+
+			assert.deepStrictEqual(result, {
+				name: 'customType',
+				type: 'string', // Fallback to string
+				required: false,
+				fieldDirectives: [],
+				description: undefined,
+			});
+		});
+	});
+});
diff --git a/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js
new file mode 100644
index 0000000..495bdbe
--- /dev/null
+++ b/test/reverse_engineering/mappers/typeDefinitions/objectType.spec.js
@@ -0,0 +1,328 @@
+const { describe, it, mock, afterEach } = require('node:test');
+const assert = require('assert');
+
+// Mock dependencies
+const sortByNameMock = mock.fn(({ items }) => items);
+mock.module('../../../../reverse_engineering/helpers/sortByName', {
+	namedExports: {
+		sortByName: sortByNameMock,
+	},
+});
+
+const mapDirectivesUsageMock = mock.fn(() => []);
+mock.module('../../../../reverse_engineering/mappers/directiveUsage', {
+	namedExports: {
+		mapDirectivesUsage: mapDirectivesUsageMock,
+	},
+});
+
+const mapFieldMock = mock.fn(({ field }) => ({
+	name: field.name.value,
+	required: field.type.kind === 'NON_NULL_TYPE',
+	// Add other properties that mapField would return
+}));
+mock.module('../../../../reverse_engineering/mappers/field', {
+	namedExports: {
+		mapField: mapFieldMock,
+	},
+});
+
+const { getObjectTypeDefinitions } = require('../../../../reverse_engineering/mappers/typeDefinitions/objectType');
+
+describe('getObjectTypeDefinitions', () => {
+	afterEach(() => {
+		sortByNameMock.mock.resetCalls();
+		mapDirectivesUsageMock.mock.resetCalls();
+		mapFieldMock.mock.resetCalls();
+	});
+
+	it('should return an empty array when no object types are provided', () => {
+		const result = getObjectTypeDefinitions({
+			objectTypes: [],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+		assert.deepStrictEqual(result, []);
+	});
+
+	it('should correctly map a simple object type with no fields', () => {
+		const mockObjectType = {
+			name: { value: 'EmptyType' },
+			fields: [],
+			directives: [],
+		};
+
+		const expected = [
+			{
+				type: 'object',
+				name: 'EmptyType',
+				properties: {},
+				required: [],
+				description: '',
+				typeDirectives: [],
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: [mockObjectType],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.deepStrictEqual(result, expected);
+		assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+		assert.strictEqual(sortByNameMock.mock.calls.length, 1);
+		assert.strictEqual(mapFieldMock.mock.calls.length, 0);
+	});
+
+	it('should correctly map an object type with fields', () => {
+		const mockObjectType = {
+			name: { value: 'User' },
+			description: { value: 'A user object' },
+			fields: [
+				{
+					name: { value: 'id' },
+					type: { kind: 'NON_NULL_TYPE' },
+					directives: [],
+				},
+				{
+					name: { value: 'name' },
+					type: { kind: 'NAMED_TYPE' },
+					directives: [],
+				},
+			],
+			directives: [],
+		};
+
+		// Setup the mapField mock to return expected values
+		mapFieldMock.mock.mockImplementation(({ field }) => ({
+			name: field.name.value,
+			required: field.type.kind === 'NON_NULL_TYPE',
+		}));
+
+		// Expected result with the properties based on the mocked mapField responses
+		const expected = [
+			{
+				type: 'object',
+				name: 'User',
+				properties: {
+					id: { name: 'id', required: true },
+					name: { name: 'name', required: false },
+				},
+				required: ['id'],
+				description: 'A user object',
+				typeDirectives: [],
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: [mockObjectType],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.deepStrictEqual(result, expected);
+		assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+		assert.strictEqual(sortByNameMock.mock.calls.length, 1);
+		assert.strictEqual(mapFieldMock.mock.calls.length, 2);
+	});
+
+	it('should correctly map an object type with directives', () => {
+		const mockDirectiveResult = [{ directiveName: '@auth', rawArgumentValues: 'requires: "ADMIN"' }];
+		mapDirectivesUsageMock.mock.mockImplementationOnce(() => mockDirectiveResult);
+
+		const mockObjectType = {
+			name: { value: 'AdminType' },
+			fields: [],
+			directives: [
+				{
+					name: { value: 'auth' },
+					arguments: [{ name: { value: 'requires' }, value: { value: 'ADMIN' } }],
+				},
+			],
+		};
+
+		const expected = [
+			{
+				type: 'object',
+				name: 'AdminType',
+				properties: {},
+				required: [],
+				description: '',
+				typeDirectives: mockDirectiveResult,
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: [mockObjectType],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.deepStrictEqual(result, expected);
+		assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+		assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0], {
+			directives: mockObjectType.directives,
+		});
+	});
+
+	it('should correctly handle fields order', () => {
+		const mockObjectType = {
+			name: { value: 'OrderedType' },
+			fields: [
+				{ name: { value: 'fieldB' }, type: { kind: 'NAMED_TYPE' }, directives: [] },
+				{ name: { value: 'fieldA' }, type: { kind: 'NAMED_TYPE' }, directives: [] },
+				{ name: { value: 'fieldC' }, type: { kind: 'NAMED_TYPE' }, directives: [] },
+			],
+			directives: [],
+		};
+
+		mapFieldMock.mock.mockImplementation(({ field }) => ({
+			name: field.name.value,
+			required: false,
+		}));
+
+		// Test both fieldsOrder options
+		const testCases = [
+			{
+				fieldsOrder: 'alphabetical',
+				expectedOrder: ['fieldA', 'fieldB', 'fieldC'], // Alphabetical order
+			},
+			{
+				fieldsOrder: 'field',
+				expectedOrder: ['fieldB', 'fieldA', 'fieldC'], // Original order
+			},
+		];
+
+		for (const testCase of testCases) {
+			// Reset mocks before each test case
+			sortByNameMock.mock.resetCalls();
+			mapFieldMock.mock.resetCalls();
+
+			// Mock sortByName to simulate behavior based on fieldsOrder value
+			if (testCase.fieldsOrder === 'alphabetical') {
+				// For 'alphabetical', sort alphabetically
+				sortByNameMock.mock.mockImplementationOnce(({ items }) => {
+					return [...items].sort((a, b) => a.name.localeCompare(b.name));
+				});
+			} else {
+				// For 'field', maintain original order
+				sortByNameMock.mock.mockImplementationOnce(({ items }) => items);
+			}
+
+			const result = getObjectTypeDefinitions({
+				objectTypes: [mockObjectType],
+				definitionCategoryByNameMap: {},
+				fieldsOrder: testCase.fieldsOrder,
+			});
+
+			// Check that sortByName was called correctly
+			assert.strictEqual(sortByNameMock.mock.calls.length, 1);
+			assert.strictEqual(sortByNameMock.mock.calls[0].arguments[0].fieldsOrder, testCase.fieldsOrder);
+
+			// Verify the result structure
+			assert.strictEqual(result.length, 1);
+			assert.strictEqual(result[0].name, 'OrderedType');
+
+			const propertyNames = Object.keys(result[0].properties);
+
+			// Verify the properties are in the expected order for this test case
+			assert.deepStrictEqual(
+				propertyNames,
+				testCase.expectedOrder,
+				`Field order should be ${testCase.expectedOrder.join(', ')} when fieldsOrder is "${testCase.fieldsOrder}"`,
+			);
+		}
+	});
+
+	it('should correctly map multiple object types', () => {
+		const mockObjectTypes = [
+			{
+				name: { value: 'Type1' },
+				fields: [],
+				directives: [],
+			},
+			{
+				name: { value: 'Type2' },
+				fields: [],
+				directives: [],
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: mockObjectTypes,
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.strictEqual(result.length, 2);
+		assert.strictEqual(result[0].name, 'Type1');
+		assert.strictEqual(result[1].name, 'Type2');
+		assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 2);
+	});
+
+	it('should handle undefined fields', () => {
+		const mockObjectType = {
+			name: { value: 'TypeWithoutFields' },
+			// fields is undefined
+			directives: [],
+		};
+
+		const expected = [
+			{
+				type: 'object',
+				name: 'TypeWithoutFields',
+				properties: {},
+				required: [],
+				description: '',
+				typeDirectives: [],
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: [mockObjectType],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.deepStrictEqual(result, expected);
+
+		// Verify no fields were processed
+		assert.strictEqual(mapFieldMock.mock.calls.length, 0);
+
+		// Verify sortByName was still called (but with empty array)
+		assert.strictEqual(sortByNameMock.mock.calls.length, 1);
+		assert.deepStrictEqual(sortByNameMock.mock.calls[0].arguments[0].items, []);
+	});
+
+	it('should handle undefined directives', () => {
+		const mockObjectType = {
+			name: { value: 'TypeWithoutDirectives' },
+			fields: [],
+			// directives is undefined
+		};
+
+		const expected = [
+			{
+				type: 'object',
+				name: 'TypeWithoutDirectives',
+				properties: {},
+				required: [],
+				description: '',
+				typeDirectives: [], // We expect an empty array since our mock returns []
+			},
+		];
+
+		const result = getObjectTypeDefinitions({
+			objectTypes: [mockObjectType],
+			definitionCategoryByNameMap: {},
+			fieldsOrder: {},
+		});
+
+		assert.deepStrictEqual(result, expected);
+
+		// Verify the mapDirectivesUsage was called with empty directives array
+		assert.strictEqual(mapDirectivesUsageMock.mock.calls.length, 1);
+		assert.deepStrictEqual(mapDirectivesUsageMock.mock.calls[0].arguments[0].directives, []);
+	});
+});


Reviews:
user: login: VitaliiBedletskyi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T12:42:40Z
user: login: VitaliiBedletskyi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T12:44:10Z
user: login: taras-dubyk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:19:22Z
user: login: VitaliiBedletskyi
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:20:48Z

Review comments:
diff_hunk: @@ -68,6 +68,63 @@ export type DirectiveDefinition<T, D = DirectiveLocations> = {
 	directiveLocations: D;
 };
 
+export type ObjectLikeDefinition<Interfaces> = {
+	description?: string; // Description of the object type
+	isActivated?: boolean; // If the object type is activated
+	implementsInterfaces?: Interfaces; // Interfaces that the object type implements
+	typeDirectives?: DirectivePropertyData[]; // Directives for the type
path: shared/types/shared.d.ts
user: login: VitaliiBedletskyi
body: As far as I know, we don't parse SDL directives to raw directive types. I think it is better to use `StructuredDirective[]` type
created_at: 2025-03-24T12:42:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,6 +68,63 @@ export type DirectiveDefinition<T, D = DirectiveLocations> = {
 	directiveLocations: D;
 };
 
+export type ObjectLikeDefinition<Interfaces> = {
+	description?: string; // Description of the object type
+	isActivated?: boolean; // If the object type is activated
+	implementsInterfaces?: Interfaces; // Interfaces that the object type implements
+	typeDirectives?: DirectivePropertyData[]; // Directives for the type
path: shared/types/shared.d.ts
user: login: VitaliiBedletskyi
body: UPD: as we reuse it in FE logic, maybe better to move it to generic as `Interfaces`
created_at: 2025-03-24T12:44:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,6 +68,63 @@ export type DirectiveDefinition<T, D = DirectiveLocations> = {
 	directiveLocations: D;
 };
 
+export type ObjectLikeDefinition<Interfaces> = {
+	description?: string; // Description of the object type
+	isActivated?: boolean; // If the object type is activated
+	implementsInterfaces?: Interfaces; // Interfaces that the object type implements
+	typeDirectives?: DirectivePropertyData[]; // Directives for the type
path: shared/types/shared.d.ts
user: login: taras-dubyk
body: I moved it to generic parameters.
created_at: 2025-03-24T14:19:22Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-03-24T14:18:37Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=hackolade_GraphQL&pullRequest=94) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=hackolade_GraphQL&pullRequest=94&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [3 Accepted issues](https://sonarcloud.io/project/issues?id=hackolade_GraphQL&pullRequest=94&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=hackolade_GraphQL&pullRequest=94&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=hackolade_GraphQL&pullRequest=94&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=hackolade_GraphQL&pullRequest=94&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=hackolade_GraphQL&pullRequest=94)


author_association: NONE