{"version":3,"sources":["parser/parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAA8I,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAY,MAAM,YAAY,CAAC;AAMpO,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQvD,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO;;;;;;;;EAy4ClE;AA8GD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,UAE5C","file":"parser.d.ts","sourcesContent":["//** PARSE TYPESCRIPT DATA\n\nimport { E, errorFile, TError } from \"@src/utils/error\";\nimport { warn } from \"@src/utils/log\";\nimport ts from \"typescript\";\nimport { AssertOptions, BasicScalar, Enum, EnumMember, InputField, Kind, List, MethodDescriptor, Node, OutputField, Param, Reference, Scalar, Union, InputNode, InputObject, OutputObject, OutputNode, AllNodes } from './model';\nimport { NodeVisitor } from \"./visitor\";\nimport Yaml from 'yaml';\nconst parseYaml = Yaml.parse;\n//@ts-ignore\nimport { DEFAULT_SCALARS, RootConfig as RootConfigTTModel } from \"tt-model\";\nimport { FunctionExpr, RootConfig } from \"..\";\nimport { PACKAGE_NAME } from \"@src/config\";\n\nconst IS_OF_TYPE_NULL = ts.TypeFlags.Undefined | ts.TypeFlags.Null;\n\n/** Function name validator */\nconst FX_REGEX = /^\\w+$/;\n\n/**\n * Extract Model from typescript code\n */\nexport function parse(files: readonly string[], program: ts.Program) {\n\t//* Init\n\tconst INPUT_ENTITIES: Map<string, InputNode> = new Map();\n\tconst OUTPUT_ENTITIES: Map<string, OutputNode> = new Map();\n\t/** Contains validator functions */\n\tconst INPUT_VALIDATORS: Map<string, FunctionExpr> = new Map();\n\t/** Contains resolver functions */\n\tconst OUTPUT_RESOLVERS: Map<string, FunctionExpr> = new Map();\n\t//* Literal objects had no with missing name like Literal objects\n\tconst LITERAL_OBJECTS: { node: InputObject | OutputObject, isInput: boolean | undefined, ref: Reference }[] = [];\n\t/** Root wrappers: wrap root controller */\n\tconst rootConfig: RootConfig = {\n\t\tbefore: [],\n\t\tafter: [],\n\t\twrappers: []\n\t};\n\t/** Helper entities */\n\tconst inputHelperEntities: Map<string, InputObject[]> = new Map();\n\tconst outputHelperEntities: Map<string, OutputObject[]> = new Map();\n\t/** Print Node Names */\n\tconst tsNodePrinter = ts.createPrinter({\n\t\tomitTrailingSemicolon: false,\n\t\tremoveComments: true\n\t});\n\t/** Node Factory */\n\tconst factory = ts.factory;\n\t/** Type Checker */\n\tconst typeChecker = program.getTypeChecker();\n\t/** Parsing Errors */\n\tconst errors: string[] = [];\n\t/** Node Visitor */\n\tconst visitor = new NodeVisitor();\n\t//* Parse file and put root children into visitor's queue\n\tfor (let i = 0, len = files.length; i < len; ++i) {\n\t\tlet srcFile = program.getSourceFile(files[i])!;\n\t\tvisitor.pushChildren(typeChecker, srcFile, undefined, srcFile, undefined, undefined);\n\t}\n\t//* Iterate over all nodes\n\tconst it = visitor.it();\n\trootLoop: while (true) {\n\t\ttry {\n\t\t\t//* Get next item\n\t\t\tlet item = it.next();\n\t\t\tif (item.done) break;\n\t\t\tlet { node, nodeType, parentDescriptor: pDesc, srcFile, isInput, entityName, isResolversImplementation, propertyType, symbol: nodeSymbol } = item.value;\n\t\t\tlet fileName = srcFile.fileName;\n\t\t\tif (nodeSymbol == null) nodeSymbol = nodeType.symbol;\n\t\t\t//* Extract jsDoc && Metadata\n\t\t\tlet asserts: string[] | undefined;\n\t\t\tlet deprecated: string | undefined;\n\t\t\tlet defaultValue: string | undefined;\n\t\t\tlet fieldAlias: string | undefined;\n\t\t\tlet jsDoc: string[] = nodeSymbol?.getDocumentationComment(typeChecker).map(e => e.text) ?? [];\n\t\t\t/** Do order fields by name */\n\t\t\tlet orderByName: boolean | undefined;\n\t\t\t// Parse JsDocTags\n\t\t\tlet jsDocTags = nodeSymbol?.getJsDocTags();\n\t\t\tlet resolverFx: string[] = [];\n\t\t\tlet validatorFx: string[] = [];\n\t\t\tif (jsDocTags != null && jsDocTags.length) {\n\t\t\t\tfor (let i = 0, len = jsDocTags.length; i < len; ++i) {\n\t\t\t\t\tlet tag = jsDocTags[i];\n\t\t\t\t\tlet tagText = tag.text?.map(c => c.text).join(\"\\n\").trim();\n\t\t\t\t\tjsDoc.push(tag.text == null ? `@${tag.name}` : `@${tag.name} ${tagText}`);\n\t\t\t\t\tswitch (tag.name) {\n\t\t\t\t\t\tcase 'ignore':\n\t\t\t\t\t\tcase 'virtual':\n\t\t\t\t\t\t\t// Ignore this Node\n\t\t\t\t\t\t\tcontinue rootLoop;\n\t\t\t\t\t\tcase 'deprecated':\n\t\t\t\t\t\t\tdeprecated = tagText ?? '';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'assert':\n\t\t\t\t\t\t\tif (tagText) {\n\t\t\t\t\t\t\t\t// FIXME check using multiple lines for jsdoc tag\n\t\t\t\t\t\t\t\tif (!tagText.startsWith('{'))\n\t\t\t\t\t\t\t\t\ttagText = `{${tagText}}`;\n\t\t\t\t\t\t\t\t(asserts ??= []).push(tagText);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'default':\n\t\t\t\t\t\t\tdefaultValue = tagText;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'input':\n\t\t\t\t\t\t\tif (isInput === false) continue rootLoop;\n\t\t\t\t\t\t\tisInput = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'output':\n\t\t\t\t\t\t\tif (isInput === true) continue rootLoop;\n\t\t\t\t\t\t\tisInput = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'alias':\n\t\t\t\t\t\t\tif (tagText) {\n\t\t\t\t\t\t\t\tlet t = tagText.match(/^\\w+/);\n\t\t\t\t\t\t\t\tif (t != null) fieldAlias = t[0];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'resolvers':\n\t\t\t\t\t\t\t/** Interpret methods as resolvers */\n\t\t\t\t\t\t\tisResolversImplementation = true;\n\t\t\t\t\t\t\tisInput = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ordered':\n\t\t\t\t\t\t\torderByName = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t/** Link resolver to an interface field */\n\t\t\t\t\t\tcase 'resolver':\n\t\t\t\t\t\t\tif (tagText == null || !FX_REGEX.test(tagText))\n\t\t\t\t\t\t\t\tthrow `Illegal resolver's name \"${tagText}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t\tresolverFx.push(tagText);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'inputValidator':\n\t\t\t\t\t\t\tif (tagText == null || !FX_REGEX.test(tagText))\n\t\t\t\t\t\t\t\tthrow `Illegal validator's name \"${tagText}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t\tvalidatorFx.push(tagText);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t// default:\n\t\t\t\t\t\t// \tconsole.log('>>>ANNOTATION>>', tag.name, '=>', tagText);\n\t\t\t\t\t\t// \tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// ADD decorators as part of description\n\t\t\tnode.decorators?.forEach(function (deco) {\n\t\t\t\tjsDoc.push(deco.getText());\n\t\t\t});\n\t\t\t//* Parse Node Specific Info\n\t\t\tswitch (node.kind) {\n\t\t\t\tcase ts.SyntaxKind.InterfaceDeclaration:\n\t\t\t\tcase ts.SyntaxKind.ClassDeclaration: {\n\t\t\t\t\tif (\n\t\t\t\t\t\tnode.modifiers == null ||\n\t\t\t\t\t\tnode.modifiers.every(modifier => modifier.kind !== ts.SyntaxKind.ExportKeyword)\n\t\t\t\t\t) {\n\t\t\t\t\t\twarn(`Ignored ${ts.SyntaxKind[node.kind]} due to missing \"export\" keyword at ${errorFile(srcFile, node)}`);\n\t\t\t\t\t\tcontinue rootLoop;\n\t\t\t\t\t}\n\t\t\t\t\tlet nodeEntity = node as ts.ClassDeclaration | ts.InterfaceDeclaration;\n\t\t\t\t\t//* Check if it is a helper entity (class that implements ValidatorsOf or ResolversOf)\n\t\t\t\t\tlet implementedEntities: string[] | undefined = undefined;\n\t\t\t\t\tlet inheritedEntities: string[] | undefined = undefined;\n\t\t\t\t\tif (nodeEntity.heritageClauses != null) {\n\t\t\t\t\t\tlet isInterface = ts.isInterfaceDeclaration(node);\n\t\t\t\t\t\tfor (let i = 0, clauses = nodeEntity.heritageClauses, len = clauses.length; i < len; ++i) {\n\t\t\t\t\t\t\tfor (let j = 0, types = clauses[i].types, jLen = types.length; j < jLen; ++j) {\n\t\t\t\t\t\t\t\tlet type = types[j];\n\t\t\t\t\t\t\t\tlet typeSymbol = typeChecker.getSymbolAtLocation(type.expression);\n\t\t\t\t\t\t\t\tif (typeSymbol == null || typeSymbol.name == null)\n\t\t\t\t\t\t\t\t\tthrow `Could not resolve type \"${type.expression.getText()}\" at ${errorFile(srcFile, type)}`;\n\t\t\t\t\t\t\t\tswitch (typeSymbol.name) {\n\t\t\t\t\t\t\t\t\tcase 'ValidatorsOf':\n\t\t\t\t\t\t\t\t\tcase 'ResolversOf': {\n\t\t\t\t\t\t\t\t\t\tlet resolverConfig = typeSymbol.name;\n\t\t\t\t\t\t\t\t\t\tif (isInterface)\n\t\t\t\t\t\t\t\t\t\t\tthrow `An interface could not extends \"${resolverConfig}\". at ${errorFile(srcFile, type)}`;\n\t\t\t\t\t\t\t\t\t\tlet isResolversOf = resolverConfig === 'ResolversOf';\n\t\t\t\t\t\t\t\t\t\tif (isInput === isResolversOf)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Could not implement \"${resolverConfig}\" for ${isResolversOf ? 'output' : 'input'} only entities. at ${errorFile(srcFile, type)}`;\n\t\t\t\t\t\t\t\t\t\tconst typeName: ts.Node = (type.typeArguments![0] as ts.TypeReferenceNode).typeName;\n\t\t\t\t\t\t\t\t\t\tconst targetSym = typeChecker.getTypeAtLocation(typeName).symbol;\n\t\t\t\t\t\t\t\t\t\tconst targetType = ((targetSym?.valueDeclaration ?? targetSym.declarations?.[0]) as ts.InterfaceDeclaration)?.name;\n\t\t\t\t\t\t\t\t\t\t// if (!ts.isTypeReferenceNode(t) || !typeChecker.getTypeFromTypeNode(t).isClassOrInterface())\n\t\t\t\t\t\t\t\t\t\t// \tthrow `Expected \"${resolverConfig}\" argument to reference a \"class\" or \"interface\" at ${errorFile(srcFile, t)}`;\n\t\t\t\t\t\t\t\t\t\t// let typeName = typeChecker.getSymbolAtLocation(t.typeName)!.name;\n\t\t\t\t\t\t\t\t\t\t(implementedEntities ??= []).push(targetType == null ? _getNodeName(typeName, srcFile) : _getNodeName(targetType, targetType.getSourceFile()));\n\t\t\t\t\t\t\t\t\t\tisInput = !isResolversOf;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t\tlet refName = _getNodeName(type, srcFile);\n\t\t\t\t\t\t\t\t\t\t(inheritedEntities ??= []).push(refName);\n\t\t\t\t\t\t\t\t\t\t//TODO resolve referenced entity\n\t\t\t\t\t\t\t\t\t\t// let nRef: Reference = {\n\t\t\t\t\t\t\t\t\t\t// \tkind: Kind.REF,\n\t\t\t\t\t\t\t\t\t\t// \tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t// \tname: typeSymbol.name,\n\t\t\t\t\t\t\t\t\t\t// \toName: typeSymbol.name,\n\t\t\t\t\t\t\t\t\t\t// \tfullName: undefined,\n\t\t\t\t\t\t\t\t\t\t// \tparams:\n\t\t\t\t\t\t\t\t\t\t// \t\ttype.typeArguments == null\n\t\t\t\t\t\t\t\t\t\t// \t\t\t? undefined\n\t\t\t\t\t\t\t\t\t\t// \t\t\t: [],\n\t\t\t\t\t\t\t\t\t\t// \tvisibleFields: undefined\n\t\t\t\t\t\t\t\t\t\t// };\n\t\t\t\t\t\t\t\t\t\t// visitor.push(type.typeArguments, nRef, srcFile);\n\t\t\t\t\t\t\t\t\t\t// (inherited ??= []).push(nRef);\n\t\t\t\t\t\t\t\t\t\t// //TODO resolve real nodes names\n\t\t\t\t\t\t\t\t\t\t// jsDoc.push(`@Extends ${type.getText()}`);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Get entity name\n\t\t\t\t\tif (entityName == null) {\n\t\t\t\t\t\tif (nodeEntity.name == null) throw `Unexpected anonymous class at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\tentityName = nodeEntity.name.getText();\n\t\t\t\t\t}\n\t\t\t\t\t// Get qualified name\n\t\t\t\t\tentityName = _getEntityQualifiedName(nodeEntity, entityName);\n\t\t\t\t\t// Check if is entity or entity implementation (ie: resolvers or generic entity)\n\t\t\t\t\tlet isImplementation = implementedEntities != null;\n\t\t\t\t\tisResolversImplementation = isImplementation || isResolversImplementation; // Set by @entity or helper class\n\t\t\t\t\tif (!isImplementation && nodeEntity.typeParameters?.length) {\n\t\t\t\t\t\tisImplementation = true;\n\t\t\t\t\t\timplementedEntities = [entityName];\n\t\t\t\t\t}\n\t\t\t\t\t// Resolve: First we check for INPUT and than for OUTPUT\n\t\t\t\t\tfor (let k = 0, isResolveInput = true; k < 2; k++) {\n\t\t\t\t\t\t// Escape if is explicitly input or output and we checking for other type\n\t\t\t\t\t\tisResolveInput = !isResolveInput;\n\t\t\t\t\t\tif (isInput === !isResolveInput) continue;\n\t\t\t\t\t\ttype ObjectType = InputObject | OutputObject;\n\t\t\t\t\t\tlet TARGET_MAP = isResolveInput ? INPUT_ENTITIES : OUTPUT_ENTITIES;\n\t\t\t\t\t\tlet entity: InputNode | OutputNode | undefined;\n\t\t\t\t\t\tif (!isImplementation) entity = TARGET_MAP.get(entityName);\n\t\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t\tentity = {\n\t\t\t\t\t\t\t\tkind: isResolveInput ? Kind.INPUT_OBJECT : Kind.OUTPUT_OBJECT,\n\t\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\t\tescapedName: escapeEntityName(entityName),\n\t\t\t\t\t\t\t\tfields: new Map(),\n\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\t\t\tinherit: inheritedEntities,\n\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\twrappers: undefined,\n\t\t\t\t\t\t\t\tbefore: undefined,\n\t\t\t\t\t\t\t\tafter: undefined,\n\t\t\t\t\t\t\t\townedFieldsCount: 0,\n\t\t\t\t\t\t\t\torderByName,\n\t\t\t\t\t\t\t\tconvert: undefined\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (isImplementation) {\n\t\t\t\t\t\t\t\tlet targetM = (isResolveInput ? inputHelperEntities : outputHelperEntities) as Map<string, InputObject[] | OutputObject[]>;\n\t\t\t\t\t\t\t\tfor (let l = 0, lLen = implementedEntities!.length; l < lLen; ++l) {\n\t\t\t\t\t\t\t\t\tlet entityName = implementedEntities![l];\n\t\t\t\t\t\t\t\t\tlet targetLst = targetM.get(entityName);\n\t\t\t\t\t\t\t\t\tif (targetLst == null) targetM.set(entityName, [entity as InputObject]);\n\t\t\t\t\t\t\t\t\telse (targetLst as InputObject[]).push(entity as InputObject);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t(TARGET_MAP as Map<string, ObjectType>).set(entityName, entity as ObjectType);\n\t\t\t\t\t\t} else if (entity.kind === Kind.SCALAR) {\n\t\t\t\t\t\t\t// Do nothing, just keep entity as scalar\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else if (entity.kind !== (isResolveInput ? Kind.INPUT_OBJECT : Kind.OUTPUT_OBJECT)) {\n\t\t\t\t\t\t\tthrow `Entity \"${entityName}\" has multiple types:\\n\\t> ${isResolveInput ? 'INPUT_OBJECT' : 'OUTPUT_OBJECT'\n\t\t\t\t\t\t\t} at : ${fileName}\\n\\t> ${Kind[entity.kind]} at ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (inheritedEntities != null)\n\t\t\t\t\t\t\t\t(entity.inherit ??= []).push(...inheritedEntities);\n\t\t\t\t\t\t\tentity.fileNames.push(fileName);\n\t\t\t\t\t\t\tentity.deprecated ??= deprecated;\n\t\t\t\t\t\t\tentity.orderByName ??= orderByName;\n\t\t\t\t\t\t\t// JsDoc\n\t\t\t\t\t\t\tentity.jsDoc.push(...jsDoc);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Go through properties\n\t\t\t\t\t\tfor (let i = 0, props = nodeType.getProperties(), len = props.length; i < len; ++i) {\n\t\t\t\t\t\t\tlet s = props[i];\n\t\t\t\t\t\t\tlet dec = s.valueDeclaration ?? s.declarations?.[0];\n\t\t\t\t\t\t\tif (dec == null) continue;\n\t\t\t\t\t\t\tlet propType = typeChecker.getTypeOfSymbolAtLocation(s, node);\n\t\t\t\t\t\t\tvisitor.push(dec, propType, entity, srcFile, isResolveInput, s.name, isResolversImplementation, undefined, s);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.PropertySignature:\n\t\t\t\tcase ts.SyntaxKind.MethodDeclaration:\n\t\t\t\tcase ts.SyntaxKind.PropertyDeclaration: {\n\t\t\t\t\tif (pDesc == null) continue;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpDesc.kind !== Kind.INPUT_OBJECT &&\n\t\t\t\t\t\tpDesc.kind !== Kind.OUTPUT_OBJECT\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (entityName == null) {\n\t\t\t\t\t\tthrow `Unexpected missing field name at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t}\n\t\t\t\t\tlet propertyNode = node as ts.PropertySignature | ts.MethodDeclaration | ts.PropertyDeclaration;\n\t\t\t\t\tlet className = (propertyNode.parent as ts.ClassLikeDeclaration).name?.getText();\n\t\t\t\t\tlet method: MethodDescriptor | undefined;\n\t\t\t\t\tlet isMethod = node.kind === ts.SyntaxKind.MethodDeclaration;\n\t\t\t\t\tif (isMethod) {\n\t\t\t\t\t\tif (isResolversImplementation) {\n\t\t\t\t\t\t\tif (className == null) throw `Missing class name for method \"${pDesc.name}.${entityName}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t\tmethod = {\n\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\tclassName: className,\n\t\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\t\tisAsync: _hasPromise(node as ts.MethodDeclaration),\n\t\t\t\t\t\t\t\tisStatic: node.modifiers?.some(\n\t\t\t\t\t\t\t\t\tn => n.kind === ts.SyntaxKind.StaticKeyword\n\t\t\t\t\t\t\t\t) ?? false,\n\t\t\t\t\t\t\t\tisClass: ts.isClassDeclaration(node.parent) && !node.parent.modifiers?.some(\n\t\t\t\t\t\t\t\t\te => e.kind === ts.SyntaxKind.AbstractKeyword\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontinue rootLoop; // Ignore this method cause it's only an instance method\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Create field\n\t\t\t\t\tlet fields = pDesc.fields;\n\t\t\t\t\tlet field = fields.get(entityName);\n\t\t\t\t\tif (field == null) {\n\t\t\t\t\t\t//* Add property\n\t\t\t\t\t\tif (isInput) {\n\t\t\t\t\t\t\tlet p: Omit<InputField, 'type'> & { type: undefined } = {\n\t\t\t\t\t\t\t\tkind: Kind.INPUT_FIELD,\n\t\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\t\trequired: defaultValue == null && !(node as ts.PropertyDeclaration).questionToken && _isRequired(propertyType ?? nodeType),\n\t\t\t\t\t\t\t\talias: fieldAlias,\n\t\t\t\t\t\t\t\tidx: pDesc.ownedFieldsCount++,\n\t\t\t\t\t\t\t\tclassName: className,\n\t\t\t\t\t\t\t\tdefaultValue: defaultValue,\n\t\t\t\t\t\t\t\ttype: undefined,\n\t\t\t\t\t\t\t\tasserts: asserts && _compileAsserts(asserts, undefined, srcFile, node),\n\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\tpipe: method == null ? [] : [method],\n\t\t\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\t\t\tvalidators: validatorFx\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tfield = p as any as InputField | OutputField\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlet p: Omit<OutputField, 'type'> & { type: undefined } = {\n\t\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\t\tkind: Kind.OUTPUT_FIELD,\n\t\t\t\t\t\t\t\trequired: (node as ts.PropertyDeclaration).questionToken ? false : _isRequired(propertyType ?? nodeType),\n\t\t\t\t\t\t\t\talias: fieldAlias,\n\t\t\t\t\t\t\t\tidx: pDesc.ownedFieldsCount++,\n\t\t\t\t\t\t\t\tclassName: className,\n\t\t\t\t\t\t\t\tdefaultValue: defaultValue,\n\t\t\t\t\t\t\t\ttype: undefined,\n\t\t\t\t\t\t\t\tmethod: method,\n\t\t\t\t\t\t\t\tparam: undefined,\n\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\t\t\tresolvers: resolverFx\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tfield = p as any as InputField | OutputField;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t(fields as Map<string, OutputField | InputField>).set(entityName, field);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//* Field alias\n\t\t\t\t\t\tif (field.alias == null) field.alias = fieldAlias;\n\t\t\t\t\t\telse if (field.alias !== fieldAlias)\n\t\t\t\t\t\t\tthrow `Field \"${className}.${entityName}\" could not have two aliases. got \"${field.alias}\" and \"${fieldAlias}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\tfield.deprecated ??= deprecated;\n\t\t\t\t\t\tfield.jsDoc.push(...jsDoc);\n\t\t\t\t\t\tfield.fileNames.push(fileName);\n\t\t\t\t\t\tif (method != null) {\n\t\t\t\t\t\t\tif (field.kind === Kind.INPUT_FIELD) {\n\t\t\t\t\t\t\t\tfield.pipe.push(method);\n\t\t\t\t\t\t\t} else if (field.method != null) {\n\t\t\t\t\t\t\t\tthrow `Field \"${pDesc.name}.${entityName}\" already has an ${isInput ? 'input' : 'output'\n\t\t\t\t\t\t\t\t} resolver as \"${field.method.className}.${field.method.name}\" . Got \"${className}.${entityName}\" at: ${errorFile(srcFile, node)\n\t\t\t\t\t\t\t\t}. Other files:\\n\\t> ${field.fileNames.join(\"\\n\\t> \")}`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfield.method = method;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isInput) {\n\t\t\t\t\t\t\tif (asserts != null) {\n\t\t\t\t\t\t\t\t(field as InputField).asserts = _compileAsserts(\n\t\t\t\t\t\t\t\t\tasserts,\n\t\t\t\t\t\t\t\t\t(field as InputField).asserts,\n\t\t\t\t\t\t\t\t\tsrcFile, node\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (validatorFx.length > 0)\n\t\t\t\t\t\t\t\t(field as InputField).validators.push(...validatorFx);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (resolverFx.length > 0)\n\t\t\t\t\t\t\t\t(field as OutputField).resolvers.push(...resolverFx);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Resolve param for methods\n\t\t\t\t\tif (isMethod) {\n\t\t\t\t\t\tlet param = (node as ts.MethodDeclaration).parameters?.[1];\n\t\t\t\t\t\tif (param == null) {\n\t\t\t\t\t\t\tif (isInput)\n\t\t\t\t\t\t\t\tthrow `Missing the second argument of \"${className}.${entityName}\" resolver. At ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// resolve param as input or output type\n\t\t\t\t\t\t\tvisitor.push(param, typeChecker.getTypeAtLocation(param), field, srcFile, isInput, entityName);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Resolve type\n\t\t\t\t\tif (propertyNode.type == null) {\n\t\t\t\t\t\t// TODO get implicit return value from method signature\n\t\t\t\t\t\tif (isMethod && !isInput)\n\t\t\t\t\t\t\tthrow `Missing return value of the method \"${className}.${entityName}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t} else if (!isMethod || !isInput) {\n\t\t\t\t\t\tlet propertyTypeNode = propertyNode.type;\n\t\t\t\t\t\tif (propertyType == null) propertyType = typeChecker.getTypeAtLocation(propertyNode.type);\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tpropertyTypeNode = typeChecker.typeToTypeNode(\n\t\t\t\t\t\t\t\tpropertyType, propertyTypeNode,\n\t\t\t\t\t\t\t\tts.NodeBuilderFlags.AllowUniqueESSymbolType | ts.NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope\n\t\t\t\t\t\t\t) ?? propertyTypeNode;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\t\tpropertyTypeNode, propertyType,\n\t\t\t\t\t\t\tfield, srcFile, isInput, entityName, isResolversImplementation\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.Parameter: {\n\t\t\t\t\tif (pDesc == null) continue; // Unexpected!\n\t\t\t\t\tlet paramNode = node as ts.ParameterDeclaration;\n\t\t\t\t\tlet paramName = paramNode.name?.getText();\n\t\t\t\t\tswitch (pDesc.kind) {\n\t\t\t\t\t\tcase Kind.OUTPUT_FIELD:\n\t\t\t\t\t\tcase Kind.FUNCTION_EXPRESSION:\n\t\t\t\t\t\t\tlet pRef: Param = {\n\t\t\t\t\t\t\t\tkind: Kind.PARAM,\n\t\t\t\t\t\t\t\tname: paramName,\n\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\ttype: undefined,\n\t\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t// Parse param type\n\t\t\t\t\t\t\t//TODO resolve parameter generic type\n\t\t\t\t\t\t\tif (paramNode.type != null)\n\t\t\t\t\t\t\t\tvisitor.push(paramNode.type, typeChecker.getTypeAtLocation(paramNode.type), pRef, srcFile, true, paramName);\n\t\t\t\t\t\t\tpDesc.param = pRef;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Kind.INPUT_FIELD:\n\t\t\t\t\t\t\t// Parse param type\n\t\t\t\t\t\t\tif (paramNode.type != null)\n\t\t\t\t\t\t\t\tvisitor.push(paramNode.type, typeChecker.getTypeAtLocation(paramNode.type), pDesc, srcFile, true, paramName);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow `Unexpected param parent. Got \"${Kind[pDesc.kind]}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.LastTypeNode:\n\t\t\t\tcase ts.SyntaxKind.TypeReference:\n\t\t\t\tcase ts.SyntaxKind.IntersectionType:\n\t\t\t\tcase ts.SyntaxKind.UnionType: {\n\t\t\t\t\tif (pDesc == null) continue;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpDesc.kind !== Kind.OUTPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.INPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.LIST &&\n\t\t\t\t\t\tpDesc.kind !== Kind.PARAM &&\n\t\t\t\t\t\tpDesc.kind !== Kind.UNION &&\n\t\t\t\t\t\tpDesc.kind !== Kind.CONVERTER &&\n\t\t\t\t\t\tpDesc.kind !== Kind.FUNCTION_EXPRESSION\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t//* Check if simple type name\n\t\t\t\t\tlet refTypes = _removePromiseAndNull(nodeType);\n\t\t\t\t\tif (refTypes.length === 0) throw `Field has empty type: \"${_getNodeName(node, srcFile)}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\tlet typeNode = _cleanReference(node as ts.TypeNode)\n\t\t\t\t\tif (typeNode == null) throw `Empty Type Declaration: \"${_getNodeName(node, srcFile)}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\tlet refName = _getNodeName(typeNode, srcFile);\n\t\t\t\t\tlet targetMap = isInput === true ? INPUT_ENTITIES : OUTPUT_ENTITIES;\n\t\t\t\t\tlet entity = targetMap.get(refName);\n\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t//* Check if it's enum\n\t\t\t\t\t\tconst enumMembers: ts.EnumMember[] = [];\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlet i = 0, len = refTypes.length;\n\t\t\t\t\t\t\tfor (; i < len; ++i) {\n\t\t\t\t\t\t\t\tlet type = refTypes[i];\n\t\t\t\t\t\t\t\tlet typeSymbol = type.symbol;\n\t\t\t\t\t\t\t\tif (typeSymbol == null) break;\n\t\t\t\t\t\t\t\tlet typeDec = typeSymbol.valueDeclaration ?? typeSymbol.declarations?.[0];\n\t\t\t\t\t\t\t\tif (typeDec == null) break;\n\t\t\t\t\t\t\t\tif (ts.isEnumMember(typeDec)) enumMembers.push(typeDec);\n\t\t\t\t\t\t\t\telse break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlet EnumMemberLen = enumMembers.length;\n\t\t\t\t\t\t\tif (EnumMemberLen > 0 && EnumMemberLen < len) {\n\t\t\t\t\t\t\t\t// Contains but not all of theme enum items\n\t\t\t\t\t\t\t\tthrow `Could not merge ENUM with other types at: \"${_getNodeName(node, srcFile)}\" at ${errorFile(srcFile, node)}`\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (enumMembers.length) {\n\t\t\t\t\t\t\trefName = _getUnionNameFromTypes(refTypes);\n\t\t\t\t\t\t\tlet entity = INPUT_ENTITIES.get(refName);\n\t\t\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t\t\tentity = {\n\t\t\t\t\t\t\t\t\tkind: Kind.ENUM,\n\t\t\t\t\t\t\t\t\tname: refName,\n\t\t\t\t\t\t\t\t\tescapedName: escapeEntityName(refName),\n\t\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\t\tmembers: [],\n\t\t\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tINPUT_ENTITIES.set(refName, entity);\n\t\t\t\t\t\t\t\tOUTPUT_ENTITIES.set(refName, entity);\n\t\t\t\t\t\t\t\tfor (let i = 0, len = enumMembers.length; i < len; ++i) {\n\t\t\t\t\t\t\t\t\tlet member = enumMembers[i];\n\t\t\t\t\t\t\t\t\tvisitor.push(member, refTypes[i], entity, srcFile, undefined, undefined, isResolversImplementation);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (entity.kind !== Kind.ENUM || OUTPUT_ENTITIES.get(refName) !== entity) {\n\t\t\t\t\t\t\t\tthrow `Duplicate entity \"${refName}\" at ${errorFile(srcFile, node)} and ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//* Check are constants\n\t\t\t\t\t\t//* Is reference\n\t\t\t\t\t\telse if (refTypes.length === 1) {\n\t\t\t\t\t\t\t//* Resolve to a single type\n\t\t\t\t\t\t\tlet type = refTypes[0];\n\t\t\t\t\t\t\tlet typeNode = typeChecker.typeToTypeNode(type, undefined, undefined);\n\t\t\t\t\t\t\tif (typeNode && typeNode.kind === ts.SyntaxKind.ArrayType) {\n\t\t\t\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\t\t\t\ttypeNode, type, pDesc, srcFile, isInput, entityName, isResolversImplementation\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t(type as ts.TypeReference).typeArguments != null ||\n\t\t\t\t\t\t\t\t\t(type as ts.TypeReference).aliasTypeArguments != null\n\t\t\t\t\t\t\t\t\t// (type as any as { typeParameter: any }).typeParameter != null\n\t\t\t\t\t\t\t\t) && !targetMap.has(refName)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// Resolve generic type\n\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(isInput, refName, fileName, deprecated, undefined);\n\t\t\t\t\t\t\t\tconst foundSymbols: Set<string> = new Set();\n\t\t\t\t\t\t\t\tfor (let j = 0, properties = type.getProperties(), jLen = properties.length; j < jLen; ++j) {\n\t\t\t\t\t\t\t\t\tlet property = properties[j];\n\t\t\t\t\t\t\t\t\tlet propertyTypeName = property.name;\n\t\t\t\t\t\t\t\t\tif (!foundSymbols.has(propertyTypeName)) {\n\t\t\t\t\t\t\t\t\t\tlet propertyDeclaration = (property.valueDeclaration ?? property.declarations?.[0]) as ts.PropertyDeclaration;\n\t\t\t\t\t\t\t\t\t\tif (propertyDeclaration == null) continue;\n\t\t\t\t\t\t\t\t\t\tif (propertyDeclaration.getSourceFile().isDeclarationFile)\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t// Resolve\n\t\t\t\t\t\t\t\t\t\tfoundSymbols.add(propertyTypeName);\n\t\t\t\t\t\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\t\t\t\t\t\tpropertyDeclaration, typeChecker.getTypeAtLocation(propertyDeclaration),\n\t\t\t\t\t\t\t\t\t\t\tentity, srcFile, isInput, propertyTypeName, isResolversImplementation,\n\t\t\t\t\t\t\t\t\t\t\ttypeChecker.getTypeOfSymbolAtLocation(property, propertyDeclaration),\n\t\t\t\t\t\t\t\t\t\t\tproperty\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (type.symbol != null) {\n\t\t\t\t\t\t\t\t//TODO find better way to resolve type name\n\t\t\t\t\t\t\t\trefName = typeChecker.typeToString(type, typeNode, ts.TypeFormatFlags.UseFullyQualifiedType); // referenced node's name\n\t\t\t\t\t\t\t\tlet i = refName.lastIndexOf(')');\n\t\t\t\t\t\t\t\tif (i > -1) {\n\t\t\t\t\t\t\t\t\trefName = refName.slice(i + 2);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//* Is intersection\n\t\t\t\t\t\telse if (typeChecker.getNonNullableType(nodeType).isIntersection()) {\n\t\t\t\t\t\t\t// Resolve generic type\n\t\t\t\t\t\t\tlet entity = _upObjectEntity(isInput, refName, fileName, deprecated, undefined);\n\t\t\t\t\t\t\tconst foundSymbols: Set<string> = new Set();\n\t\t\t\t\t\t\tfor (let i = 0, len = refTypes.length; i < len; ++i) {\n\t\t\t\t\t\t\t\tlet type = refTypes[i];\n\t\t\t\t\t\t\t\tfor (let j = 0, properties = type.getProperties(), jLen = properties.length; j < jLen; ++j) {\n\t\t\t\t\t\t\t\t\tlet property = properties[j];\n\t\t\t\t\t\t\t\t\tlet propertyTypeName = property.name;\n\t\t\t\t\t\t\t\t\tif (!foundSymbols.has(propertyTypeName)) {\n\t\t\t\t\t\t\t\t\t\tlet propertyDeclaration = (property.valueDeclaration ?? property.declarations?.[0]) as ts.PropertyDeclaration | undefined;\n\t\t\t\t\t\t\t\t\t\tif (propertyDeclaration == null) continue;\n\t\t\t\t\t\t\t\t\t\tif (propertyDeclaration.getSourceFile().isDeclarationFile)\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t// Resolve\n\t\t\t\t\t\t\t\t\t\tfoundSymbols.add(propertyTypeName);\n\t\t\t\t\t\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\t\t\t\t\t\tpropertyDeclaration, typeChecker.getTypeAtLocation(propertyDeclaration),\n\t\t\t\t\t\t\t\t\t\t\tentity, srcFile, isInput, propertyTypeName, isResolversImplementation,\n\t\t\t\t\t\t\t\t\t\t\ttypeChecker.getTypeOfSymbolAtLocation(property, propertyDeclaration),\n\t\t\t\t\t\t\t\t\t\t\tproperty\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//* Is Union\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t//* Resolve union\n\t\t\t\t\t\t\t// refName = _getUnionNameFromTypes(refTypes);\n\t\t\t\t\t\t\tlet entity = INPUT_ENTITIES.get(refName);\n\t\t\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t\t\tentity = {\n\t\t\t\t\t\t\t\t\tkind: Kind.UNION,\n\t\t\t\t\t\t\t\t\tname: refName,\n\t\t\t\t\t\t\t\t\tescapedName: escapeEntityName(refName),\n\t\t\t\t\t\t\t\t\t// baseName: _getUnionNameFromTypes(refTypes),\n\t\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\t\ttypes: [],\n\t\t\t\t\t\t\t\t\tparser: undefined,\n\t\t\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tINPUT_ENTITIES.set(refName, entity);\n\t\t\t\t\t\t\t\tOUTPUT_ENTITIES.set(refName, entity);\n\t\t\t\t\t\t\t} else if (entity.kind !== Kind.UNION || OUTPUT_ENTITIES.get(refName) !== entity) {\n\t\t\t\t\t\t\t\tthrow `Duplicate UNION entity \"${refName}\" at ${errorFile(srcFile, node)} and as \"${Kind[entity.kind]}\" in ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//* Reference\n\t\t\t\t\tlet refEnt: Reference = {\n\t\t\t\t\t\tkind: Kind.REF,\n\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\tname: refName\n\t\t\t\t\t};\n\t\t\t\t\tif (pDesc.kind === Kind.UNION) pDesc.types.push(refEnt);\n\t\t\t\t\telse pDesc.type = refEnt;\n\t\t\t\t\t// }\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.StringKeyword:\n\t\t\t\tcase ts.SyntaxKind.BooleanKeyword:\n\t\t\t\tcase ts.SyntaxKind.NumberKeyword:\n\t\t\t\tcase ts.SyntaxKind.SymbolKeyword:\n\t\t\t\tcase ts.SyntaxKind.BigIntKeyword: {\n\t\t\t\t\tif (pDesc == null) continue;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpDesc.kind !== Kind.OUTPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.INPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.LIST &&\n\t\t\t\t\t\t// pDesc.kind !== Kind.REF &&\n\t\t\t\t\t\tpDesc.kind !== Kind.PARAM\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tlet nodeName = _getNodeName(node, srcFile);\n\t\t\t\t\tpDesc.type = {\n\t\t\t\t\t\tkind: Kind.REF,\n\t\t\t\t\t\tname: nodeName,\n\t\t\t\t\t\tfileName: srcFile.fileName\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.ArrayType: {\n\t\t\t\t\tif (pDesc == null) continue;\n\t\t\t\t\tif (\n\t\t\t\t\t\tpDesc.kind !== Kind.OUTPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.INPUT_FIELD &&\n\t\t\t\t\t\tpDesc.kind !== Kind.LIST &&\n\t\t\t\t\t\tpDesc.kind !== Kind.PARAM\n\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tlet arrTpe: Omit<List, 'type'> & { type: undefined } = {\n\t\t\t\t\t\tkind: Kind.LIST,\n\t\t\t\t\t\trequired: true, // TODO find a solution to make list content nullable\n\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\ttype: undefined\n\t\t\t\t\t};\n\t\t\t\t\tlet arrType = arrTpe as any as List;\n\t\t\t\t\tpDesc.type = arrType;\n\t\t\t\t\t// Visit each children\n\t\t\t\t\tlet arrEl = (node as ts.ArrayTypeNode).elementType;\n\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\tarrEl,\n\t\t\t\t\t\t(typeChecker.getTypeFromTypeNode(arrEl)),\n\t\t\t\t\t\tarrType, srcFile, isInput, entityName);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.EnumDeclaration: {\n\t\t\t\t\tlet enumNode = node as ts.EnumDeclaration;\n\t\t\t\t\tlet nodeName = _getEntityQualifiedName(enumNode, enumNode.name?.getText());\n\t\t\t\t\t// Check for duplicate entities\n\t\t\t\t\tlet entity = INPUT_ENTITIES.get(nodeName);\n\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t// Create Enum\n\t\t\t\t\t\tentity = {\n\t\t\t\t\t\t\tkind: Kind.ENUM,\n\t\t\t\t\t\t\tname: nodeName,\n\t\t\t\t\t\t\tescapedName: escapeEntityName(nodeName),\n\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\tmembers: [],\n\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t};\n\t\t\t\t\t\tINPUT_ENTITIES.set(nodeName, entity);\n\t\t\t\t\t\tOUTPUT_ENTITIES.set(nodeName, entity);\n\t\t\t\t\t}\n\t\t\t\t\telse if (entity.kind !== Kind.ENUM || entity !== OUTPUT_ENTITIES.get(nodeName))\n\t\t\t\t\t\tthrow `Duplicate ENUM \"${nodeName}\" at ${errorFile(srcFile, node)}. Other files: \\n\\t> ${entity.fileNames.join(\"\\n\\t> \")}`;\n\t\t\t\t\telse {\n\t\t\t\t\t\tentity.jsDoc.push(...jsDoc);\n\t\t\t\t\t\tentity.deprecated ??= deprecated;\n\t\t\t\t\t\tentity.fileNames.push(fileName);\n\t\t\t\t\t}\n\t\t\t\t\t// Resolve children\n\t\t\t\t\tfor (let i = 0, members = enumNode.members, len = members.length; i < len; ++i) {\n\t\t\t\t\t\tlet member = members[i];\n\t\t\t\t\t\tvisitor.push(member, typeChecker.getTypeAtLocation(member), entity, srcFile, undefined);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.EnumMember: {\n\t\t\t\t\t//* Enum member\n\t\t\t\t\tlet nodeName = (node as ts.EnumMember).name?.getText();\n\t\t\t\t\tif (pDesc == null || pDesc.kind != Kind.ENUM)\n\t\t\t\t\t\tthrow `Unexpected ENUM MEMBER \"${nodeName}\" at: ${errorFile(srcFile, node)}`;\n\t\t\t\t\tlet enumMember: EnumMember = {\n\t\t\t\t\t\tkind: Kind.ENUM_MEMBER,\n\t\t\t\t\t\tname: nodeName,\n\t\t\t\t\t\tvalue: typeChecker.getConstantValue(node as ts.EnumMember)!,\n\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t};\n\t\t\t\t\tpDesc.members.push(enumMember);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.TypeLiteral: {\n\t\t\t\t\t//* Type literal are equivalent to nameless classes\n\t\t\t\t\tif (pDesc == null) continue;\n\t\t\t\t\tif (pDesc.kind === Kind.INPUT_OBJECT || pDesc.kind === Kind.OUTPUT_OBJECT) {\n\t\t\t\t\t\t//* Update already defined plain object\n\t\t\t\t\t\t//TODO check works\n\t\t\t\t\t\tvisitor.pushChildren(typeChecker, node, pDesc, srcFile, isInput, undefined, isResolversImplementation);\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tpDesc.kind === Kind.OUTPUT_FIELD ||\n\t\t\t\t\t\tpDesc.kind === Kind.INPUT_FIELD ||\n\t\t\t\t\t\tpDesc.kind === Kind.LIST ||\n\t\t\t\t\t\tpDesc.kind === Kind.PARAM\n\t\t\t\t\t) {\n\t\t\t\t\t\tentityName ??= '';\n\t\t\t\t\t\t// let nodeType = typeChecker.getTypeAtLocation(node);\n\t\t\t\t\t\tlet entity: InputObject | OutputObject = {\n\t\t\t\t\t\t\tkind: isInput ? Kind.INPUT_OBJECT : Kind.OUTPUT_OBJECT,\n\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\tescapedName: escapeEntityName(entityName),\n\t\t\t\t\t\t\tfields: new Map(),\n\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\t\tinherit: undefined,\n\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\twrappers: undefined,\n\t\t\t\t\t\t\tbefore: undefined,\n\t\t\t\t\t\t\tafter: undefined,\n\t\t\t\t\t\t\townedFieldsCount: 0,\n\t\t\t\t\t\t\torderByName,\n\t\t\t\t\t\t\tconvert: undefined\n\t\t\t\t\t\t};\n\t\t\t\t\t\tlet typeRef: Reference = {\n\t\t\t\t\t\t\tkind: Kind.REF,\n\t\t\t\t\t\t\tname: entityName,\n\t\t\t\t\t\t\tfileName: srcFile.fileName\n\t\t\t\t\t\t};\n\t\t\t\t\t\tLITERAL_OBJECTS.push({ node: entity, ref: typeRef, isInput });\n\t\t\t\t\t\tpDesc.type = typeRef;\n\t\t\t\t\t\t// Go through fields\n\t\t\t\t\t\tvisitor.pushChildren(typeChecker, node, entity, srcFile, isInput, undefined, isResolversImplementation);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.VariableStatement: {\n\t\t\t\t\t//* Check node has export\n\t\t\t\t\tlet variableNode = node as ts.VariableStatement;\n\t\t\t\t\tlet hasExport = variableNode.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;\n\t\t\t\t\t//* Check each variable declaration\n\t\t\t\t\tfor (\n\t\t\t\t\t\tlet i = 0,\n\t\t\t\t\t\tdeclarations = variableNode.declarationList.declarations,\n\t\t\t\t\t\tlen = declarations.length;\n\t\t\t\t\t\ti < len; ++i\n\t\t\t\t\t) {\n\t\t\t\t\t\t//* Check has type definition\n\t\t\t\t\t\tlet declaration = declarations[i];\n\t\t\t\t\t\tlet type = declaration.type;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdeclaration.initializer == null ||\n\t\t\t\t\t\t\ttype == null ||\n\t\t\t\t\t\t\t!ts.isTypeReferenceNode(type)\n\t\t\t\t\t\t) continue;\n\t\t\t\t\t\tlet nodeName = declaration.name.getText();\n\t\t\t\t\t\t//* Check type imported from tt-model\n\t\t\t\t\t\tlet s = typeChecker.getSymbolAtLocation(type.typeName);\n\t\t\t\t\t\tlet d = s?.declarations?.[0];\n\t\t\t\t\t\tif (d == null || !ts.isImportSpecifier(d)) continue;\n\t\t\t\t\t\tlet lib = d.parent.parent.parent.moduleSpecifier.getText().slice(1, -1);\n\t\t\t\t\t\tlet opName = (d.propertyName ?? d.name).getText();\n\t\t\t\t\t\tif (lib !== PACKAGE_NAME) continue;\n\t\t\t\t\t\t//* Resolve node type name\n\t\t\t\t\t\t// let tp = typeChecker.getTypeAtLocation(declaration);\n\t\t\t\t\t\t// s = tp.symbol;\n\t\t\t\t\t\t// if (s == null) continue;\n\t\t\t\t\t\t//* Check has export\n\t\t\t\t\t\tif (hasExport === false)\n\t\t\t\t\t\t\tthrow `Missing \"export\" keyword on \"${nodeName}:${type.typeName.getText()}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t//* Resolve\n\t\t\t\t\t\tif (type.typeArguments?.length === 1) {\n\t\t\t\t\t\t\tlet typeArg = type.typeArguments[0];\n\t\t\t\t\t\t\tlet fieldName = typeArg.getText();\n\t\t\t\t\t\t\tif (!ts.isTypeReferenceNode(typeArg))\n\t\t\t\t\t\t\t\tthrow `Unexpected Entity Name: \"${fieldName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t//* Check has correct config. Only \"Scalar\" has object configuration, others has function\n\t\t\t\t\t\t\tswitch (opName) {\n\t\t\t\t\t\t\t\tcase 'Scalar':\n\t\t\t\t\t\t\t\t\t_assertEntityNotFound(fieldName, declaration, srcFile);\n\t\t\t\t\t\t\t\t\tif (!ts.isObjectLiteralExpression(declaration.initializer))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected an object to define scalar \"${fieldName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase 'UnionResolver':\n\t\t\t\t\t\t\t\tcase 'ConvertInput':\n\t\t\t\t\t\t\t\tcase 'ConvertOutput':\n\t\t\t\t\t\t\t\t\tif (!ts.isFunctionExpression(declaration.initializer))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected function expressions for \"${fieldName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t//* Other config are functions\n\t\t\t\t\t\t\t\t\t_assertEntityNotFound(fieldName, declaration, srcFile);\n\t\t\t\t\t\t\t\t\tif (!ts.isFunctionExpression(declaration.initializer))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected function expressions for \"${fieldName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t//* Add data\n\t\t\t\t\t\t\tswitch (opName) {\n\t\t\t\t\t\t\t\tcase 'Scalar': {\n\t\t\t\t\t\t\t\t\t//* Scalar\n\t\t\t\t\t\t\t\t\tlet scalarEntity = INPUT_ENTITIES.get(fieldName) as Scalar | undefined;\n\t\t\t\t\t\t\t\t\tif (scalarEntity == null || scalarEntity.kind !== Kind.SCALAR) {\n\t\t\t\t\t\t\t\t\t\tscalarEntity = {\n\t\t\t\t\t\t\t\t\t\t\tkind: Kind.SCALAR,\n\t\t\t\t\t\t\t\t\t\t\tname: fieldName,\n\t\t\t\t\t\t\t\t\t\t\tescapedName: escapeEntityName(fieldName),\n\t\t\t\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\t\t\t\tparser: {\n\t\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\tisAsync: false,\n\t\t\t\t\t\t\t\t\t\t\t\tisStatic: true,\n\t\t\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\t\t\tisClass: false\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t//TODO Enable partial scalar definition\n\t\t\t\t\t\t\t\t\t\tconsole.error('Scalar redefined')\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// JUST OVERRIDE WHEN SCALAR :)\n\t\t\t\t\t\t\t\t\tINPUT_ENTITIES.set(fieldName, scalarEntity);\n\t\t\t\t\t\t\t\t\tOUTPUT_ENTITIES.set(fieldName, scalarEntity);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase 'UnionResolver': {\n\t\t\t\t\t\t\t\t\t// parse types\n\t\t\t\t\t\t\t\t\tlet typeNode = _cleanReference(typeArg);\n\t\t\t\t\t\t\t\t\tif (typeNode == null)\n\t\t\t\t\t\t\t\t\t\tthrow `Wrong union reference \"${_getNodeName(declaration, srcFile)}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tlet unionName = _getNodeName(typeNode, srcFile); //_getUnionNameFromTypes(types);\n\t\t\t\t\t\t\t\t\tlet entity = INPUT_ENTITIES.get(unionName) as Union | undefined;\n\t\t\t\t\t\t\t\t\tif (entity == null) {\n\t\t\t\t\t\t\t\t\t\tif (entity = OUTPUT_ENTITIES.get(unionName) as Union | undefined)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Duplicate entity \"${unionName}\" at ${errorFile(srcFile, declaration)\n\t\t\t\t\t\t\t\t\t\t\t} and ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t\t\t\t\tentity = {\n\t\t\t\t\t\t\t\t\t\t\tkind: Kind.UNION,\n\t\t\t\t\t\t\t\t\t\t\tname: unionName,\n\t\t\t\t\t\t\t\t\t\t\tescapedName: escapeEntityName(fieldName),\n\t\t\t\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\t\t\t\ttypes: [],\n\t\t\t\t\t\t\t\t\t\t\tparser: {\n\t\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\tisStatic: true,\n\t\t\t\t\t\t\t\t\t\t\t\tisAsync: false,\n\t\t\t\t\t\t\t\t\t\t\t\t// name: 'resolveType',\n\t\t\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\t\t\tisClass: false\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tfileNames: [fileName]\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tINPUT_ENTITIES.set(unionName, entity);\n\t\t\t\t\t\t\t\t\t\tOUTPUT_ENTITIES.set(unionName, entity);\n\t\t\t\t\t\t\t\t\t} else if (entity.kind != Kind.UNION || OUTPUT_ENTITIES.get(unionName) !== entity) {\n\t\t\t\t\t\t\t\t\t\tthrow `Could not create union for \"${unionName}\" at ${errorFile(srcFile, declaration)}. Already defined as \"${Kind[entity.kind]}\" at ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t\t\t\t} else if (entity.parser != null) {\n\t\t\t\t\t\t\t\t\t\tthrow `Union for \"${unionName}\" at ${errorFile(srcFile, declaration)} already defined at ${entity.fileNames.join(', ')}`;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tentity.name = unionName;\n\t\t\t\t\t\t\t\t\t\tentity.jsDoc.push(...jsDoc);\n\t\t\t\t\t\t\t\t\t\tentity.deprecated ??= deprecated;\n\t\t\t\t\t\t\t\t\t\tentity.fileNames.push(fileName);\n\t\t\t\t\t\t\t\t\t\tentity.parser ??= {\n\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\tisAsync: false,\n\t\t\t\t\t\t\t\t\t\t\tisStatic: true,\n\t\t\t\t\t\t\t\t\t\t\t// name: 'resolveType',\n\t\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\t\tisClass: false\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// Add child entities\n\t\t\t\t\t\t\t\t\tlet types = _removePromiseAndNull(typeChecker.getTypeFromTypeNode(typeArg));\n\t\t\t\t\t\t\t\t\tfor (let i = 0, len = types.length; i < len; ++i) {\n\t\t\t\t\t\t\t\t\t\tlet type = types[i];\n\t\t\t\t\t\t\t\t\t\tlet typeSymbol = type.symbol;\n\t\t\t\t\t\t\t\t\t\tif (typeSymbol == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing definition for union type \"${typeChecker.typeToString(type)}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\t\tif (!type.isClassOrInterface())\n\t\t\t\t\t\t\t\t\t\t\tthrow `Union type \"${typeChecker.typeToString(type)}\" expected Interface or Class at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\t\tlet typeNode = typeSymbol.valueDeclaration ?? typeSymbol.declarations?.[0];\n\t\t\t\t\t\t\t\t\t\tif (typeNode == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing definition for union type \"${typeChecker.typeToString(type)}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\t\tvisitor.push(typeNode, type, entity, srcFile, undefined, undefined, isResolversImplementation);\n\t\t\t\t\t\t\t\t\t\tentity.types.push({\n\t\t\t\t\t\t\t\t\t\t\tkind: Kind.REF, name: typeSymbol.name, fileName\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* PreValidate\n\t\t\t\t\t\t\t\tcase 'PreValidate': {\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(true, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.before ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* PreValidate\n\t\t\t\t\t\t\t\tcase 'PostValidate': {\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(true, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.after ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* WrapValidation\n\t\t\t\t\t\t\t\tcase 'WrapValidation': {\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(true, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.wrappers ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* PreResolve\n\t\t\t\t\t\t\t\tcase 'PreResolve': {\n\t\t\t\t\t\t\t\t\t_assertEntityNotFound(fieldName, declaration, srcFile);\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(false, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.before ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase 'PostResolve': {\n\t\t\t\t\t\t\t\t\t_assertEntityNotFound(fieldName, declaration, srcFile);\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(false, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.after ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase 'WrapResolver': {\n\t\t\t\t\t\t\t\t\t_assertEntityNotFound(fieldName, declaration, srcFile);\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(false, fieldName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\t(entity.wrappers ??= []).push({\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(declaration.initializer as ts.FunctionExpression)\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* Convert input\n\t\t\t\t\t\t\t\tcase 'ConvertInput':\n\t\t\t\t\t\t\t\tcase 'ConvertOutput': {\n\t\t\t\t\t\t\t\t\t// Resolve Entities\n\t\t\t\t\t\t\t\t\tlet entityName = typeChecker.getTypeAtLocation(typeArg.typeName)?.symbol?.name;\n\t\t\t\t\t\t\t\t\tif (entityName == null)\n\t\t\t\t\t\t\t\t\t\tthrow `Could not resolve entity: \"${fieldName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tconst fxDeclaration = declaration.initializer;\n\t\t\t\t\t\t\t\t\tif (!ts.isFunctionExpression(fxDeclaration))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected function expressions for \"${opName}\" at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\t//---\n\t\t\t\t\t\t\t\t\tlet isInput = opName === 'ConvertInput';\n\t\t\t\t\t\t\t\t\tlet entity = _upObjectEntity(isInput, entityName, fileName, deprecated, jsDoc);\n\t\t\t\t\t\t\t\t\tif (entity.convert != null)\n\t\t\t\t\t\t\t\t\t\tthrow `${opName}<${entityName}> already defined at ${entity.convert.fileName}. at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tentity.convert = {\n\t\t\t\t\t\t\t\t\t\tkind: Kind.CONVERTER,\n\t\t\t\t\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(fxDeclaration),\n\t\t\t\t\t\t\t\t\t\ttype: undefined\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t// Resolve\n\t\t\t\t\t\t\t\t\tif (isInput) {\n\t\t\t\t\t\t\t\t\t\t// Resolve input param\n\t\t\t\t\t\t\t\t\t\tlet paramType = _rmNull(fxDeclaration.parameters?.[1]?.type);\n\t\t\t\t\t\t\t\t\t\tif (paramType == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing param for ${opName}<${entityName}> at ${errorFile(srcFile, fxDeclaration)}`;\n\t\t\t\t\t\t\t\t\t\tvisitor.push(paramType, typeChecker.getTypeAtLocation(paramType), entity.convert, srcFile, undefined, entityName);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tlet tp = fxDeclaration.type;\n\t\t\t\t\t\t\t\t\t\tif (tp == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing return type for ${opName}<${entityName}> at ${errorFile(srcFile, fxDeclaration)}`;\n\t\t\t\t\t\t\t\t\t\tvisitor.push(tp, typeChecker.getTypeAtLocation(tp), entity.convert, srcFile, undefined, entityName);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tswitch (opName) {\n\t\t\t\t\t\t\t\t//* Root config\n\t\t\t\t\t\t\t\tcase 'RootConfig': {\n\t\t\t\t\t\t\t\t\tlet obj = declaration.initializer;\n\t\t\t\t\t\t\t\t\tif (obj == null)\n\t\t\t\t\t\t\t\t\t\tthrow `Missing wrapper method at ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\tif (!ts.isObjectLiteralExpression(obj))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected an object expression to define the root configuration. Got \"${ts.SyntaxKind[obj.kind]}\" at ${errorFile(srcFile, obj)}`;\n\t\t\t\t\t\t\t\t\tfor (let j = 0, properties = obj.properties, jLen = properties.length; j < jLen; ++j) {\n\t\t\t\t\t\t\t\t\t\tlet property = properties[j];\n\t\t\t\t\t\t\t\t\t\tlet propertyName = property.name?.getText() as keyof RootConfigTTModel;\n\t\t\t\t\t\t\t\t\t\tswitch (propertyName) {\n\t\t\t\t\t\t\t\t\t\t\tcase \"after\": {\n\t\t\t\t\t\t\t\t\t\t\t\trootConfig.after.push({\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: propertyName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(property)\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tcase \"before\": {\n\t\t\t\t\t\t\t\t\t\t\t\trootConfig.before.push({\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: propertyName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(property)\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tcase 'wrap': {\n\t\t\t\t\t\t\t\t\t\t\t\trootConfig.wrappers.push({\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: propertyName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName: nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(property)\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t\t\t\tlet n: never = propertyName;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t//* Output resolver\n\t\t\t\t\t\t\t\tcase 'Resolver':\n\t\t\t\t\t\t\t\tcase 'Validator': {\n\t\t\t\t\t\t\t\t\tconst isValidator = opName === 'Validator';\n\t\t\t\t\t\t\t\t\tconst targetMap = isValidator ? INPUT_VALIDATORS : OUTPUT_RESOLVERS;\n\t\t\t\t\t\t\t\t\tlet v = targetMap.get(nodeName);\n\t\t\t\t\t\t\t\t\tif (v != null)\n\t\t\t\t\t\t\t\t\t\tthrow `Duplicated ${isValidator ? 'validator' : 'resolver'} \"${nodeName}\" at ${errorFile(srcFile, declaration)} and ${v.fileName}`;\n\t\t\t\t\t\t\t\t\tconst fxExpression = declaration.initializer;\n\t\t\t\t\t\t\t\t\tif (!ts.isFunctionExpression(fxExpression))\n\t\t\t\t\t\t\t\t\t\tthrow `Expected function expression for ${isValidator ? 'validator' : 'resolver'} \"${nodeName}\" at ${errorFile(srcFile, declaration)}. Got ${ts.SyntaxKind[fxExpression.kind]}`;\n\t\t\t\t\t\t\t\t\tconst desc: FunctionExpr = {\n\t\t\t\t\t\t\t\t\t\tkind: Kind.FUNCTION_EXPRESSION,\n\t\t\t\t\t\t\t\t\t\tname: nodeName,\n\t\t\t\t\t\t\t\t\t\tfileName: fileName,\n\t\t\t\t\t\t\t\t\t\tfileNames: [fileName],\n\t\t\t\t\t\t\t\t\t\tdeprecated: deprecated,\n\t\t\t\t\t\t\t\t\t\tjsDoc: jsDoc,\n\t\t\t\t\t\t\t\t\t\tisAsync: _hasPromise(fxExpression),\n\t\t\t\t\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\t\t\t\t\tparam: undefined,\n\t\t\t\t\t\t\t\t\t\t// @ts-ignore\n\t\t\t\t\t\t\t\t\t\ttype: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ttargetMap.set(nodeName, desc);\n\t\t\t\t\t\t\t\t\t//* Resolve type\n\t\t\t\t\t\t\t\t\tlet param = fxExpression.parameters?.[1];\n\t\t\t\t\t\t\t\t\tif (param == null) {\n\t\t\t\t\t\t\t\t\t\tif (isValidator)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing the second argument of validator \"${nodeName}\" resolver. At ${errorFile(srcFile, declaration)}`;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// resolve param as input or output type\n\t\t\t\t\t\t\t\t\t\tvisitor.push(param, typeChecker.getTypeAtLocation(param), desc, srcFile, isValidator, nodeName);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t//* Resolve output type for \"Resolver\"\n\t\t\t\t\t\t\t\t\tif (!isValidator) {\n\t\t\t\t\t\t\t\t\t\tif (fxExpression.type == null)\n\t\t\t\t\t\t\t\t\t\t\tthrow `Missing return value of the function \"${nodeName}\" at ${errorFile(srcFile, node)}`;\n\t\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\t\tlet propertyTypeNode = fxExpression.type;\n\t\t\t\t\t\t\t\t\t\t\tif (propertyType == null) propertyType = typeChecker.getTypeAtLocation(propertyTypeNode);\n\t\t\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\t\t\tpropertyTypeNode = typeChecker.typeToTypeNode(\n\t\t\t\t\t\t\t\t\t\t\t\t\tpropertyType, propertyTypeNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tts.NodeBuilderFlags.AllowUniqueESSymbolType | ts.NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope\n\t\t\t\t\t\t\t\t\t\t\t\t) ?? propertyTypeNode;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tvisitor.push(\n\t\t\t\t\t\t\t\t\t\t\t\tpropertyTypeNode, propertyType,\n\t\t\t\t\t\t\t\t\t\t\t\tdesc, srcFile, isInput, entityName, isResolversImplementation\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase ts.SyntaxKind.SyntaxList:\n\t\t\t\tcase ts.SyntaxKind.ModuleDeclaration:\n\t\t\t\tcase ts.SyntaxKind.ModuleBlock:\n\t\t\t\t\tvisitor.pushChildren(typeChecker, node, pDesc, srcFile, isInput, undefined);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ts.SyntaxKind.TupleType:\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Tuples are unsupported, did you mean Array of type? at ${errorFile(srcFile, node)\n\t\t\t\t\t\t}\\n${node.getText()}`\n\t\t\t\t\t);\n\t\t\t\tcase ts.SyntaxKind.TypeOperator: {\n\t\t\t\t\t//FIXME Check what TypeOperatorNode do!\n\t\t\t\t\tlet tp = (node as ts.TypeOperatorNode).type;\n\t\t\t\t\tvisitor.push(tp, typeChecker.getTypeAtLocation(tp), pDesc, srcFile, isInput);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\t// console.log('--- GOT: ', !!pDesc, ts.SyntaxKind[node.kind]);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error: any) {\n\t\t\tif (typeof error === 'string') errors.push(error);\n\t\t\telse throw error;\n\t\t}\n\t}\n\t//* Throw errors if found\n\tif (errors.length) throw new TError(E.PARSING_ERRORS, `Parsing Errors: \\n\\t - ${errors.join('\\n\\t- ')} `);\n\t//* STEP 2: ADD DEFAULT SCALARS\n\tfor (let i = 0, len = DEFAULT_SCALARS.length; i < len; ++i) {\n\t\tlet fieldName = DEFAULT_SCALARS[i];\n\t\tlet scalarNode: BasicScalar = {\n\t\t\tkind: Kind.BASIC_SCALAR,\n\t\t\tname: fieldName,\n\t\t\tescapedName: escapeEntityName(fieldName),\n\t\t\tdeprecated: undefined,\n\t\t\tfileNames: [],\n\t\t\tjsDoc: []\n\t\t};\n\t\tlet entity = INPUT_ENTITIES.get(fieldName);\n\t\tif (entity == null || entity.kind === Kind.UNION) {\n\t\t\tINPUT_ENTITIES.set(fieldName, scalarNode);\n\t\t\tOUTPUT_ENTITIES.set(fieldName, scalarNode);\n\t\t}\n\t}\n\t//* Resolve nameless entities\n\tfor (\n\t\tlet i = 0, len = LITERAL_OBJECTS.length, namelessMap: Map<string, number> = new Map(); i < len; ++i\n\t) {\n\t\tlet item = LITERAL_OBJECTS[i];\n\t\tlet node = item.node;\n\t\tlet itemName = node.name ?? item.isInput ? 'Input' : 'Output';\n\t\tlet targetMap = item.isInput ? INPUT_ENTITIES : OUTPUT_ENTITIES;\n\t\tlet tmpN = itemName;\n\t\tlet itemI = namelessMap.get(tmpN) ?? 0;\n\t\twhile (targetMap.has(itemName)) {\n\t\t\t++itemI;\n\t\t\titemName = `${tmpN}_${itemI}`;\n\t\t}\n\t\tnamelessMap.set(tmpN, itemI);\n\t\tnode.name = itemName;\n\t\titem.ref.name = itemName;\n\t\t(targetMap as Map<string, InputObject | OutputObject>).set(itemName, node);\n\t}\n\t//* Adjust inheritance\n\tOUTPUT_ENTITIES.forEach((entity) => {\n\t\t//* List full inheritance list\n\t\tif (entity.kind === Kind.OUTPUT_OBJECT) {\n\t\t\t_adjustInheritance(entity, OUTPUT_ENTITIES);\n\t\t}\n\t});\n\tINPUT_ENTITIES.forEach((entity) => {\n\t\t//* List full inheritance list\n\t\tif (entity.kind === Kind.INPUT_OBJECT) {\n\t\t\t_adjustInheritance(entity, INPUT_ENTITIES);\n\t\t}\n\t});\n\t//* Return\n\treturn {\n\t\tinput: INPUT_ENTITIES,\n\t\toutput: OUTPUT_ENTITIES,\n\t\trootConfig,\n\t\tinputHelperEntities: _mergeEntityHelpers(inputHelperEntities),\n\t\toutputHelperEntities: _mergeEntityHelpers(outputHelperEntities),\n\t\t//* Unique functions\n\t\tvalidators: INPUT_VALIDATORS,\n\t\tresolvers: OUTPUT_RESOLVERS\n\t};\n\n\t// TODO\n\t/** Get entity name */\n\tfunction _getNodeName(node: ts.Node, srcFile: ts.SourceFile): string {\n\t\treturn tsNodePrinter.printNode(ts.EmitHint.Unspecified, node, srcFile);\n\t}\n\t/** Remove Promise & Null from type */\n\tfunction _removePromiseAndNull(type: ts.Type): ts.Type[] {\n\t\tconst queue: ts.Type[] = [type];\n\t\tconst result: ts.Type[] = [];\n\t\twhile (queue.length > 0) {\n\t\t\tlet tp: ts.Type | undefined = queue.pop()!;\n\t\t\ttp = tp.getNonNullableType();\n\t\t\tif (tp.isUnionOrIntersection()) {\n\t\t\t\tfor (let i = 0, types = tp.types, len = types.length; i < len; ++i) {\n\t\t\t\t\tqueue.push(types[i]);\n\t\t\t\t}\n\t\t\t} else if (tp.symbol?.name === 'Promise') {\n\t\t\t\ttp = (tp as ts.TypeReference).typeArguments?.[0];\n\t\t\t\tif (tp == null) throw `Fail to get Promise argument at ${typeChecker.typeToString(type)}`;\n\t\t\t\tqueue.push(tp);\n\t\t\t} else if (!result.includes(tp)) {\n\t\t\t\tresult.push(tp);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Remove Promise and null and undefined from references */\n\tfunction _cleanReference(node: ts.TypeNode): ts.TypeNode | undefined {\n\t\tvar result: ts.TypeNode | undefined;\n\t\tif (node.kind === ts.SyntaxKind.UndefinedKeyword || node.kind === ts.SyntaxKind.NullKeyword) {\n\t\t\tresult = undefined;\n\t\t} else if (ts.isLiteralTypeNode(node)) {\n\t\t\tresult = node;\n\t\t} else if (ts.isArrayTypeNode(node)) {\n\t\t\tlet tp = _cleanReference(node.elementType);\n\t\t\tif (tp != null)\n\t\t\t\tresult = factory.createArrayTypeNode(tp);\n\t\t} else if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {\n\t\t\tlet types: ts.TypeNode[] = [];\n\t\t\tfor (let i = 0, nodeTypes = node.types, len = nodeTypes.length; i < len; ++i) {\n\t\t\t\tlet type = _cleanReference(nodeTypes[i]);\n\t\t\t\tif (type != null) types.push(type);\n\t\t\t}\n\t\t\tif (types.length > 0) {\n\t\t\t\tif (ts.isUnionTypeNode(node)) result = factory.createUnionTypeNode(types);\n\t\t\t\telse result = factory.createIntersectionTypeNode(types);\n\t\t\t}\n\t\t} else if (ts.isTypeReferenceNode(node) && node.typeArguments != null) {\n\t\t\tlet typeNameType = typeChecker.getTypeAtLocation(node.typeName);\n\t\t\tlet n: string | undefined;\n\t\t\tif (\n\t\t\t\tnode.typeArguments.length === 1 && (\n\t\t\t\t\t(n = (typeNameType.aliasSymbol ?? typeNameType.symbol)?.name) === 'Promise' ||\n\t\t\t\t\tn === 'Maybe' ||\n\t\t\t\t\tn === 'MaybeAsync'\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tresult = _cleanReference(node.typeArguments[0]);\n\t\t\telse result = node;\n\t\t} else if (_getNodeName(node, node.getSourceFile()) === 'null') {\n\t\t\tresult = undefined;\n\t\t} else {\n\t\t\tresult = node;\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Create Object entity if not exists */\n\tfunction _upObjectEntity(isInput: true, name: string, fileName: string, deprecated: string | undefined, jsDoc: string[] | undefined): InputObject;\n\tfunction _upObjectEntity(isInput: false | undefined, name: string, fileName: string, deprecated: string | undefined, jsDoc: string[] | undefined): OutputObject;\n\tfunction _upObjectEntity(isInput: boolean | undefined, name: string, fileName: string, deprecated: string | undefined, jsDoc: string[] | undefined): InputObject | OutputObject;\n\tfunction _upObjectEntity(isInput: boolean | undefined, name: string, fileName: string, deprecated: string | undefined, jsDoc: string[] | undefined): InputObject | OutputObject {\n\t\tconst targetMap = isInput ? INPUT_ENTITIES : OUTPUT_ENTITIES;\n\t\tlet entity = targetMap.get(name) as InputObject | OutputObject;\n\t\tif (entity == null) {\n\t\t\tentity = {\n\t\t\t\tkind: isInput ? Kind.INPUT_OBJECT : Kind.OUTPUT_OBJECT,\n\t\t\t\tname: name,\n\t\t\t\tescapedName: escapeEntityName(name),\n\t\t\t\tfields: new Map(),\n\t\t\t\tdeprecated: deprecated,\n\t\t\t\tfileNames: [fileName],\n\t\t\t\tinherit: undefined,\n\t\t\t\tjsDoc: jsDoc?.slice(0) ?? [],\n\t\t\t\twrappers: undefined,\n\t\t\t\tbefore: undefined,\n\t\t\t\tafter: undefined,\n\t\t\t\townedFieldsCount: 0,\n\t\t\t\torderByName: undefined,\n\t\t\t\tconvert: undefined\n\t\t\t};\n\t\t\t(targetMap as Map<string, InputObject | OutputObject>).set(name, entity);\n\t\t}\n\t\treturn entity;\n\t}\n\n\t/** Check entity exits */\n\tfunction _assertEntityNotFound(name: string, node: ts.Node, srcFile: ts.SourceFile) {\n\t\tlet ref: AllNodes | undefined;\n\t\tif (\n\t\t\t((ref = INPUT_ENTITIES.get(name)) && ref.kind != Kind.INPUT_OBJECT) ||\n\t\t\t((ref = OUTPUT_ENTITIES.get(name)) && ref.kind != Kind.OUTPUT_OBJECT)\n\t\t)\n\t\t\tthrow `Already defined entity ${name} at ${errorFile(srcFile, node)}. Other files: \\n\\t> ${ref.fileNames.join(\"\\n\\t> \")}`;\n\t}\n\t/** Generate union name from types */\n\tfunction _getUnionNameFromTypes(types: ts.Type[]): string {\n\t\tconst names = [];\n\t\tfor (let i = 0, len = types.length; i < len; ++i) {\n\t\t\tnames.push(typeChecker.typeToString(types[i]));\n\t\t}\n\t\tnames.sort((a, b) => a.localeCompare(b));\n\t\treturn names.join('|');\n\t}\n\t/** Check if method or function has a promise as return */\n\tfunction _hasPromise(node: ts.SignatureDeclaration | ts.ObjectLiteralElementLike | ts.FunctionExpression): boolean {\n\t\t// Get method\n\t\tif (ts.isObjectLiteralElementLike(node)) {\n\t\t\tif (ts.isPropertyAssignment(node)) {\n\t\t\t\tif (node.initializer != null && ts.isFunctionLike(node.initializer)) {\n\t\t\t\t\tnode = node.initializer;\n\t\t\t\t} else {\n\t\t\t\t\tthrow `\"${node.name.getText()} Expected method! at ${errorFile(node.getSourceFile(), node)}`;\n\t\t\t\t}\n\t\t\t} else if (!ts.isMethodDeclaration(node)) {\n\t\t\t\tthrow `Unexpected type \"${ts.SyntaxKind[node.kind]}\" for property \"${node.name?.getText()} at ${errorFile(node.getSourceFile(), node)}`\n\t\t\t}\n\t\t}\n\t\t// Get return type of signature\n\t\tvar sign = typeChecker.getSignatureFromDeclaration(node);\n\t\tif (sign == null) throw `Fail to get method signature at ${errorFile(node.getSourceFile(), node)}`\n\t\tvar returnType = typeChecker.getNonNullableType(typeChecker.getReturnTypeOfSignature(sign));\n\t\tlet hasPromise = false;\n\t\tif (returnType.isUnionOrIntersection()) {\n\t\t\tfor (let i = 0, types = returnType.types, len = types.length; i < len; ++i) {\n\t\t\t\tlet type = types[i];\n\t\t\t\tif (type.symbol?.name === 'Promise') {\n\t\t\t\t\thasPromise = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else hasPromise = returnType.symbol?.name === 'Promise';\n\t\treturn hasPromise;\n\t}\n\t/** Check if field is required */\n\tfunction _isRequired(nodeType: ts.Type): boolean {\n\t\t//* Basic check\n\t\tlet required = true;\n\t\tif (nodeType.flags & IS_OF_TYPE_NULL) required = false;\n\t\telse {\n\t\t\tlet t = typeChecker.getNullableType(nodeType, nodeType.flags);\n\t\t\tif (t.flags & IS_OF_TYPE_NULL) required = false;\n\t\t\telse if (t.isUnion()) {\n\t\t\t\trequired = t.types.every(tt => (tt.flags & IS_OF_TYPE_NULL) === 0);\n\t\t\t}\n\t\t}\n\t\t//* Check promises\n\t\tif (required && nodeType.isUnion()) {\n\t\t\tlet queue: ts.Type[] = [nodeType];\n\t\t\twhile (queue.length > 0) {\n\t\t\t\tlet type = queue.pop()!;\n\t\t\t\tif (type.flags & IS_OF_TYPE_NULL) { required = false; break; }\n\t\t\t\telse if (type.isUnion()) {\n\t\t\t\t\tfor (let i = 0, types = type.types, len = types.length; i < len; ++i) {\n\t\t\t\t\t\tqueue.push(types[i]);\n\t\t\t\t\t}\n\t\t\t\t} else if (type.symbol?.name === 'Promise') {\n\t\t\t\t\tlet tp = (type as ts.TypeReference).typeArguments?.[0];\n\t\t\t\t\tif (tp != null) queue.push(tp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn required;\n\t}\n}\n\n/** Compile assert expressions */\nfunction _compileAsserts(\n\tasserts: string[],\n\tprevAsserts: AssertOptions | undefined,\n\tsrcFile: ts.SourceFile,\n\tnode: ts.Node\n): AssertOptions | undefined {\n\ttry {\n\t\tif (asserts.length) {\n\t\t\tprevAsserts = Object.assign(\n\t\t\t\tprevAsserts ?? {},\n\t\t\t\t...asserts.map(e => _evaluateString(e))\n\t\t\t);\n\t\t}\n\t\treturn prevAsserts;\n\t} catch (err: any) {\n\t\tif (typeof err === 'string')\n\t\t\tthrow `Fail to parse @assert: ${err} At ${errorFile(srcFile, node)}`;\n\t\telse\n\t\t\tthrow `Fail to parse: @assert ${asserts.join('\\n')}: ${err?.message ?? err}\\nAt ${errorFile(srcFile, node)}`;\n\t}\n}\n\n// Assert keys\nconst ASSERT_KEYS_TMP: { [k in keyof AssertOptions]-?: 1 } = {\n\tmin: 1,\n\tmax: 1,\n\tlt: 1,\n\tgt: 1,\n\tlte: 1,\n\tgte: 1,\n\teq: 1,\n\tne: 1,\n\tlength: 1,\n\tregex: 1\n};\nconst ASSERT_KEYS = new Set(Object.keys(ASSERT_KEYS_TMP));\n\n/** Evaluate expression */\nfunction _evaluateString(str: string): Record<string, string> {\n\tlet obj = parseYaml(str);\n\tfor (let k in obj) {\n\t\tif (!ASSERT_KEYS.has(k)) {\n\t\t\tif (k.includes(':')) throw `Missing space after symbol \":\" on: \"${k}\"`;\n\t\t\tthrow `Unknown assert's key \"${k}\"`;\n\t\t}\n\t\tif (typeof obj[k] !== 'string') obj[k] = String(obj[k]);\n\t\t// let v = obj[k];\n\t\t// if (typeof v === 'number') { }\n\t\t// else if (typeof v === 'string')\n\t\t// \tobj[k] = _parseStringValue(v);\n\t\t// else throw 0;\n\t}\n\treturn obj;\n}\n\n// function _parseStringValue(v: string): number {\n// \tv = v.trim();\n// \tvar result: number;\n// \t// Check for bytes\n// \tlet b = /(.*?)([kmgtp]?b)$/i.exec(v);\n// \tif (b == null) {\n// \t\tresult = strMath(v)\n// \t} else {\n// \t\tresult = bytes(strMath(b[1]) + b[2]);\n// \t}\n// \treturn result;\n// }\n\n/** Merge helpers */\nfunction _mergeEntityHelpers<T extends InputObject | OutputObject>(entities: Map<string, T[]>) {\n\tconst result: Map<string, T> = new Map();\n\tentities.forEach((arr, name) => {\n\t\tconst obj = arr[0];\n\t\tobj.wrappers ??= [];\n\t\tfor (let i = 1, len = arr.length; i < len; ++i) {\n\t\t\tlet node = arr[i];\n\t\t\tobj.escapedName ??= node.escapedName;\n\t\t\t// Inheritance\n\t\t\tif (obj.inherit == null) obj.inherit = node.inherit;\n\t\t\telse if (node.inherit != null) obj.inherit.push(...node.inherit);\n\t\t\t// before & after\n\t\t\tif (node.wrappers != null) obj.wrappers.push(...node.wrappers);\n\t\t\t// Fields\n\t\t\tnode.fields.forEach((field, fieldName) => {\n\t\t\t\tlet objField = obj.fields.get(fieldName);\n\t\t\t\tif (objField == null) (obj.fields as Map<string, OutputField | InputField>).set(fieldName, field);\n\t\t\t\telse {\n\t\t\t\t\tobjField.alias ??= field.alias;\n\t\t\t\t\tobjField.defaultValue ??= field.defaultValue;\n\n\t\t\t\t\tif (objField.kind === Kind.INPUT_FIELD) {\n\t\t\t\t\t\tobjField.asserts ??= (field as InputField).asserts;\n\t\t\t\t\t\tif ((field as InputField).pipe.length) {\n\t\t\t\t\t\t\tobjField.pipe.push(...(field as InputField).pipe);\n\t\t\t\t\t\t\tobjField.type = field.type;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (objField.method == null) {\n\t\t\t\t\t\t\tobjField.method = (field as OutputField).method;\n\t\t\t\t\t\t\tobjField.type = field.type;\n\t\t\t\t\t\t\tobjField.param = (field as OutputField).param;\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\t\tresult.set(name, obj);\n\t});\n\treturn result;\n}\n\n/** Escape entity name */\nexport function escapeEntityName(name: string) {\n\treturn name.replace(/^\\W+|\\W+$/g, '').replace(/\\W+/g, '_').replace(/_{2,}/g, '_');\n}\n\n/** Adjust inheritance list */\nfunction _adjustInheritance(entity: InputObject | OutputObject, mp: Map<string, InputNode | OutputObject>) {\n\tif (entity.inherit != null) {\n\t\tfor (let i = 0, lst = entity.inherit; i < lst.length; i++) {\n\t\t\tlet clz = lst[i];\n\t\t\tlet e = mp.get(clz);\n\t\t\tlet l = (e as InputObject | undefined)?.inherit;\n\t\t\tif (l != null) {\n\t\t\t\tfor (let j = 0, len = l.length; j < len; ++j) {\n\t\t\t\t\tlet c = l[j];\n\t\t\t\t\tif (lst.includes(c) === false) lst.push(c);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n/** Remove \"null\" and \"undefined\" */\nfunction _rmNull(type: ts.TypeNode | undefined): ts.TypeNode | undefined {\n\tif (type != null && ts.isUnionTypeNode(type)) {\n\t\tlet result: ts.TypeNode | undefined;\n\t\tfor (let i = 0, types = type.types, len = types.length; i < len; ++i) {\n\t\t\tlet tp = types[i];\n\t\t\tlet tpN = tp.getText();\n\t\t\tif (tpN !== 'undefined' && tpN !== 'null') {\n\t\t\t\tif (result == null) result = tp;\n\t\t\t\telse throw `Expected only one type for node: < ${type.getText()} > at ${errorFile(type.getSourceFile(), type)}`\n\t\t\t}\n\t\t}\n\t\ttype = result;\n\t}\n\treturn type;\n}\n\n/** Get entity qualified name (includes namespace) */\nfunction _getEntityQualifiedName(node: ts.Node, entityName: string): string {\n\tif (node.parent.kind === ts.SyntaxKind.SourceFile) return entityName;\n\telse {\n\t\tlet n: string[] = [entityName];\n\t\tlet p: ts.Node = node;\n\t\twhile (true) {\n\t\t\tp = p.parent;\n\t\t\tif (p.kind === ts.SyntaxKind.ModuleBlock) { }\n\t\t\telse if (ts.isModuleDeclaration(p) && p.name != null) {\n\t\t\t\tn.push(p.name.getText());\n\t\t\t} else break;\n\t\t}\n\t\treturn n.reverse().join('.');\n\t}\n}"]}