UNPKG

@omnigraph/soap

Version:
1,304 lines 71.6 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SOAPLoader = void 0;
const fast_xml_parser_1 = require("fast-xml-parser");
const graphql_1 = require("graphql");
const graphql_compose_1 = require("graphql-compose");
const graphql_scalars_1 = require("graphql-scalars");
const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
const transport_common_1 = require("@graphql-mesh/transport-common");
const utils_1 = require("@graphql-mesh/utils");
const fetch_1 = require("@whatwg-node/fetch");
const utils_js_1 = require("./utils.js");
const SOAPHeadersInput = new graphql_1.GraphQLInputObjectType({
    name: 'SOAPHeaders',
    fields: {
        namespace: {
            type: graphql_1.GraphQLString,
        },
        alias: {
            type: graphql_1.GraphQLString,
        },
        headers: {
            type: transport_common_1.ObjMapScalar,
        },
    },
});
const soapDirective = new graphql_1.GraphQLDirective({
    name: 'soap',
    locations: [graphql_1.DirectiveLocation.FIELD_DEFINITION],
    args: {
        elementName: {
            type: graphql_1.GraphQLString,
        },
        bindingNamespace: {
            type: graphql_1.GraphQLString,
        },
        endpoint: {
            type: graphql_1.GraphQLString,
        },
        subgraph: {
            type: graphql_1.GraphQLString,
        },
        bodyAlias: {
            type: graphql_1.GraphQLString,
        },
        soapHeaders: {
            type: SOAPHeadersInput,
        },
        soapAction: {
            type: graphql_1.GraphQLString,
        },
        soapNamespace: {
            type: graphql_1.GraphQLString,
        },
        headerArgNames: {
            type: new graphql_1.GraphQLList(graphql_1.GraphQLString),
        },
        argNamespaces: {
            type: transport_common_1.ObjMapScalar,
        },
        typeNamespaces: {
            type: transport_common_1.ObjMapScalar,
        },
    },
});
const QUERY_PREFIXES = [
    'get',
    'find',
    'list',
    'search',
    'count',
    'exists',
    'fetch',
    'load',
    'query',
    'select',
];
function isQueryOperationName(operationName) {
    return QUERY_PREFIXES.some(prefix => operationName.toLowerCase().startsWith(prefix));
}
// Matches any RFC-3986 scheme (2+ chars to avoid mistaking Windows drive
// letters like "C:" for URI schemes). The scheme isn't required to be
// followed by "//" — `file:/tmp/x`, `urn:isbn:1234`, etc. are valid
// absolute URIs.
const URI_SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+.-]+:/;
/**
 * Resolve a (possibly relative) WSDL/XSD location against the location of the
 * document doing the import. Mirrors XML Schema's xml:base resolution rules:
 * relative `schemaLocation` / `wsdl:import location` is resolved against the
 * importing document's own URI, not against a fixed root.
 *
 * Handles both URIs (http, https, file, urn, …) and filesystem paths uniformly.
 */
function resolveLocation(base, location) {
    // Already-absolute URIs (any scheme) pass through.
    if (URI_SCHEME_RE.test(location))
        return location;
    if (!base)
        return location;
    if (URI_SCHEME_RE.test(base)) {
        // base is a URI — let URL handle "../" and root-relative ("/foo") paths.
        return new URL(location, base).href;
    }
    // base is a filesystem path. A filesystem-absolute location passes through;
    // anything else is resolved relative to base's containing directory.
    if (cross_helpers_1.path.isAbsolute(location))
        return location;
    return cross_helpers_1.path.resolve(cross_helpers_1.path.dirname(base), location);
}
/**
 * Convert a namespace URI to a GraphQL-safe identifier slug.
 * Uses a single linear pass to avoid polynomial regex backtracking on
 * user-controlled input (CodeQL js/polynomial-redos).
 */
function namespaceToSlug(namespace) {
    // Determine start index after optional protocol prefix
    let start = 0;
    if (namespace.startsWith('https://')) {
        start = 8;
    }
    else if (namespace.startsWith('http://')) {
        start = 7;
    }
    // Build slug in one pass:
    // - keep alphanumeric characters as-is
    // - collapse any run of non-alphanumeric characters into a single '_'
    // - skip leading and trailing '_' (pendingUnderscore is only flushed when
    //   a subsequent alphanumeric character is found)
    // Use an array of chunks to avoid O(n^2) repeated string concatenation.
    const chunks = [];
    let pendingUnderscore = false;
    for (let i = start; i < namespace.length; i++) {
        const code = namespace.charCodeAt(i);
        if ((code >= 0x30 && code <= 0x39) || // 0-9
            (code >= 0x41 && code <= 0x5a) || // A-Z
            (code >= 0x61 && code <= 0x7a) // a-z
        ) {
            if (pendingUnderscore && chunks.length > 0) {
                chunks.push('_');
            }
            pendingUnderscore = false;
            chunks.push(namespace[i]);
        }
        else {
            pendingUnderscore = true;
        }
    }
    // If nothing was accumulated (e.g. namespace is symbols-only or empty after
    // stripping the protocol prefix), return a safe sentinel so callers always
    // receive a non-empty, GraphQL-Name-compatible string.
    if (chunks.length === 0) {
        return '_';
    }
    // Prefix a leading digit so the result is a valid GraphQL identifier
    const firstCode = chunks[0].charCodeAt(0);
    if (firstCode >= 0x30 && firstCode <= 0x39) {
        chunks.unshift('_');
    }
    return chunks.join('');
}
class SOAPLoader {
    constructor(options) {
        this.schemaComposer = new graphql_compose_1.SchemaComposer();
        this.namespaceDefinitionsMap = new Map();
        this.namespaceComplexTypesMap = new Map();
        this.namespaceSimpleTypesMap = new Map();
        this.namespacePortTypesMap = new Map();
        this.namespaceBindingMap = new Map();
        this.namespaceMessageMap = new Map();
        this.aliasMap = new WeakMap();
        this.messageOutputTCMap = new WeakMap();
        this.complexTypeInputTCMap = new WeakMap();
        this.complexTypeOutputTCMap = new WeakMap();
        this.simpleTypeTCMap = new WeakMap();
        this.namespaceTypePrefixMap = new Map();
        /** Maps GraphQL input type name → the XSD namespace URI where that type is defined. */
        this.typeNamespaceMap = new Map();
        this.namespaceElementRefMap = new Map();
        this.loadedLocations = new Map();
        this.xmlParser = new fast_xml_parser_1.XMLParser(utils_js_1.PARSE_XML_OPTIONS);
        this.fetchFn = options.fetch || fetch_1.fetch;
        this.logger = options.logger || new utils_1.DefaultLogger(options.subgraphName);
        this.subgraphName = options.subgraphName;
        this.loadXMLSchemaNamespace();
        this.schemaComposer.addDirective(soapDirective);
        this.schemaHeadersFactory = (0, string_interpolation_1.getInterpolatedHeadersFactory)(options.schemaHeaders || {});
        this.endpoint = options.endpoint;
        this.cwd = options.cwd;
        this.soapHeaders = options.soapHeaders;
        this.bodyAlias = options.bodyAlias;
        this.soapNamespace = 'http://schemas.xmlsoap.org/soap/envelope/';
    }
    loadXMLSchemaNamespace() {
        const namespace = 'http://www.w3.org/2001/XMLSchema';
        const simpleTypeGraphQLScalarMap = new Map([
            ['anyType', graphql_compose_1.GraphQLJSON],
            ['anySimpleType', graphql_1.GraphQLString],
            ['anyURI', graphql_scalars_1.GraphQLURL],
            ['base64Binary', graphql_scalars_1.GraphQLByte],
            ['byte', graphql_scalars_1.GraphQLByte],
            ['boolean', graphql_1.GraphQLBoolean],
            ['date', graphql_scalars_1.GraphQLDate],
            ['dateTime', graphql_scalars_1.GraphQLDateTime],
            ['decimal', graphql_1.GraphQLFloat],
            ['double', graphql_1.GraphQLFloat],
            ['duration', graphql_scalars_1.GraphQLDuration],
            ['float', graphql_1.GraphQLFloat],
            ['int', graphql_1.GraphQLInt],
            ['integer', graphql_1.GraphQLInt],
            ['negativeInteger', graphql_scalars_1.GraphQLNegativeInt],
            ['nonNegativeInteger', graphql_scalars_1.GraphQLNonNegativeInt],
            ['nonPositiveInteger', graphql_scalars_1.GraphQLNonPositiveInt],
            ['positiveInteger', graphql_scalars_1.GraphQLPositiveInt],
            ['hexBinary', graphql_scalars_1.GraphQLHexadecimal],
            ['long', graphql_scalars_1.GraphQLBigInt],
            ['gDay', graphql_1.GraphQLString],
            ['gMonth', graphql_1.GraphQLString],
            ['gMonthDay', graphql_1.GraphQLString],
            ['gYear', graphql_1.GraphQLString],
            ['gYearMonth', graphql_1.GraphQLString],
            ['NOTATION', graphql_1.GraphQLString],
            ['QName', graphql_1.GraphQLString],
            ['short', graphql_1.GraphQLInt],
            ['string', graphql_1.GraphQLString],
            ['unsignedByte', graphql_scalars_1.GraphQLByte],
            ['unsignedInt', graphql_scalars_1.GraphQLUnsignedInt],
            ['unsignedLong', graphql_scalars_1.GraphQLBigInt],
            ['unsignedShort', graphql_scalars_1.GraphQLUnsignedInt],
            ['time', graphql_scalars_1.GraphQLTime],
        ]);
        const namespaceSimpleTypesMap = this.getNamespaceSimpleTypeMap(namespace);
        for (const [singleTypeName, scalarType] of simpleTypeGraphQLScalarMap) {
            const singleType = {
                attributes: {
                    name: singleTypeName,
                },
            };
            namespaceSimpleTypesMap.set(singleTypeName, singleType);
            const simpleTypeTC = this.schemaComposer.createScalarTC(scalarType);
            this.simpleTypeTCMap.set(singleType, simpleTypeTC);
        }
    }
    getNamespaceDefinitions(namespace) {
        let namespaceDefinitions = this.namespaceDefinitionsMap.get(namespace);
        if (!namespaceDefinitions) {
            namespaceDefinitions = [];
            this.namespaceDefinitionsMap.set(namespace, namespaceDefinitions);
        }
        return namespaceDefinitions;
    }
    getNamespaceComplexTypeMap(namespace) {
        let namespaceComplexTypes = this.namespaceComplexTypesMap.get(namespace);
        if (!namespaceComplexTypes) {
            namespaceComplexTypes = new Map();
            this.namespaceComplexTypesMap.set(namespace, namespaceComplexTypes);
        }
        return namespaceComplexTypes;
    }
    getNamespaceSimpleTypeMap(namespace) {
        let namespaceSimpleTypes = this.namespaceSimpleTypesMap.get(namespace);
        if (!namespaceSimpleTypes) {
            namespaceSimpleTypes = new Map();
            this.namespaceSimpleTypesMap.set(namespace, namespaceSimpleTypes);
        }
        return namespaceSimpleTypes;
    }
    getNamespacePortTypeMap(namespace) {
        let namespacePortTypes = this.namespacePortTypesMap.get(namespace);
        if (!namespacePortTypes) {
            namespacePortTypes = new Map();
            this.namespacePortTypesMap.set(namespace, namespacePortTypes);
        }
        return namespacePortTypes;
    }
    getNamespaceBindingMap(namespace) {
        let namespaceBindingMap = this.namespaceBindingMap.get(namespace);
        if (!namespaceBindingMap) {
            namespaceBindingMap = new Map();
            this.namespaceBindingMap.set(namespace, namespaceBindingMap);
        }
        return namespaceBindingMap;
    }
    getNamespaceMessageMap(namespace) {
        let namespaceMessageMap = this.namespaceMessageMap.get(namespace);
        if (!namespaceMessageMap) {
            namespaceMessageMap = new Map();
            this.namespaceMessageMap.set(namespace, namespaceMessageMap);
        }
        return namespaceMessageMap;
    }
    async loadSchema(schemaObj, parentAliasMap = new Map(), baseUrl) {
        // A bare <xsd:schema> wrapper that only contains imports (no targetNamespace,
        // no type definitions) is a valid pattern — it lets a WSDL pull external
        // schemas into its <wsdl:types> without redefining anything itself. Process
        // its imports here. A "chameleon" schema (no targetNamespace + own type
        // definitions) is a different, larger case the loader doesn't yet support;
        // surface it loudly rather than silently dropping the definitions.
        if (!schemaObj.attributes || !schemaObj.attributes.targetNamespace) {
            if (schemaObj.import) {
                for (const importObj of schemaObj.import) {
                    const importLocation = importObj.attributes.schemaLocation;
                    if (importLocation) {
                        await this.fetchXSD(importLocation, parentAliasMap, baseUrl);
                    }
                }
            }
            if (schemaObj.complexType || schemaObj.simpleType || schemaObj.element) {
                throw new Error('<xsd:schema> without targetNamespace but with type definitions ' +
                    '(chameleon schemas) is not yet supported by the SOAP loader.');
            }
            return;
        }
        const schemaNamespace = schemaObj.attributes.targetNamespace;
        const aliasMap = this.getAliasMapFromAttributes(schemaObj.attributes);
        let typePrefix = this.namespaceTypePrefixMap.get(schemaNamespace);
        if (!typePrefix) {
            typePrefix =
                schemaObj.attributes.id ||
                    [...aliasMap.entries()].find(([, namespace]) => namespace === schemaNamespace)?.[0];
            this.namespaceTypePrefixMap.set(schemaNamespace, typePrefix);
        }
        for (const [alias, namespace] of parentAliasMap) {
            if (!aliasMap.has(alias)) {
                aliasMap.set(alias, namespace);
            }
        }
        if (schemaObj.import) {
            for (const importObj of schemaObj.import) {
                const importLocation = importObj.attributes.schemaLocation;
                if (importLocation) {
                    await this.fetchXSD(importLocation, parentAliasMap, baseUrl);
                }
            }
        }
        // Complex and simple types can be inside element tag or outside of it
        if (schemaObj.complexType) {
            const namespaceComplexTypes = this.getNamespaceComplexTypeMap(schemaNamespace);
            for (const complexType of schemaObj.complexType) {
                namespaceComplexTypes.set(complexType.attributes.name, complexType);
                this.aliasMap.set(complexType, aliasMap);
            }
        }
        if (schemaObj.simpleType) {
            const namespaceSimpleTypes = this.getNamespaceSimpleTypeMap(schemaNamespace);
            for (const simpleType of schemaObj.simpleType) {
                namespaceSimpleTypes.set(simpleType.attributes.name, simpleType);
                this.aliasMap.set(simpleType, aliasMap);
            }
        }
        if (schemaObj.element) {
            for (const elementObj of schemaObj.element) {
                if (elementObj.complexType) {
                    const namespaceComplexTypes = this.getNamespaceComplexTypeMap(schemaNamespace);
                    for (const complexType of elementObj.complexType) {
                        // Sometimes type name is defined on element object
                        complexType.attributes = complexType.attributes || {};
                        complexType.attributes.name = elementObj.attributes.name;
                        namespaceComplexTypes.set(complexType.attributes.name, complexType);
                        this.aliasMap.set(complexType, aliasMap);
                    }
                }
                if (elementObj.simpleType) {
                    const namespaceSimpleTypes = this.getNamespaceSimpleTypeMap(schemaNamespace);
                    for (const simpleType of elementObj.simpleType) {
                        simpleType.attributes = simpleType.attributes || {};
                        simpleType.attributes.name = elementObj.attributes.name;
                        namespaceSimpleTypes.set(simpleType.attributes.name, simpleType);
                        this.aliasMap.set(simpleType, aliasMap);
                    }
                }
                if (elementObj.attributes?.type) {
                    const [refTypeNamespaceAlias, refTypeName] = elementObj.attributes.type.split(':');
                    const refTypeNamespace = aliasMap.get(refTypeNamespaceAlias);
                    if (!refTypeNamespace) {
                        throw new Error(`Invalid namespace alias: ${refTypeNamespaceAlias}`);
                    }
                    const refComplexType = this.getNamespaceComplexTypeMap(refTypeNamespace).get(refTypeName);
                    if (refComplexType) {
                        this.getNamespaceComplexTypeMap(schemaNamespace).set(elementObj.attributes.name, refComplexType);
                    }
                    const refSimpleType = this.getNamespaceSimpleTypeMap(refTypeNamespace).get(refTypeName);
                    if (refSimpleType) {
                        this.getNamespaceSimpleTypeMap(schemaNamespace).set(elementObj.attributes.name, refSimpleType);
                    }
                    if (!refComplexType && !refSimpleType) {
                        // Forward reference: referenced type not yet loaded. Store for lazy lookup.
                        let elementRefs = this.namespaceElementRefMap.get(schemaNamespace);
                        if (!elementRefs) {
                            elementRefs = new Map();
                            this.namespaceElementRefMap.set(schemaNamespace, elementRefs);
                        }
                        elementRefs.set(elementObj.attributes.name, {
                            typeName: refTypeName,
                            typeNamespace: refTypeNamespace,
                        });
                    }
                }
            }
        }
    }
    async loadDefinition(definition, baseUrl) {
        this.getNamespaceDefinitions(definition.attributes.targetNamespace).push(definition);
        if (definition.attributes.soap12) {
            this.soapNamespace = 'http://www.w3.org/2003/05/soap-envelope';
        }
        const definitionAliasMap = this.getAliasMapFromAttributes(definition.attributes);
        const definitionNamespace = definition.attributes.targetNamespace;
        const typePrefix = definition.attributes.name ||
            [...definitionAliasMap.entries()].find(([, namespace]) => namespace === definitionNamespace)?.[0] ||
            '';
        this.namespaceTypePrefixMap.set(definition.attributes.targetNamespace, typePrefix);
        if (definition.import) {
            for (const importObj of definition.import) {
                const importLocation = importObj.attributes.location;
                if (importLocation) {
                    await this.fetchWSDL(importLocation, baseUrl);
                }
            }
        }
        if (definition.types) {
            for (const typesObj of definition.types) {
                for (const schemaObj of typesObj.schema) {
                    await this.loadSchema(schemaObj, definitionAliasMap, baseUrl);
                }
            }
        }
        if (definition.portType) {
            const namespacePortTypes = this.getNamespacePortTypeMap(definition.attributes.targetNamespace);
            for (const portTypeObj of definition.portType) {
                namespacePortTypes.set(portTypeObj.attributes.name, portTypeObj);
                this.aliasMap.set(portTypeObj, definitionAliasMap);
            }
        }
        if (definition.binding) {
            const namespaceBindingMap = this.getNamespaceBindingMap(definition.attributes.targetNamespace);
            for (const bindingObj of definition.binding) {
                namespaceBindingMap.set(bindingObj.attributes.name, bindingObj);
                this.aliasMap.set(bindingObj, definitionAliasMap);
            }
        }
        if (definition.message) {
            const namespaceMessageMap = this.getNamespaceMessageMap(definition.attributes.targetNamespace);
            for (const messageObj of definition.message) {
                namespaceMessageMap.set(messageObj.attributes.name, messageObj);
                this.aliasMap.set(messageObj, definitionAliasMap);
            }
        }
        const serviceAndPortAliasMap = this.getAliasMapFromAttributes(definition.attributes);
        if (definition.service) {
            for (const serviceObj of definition.service) {
                const serviceName = serviceObj.attributes.name;
                for (const portObj of serviceObj.port) {
                    const portName = portObj.attributes.name;
                    const [bindingNamespaceAlias, bindingName] = portObj.attributes.binding.split(':');
                    const bindingNamespace = serviceAndPortAliasMap.get(bindingNamespaceAlias);
                    if (!bindingNamespace) {
                        throw new Error(`Namespace alias: ${bindingNamespaceAlias} is undefined!`);
                    }
                    const bindingObj = this.getNamespaceBindingMap(bindingNamespace).get(bindingName);
                    if (!bindingObj) {
                        throw new Error(`Binding: ${bindingName} is not defined in ${bindingNamespace} needed for ${serviceName}->${portName}`);
                    }
                    const bindingAliasMap = this.aliasMap.get(bindingObj);
                    if (!bindingAliasMap) {
                        throw new Error(`Namespace alias definitions couldn't be found for ${bindingName}`);
                    }
                    const [portTypeNamespaceAlias, portTypeName] = bindingObj.attributes.type.split(':');
                    const portTypeNamespace = bindingAliasMap.get(portTypeNamespaceAlias);
                    if (!portTypeNamespace) {
                        throw new Error(`Namespace alias: ${portTypeNamespaceAlias} is undefined!`);
                    }
                    const portTypeObj = this.getNamespacePortTypeMap(portTypeNamespace).get(portTypeName);
                    if (!portTypeObj) {
                        throw new Error(`Port Type: ${portTypeName} is not defined in ${portTypeNamespace} needed for ${bindingNamespaceAlias}->${bindingName}`);
                    }
                    const portTypeAliasMap = this.aliasMap.get(portTypeObj);
                    for (const operationObj of portTypeObj.operation) {
                        const operationName = operationObj.attributes.name;
                        const rootTC = isQueryOperationName(operationName)
                            ? this.schemaComposer.Query
                            : this.schemaComposer.Mutation;
                        const operationFieldName = (0, utils_1.sanitizeNameForGraphQL)(`${typePrefix}_${serviceName}_${portName}_${operationName}`);
                        const outputObj = operationObj.output[0];
                        const [messageNamespaceAlias, messageName] = outputObj.attributes.message.split(':');
                        const messageNamespace = portTypeAliasMap.get(messageNamespaceAlias);
                        if (!messageNamespace) {
                            throw new Error(`Namespace alias: ${messageNamespaceAlias} is undefined!`);
                        }
                        const { type, elementName } = this.getOutputTypeForMessage(this.getNamespaceMessageMap(messageNamespace).get(messageName));
                        const bindingOperationObject = bindingObj.operation.find(operation => operation.attributes.name === operationName);
                        const soapAnnotations = {
                            elementName,
                            bindingNamespace,
                            endpoint: this.endpoint,
                            subgraph: this.subgraphName,
                            soapNamespace: this.soapNamespace,
                        };
                        if (!soapAnnotations.endpoint && portObj.address) {
                            for (const address of portObj.address) {
                                if (address.attributes) {
                                    for (const attributeName in address.attributes) {
                                        const value = address.attributes[attributeName];
                                        if (value && attributeName.toLowerCase().endsWith('location')) {
                                            soapAnnotations.endpoint = value;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (bindingOperationObject?.operation) {
                            for (const bindingOperationObjectElem of bindingOperationObject.operation) {
                                if (bindingOperationObjectElem.attributes) {
                                    for (const attributeName in bindingOperationObjectElem.attributes) {
                                        const value = bindingOperationObjectElem.attributes[attributeName];
                                        if (value && attributeName.toLowerCase().endsWith('action')) {
                                            soapAnnotations.soapAction = value;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (this.bodyAlias) {
                            soapAnnotations.bodyAlias = this.bodyAlias;
                        }
                        if (this.soapHeaders) {
                            soapAnnotations.soapHeaders = this.soapHeaders;
                        }
                        rootTC.addFields({
                            [operationFieldName]: {
                                type,
                                directives: [
                                    {
                                        name: 'soap',
                                        args: soapAnnotations,
                                    },
                                ],
                            },
                        });
                        const inputObj = operationObj.input[0];
                        const [inputMessageNamespaceAlias, inputMessageName] = inputObj.attributes.message.split(':');
                        const inputMessageNamespace = portTypeAliasMap.get(inputMessageNamespaceAlias);
                        if (!inputMessageNamespace) {
                            throw new Error(`Namespace alias: ${inputMessageNamespaceAlias} is undefined!`);
                        }
                        const inputMessageObj = this.getNamespaceMessageMap(inputMessageNamespace).get(inputMessageName);
                        if (!inputMessageObj) {
                            throw new Error(`Message: ${inputMessageName} is not defined in ${inputMessageNamespace} needed for ${portTypeName}->${operationName}`);
                        }
                        const aliasMap = this.aliasMap.get(inputMessageObj);
                        // Collect which message part names the binding routes to soap:Header
                        const headerPartNames = new Set();
                        for (const inputBinding of bindingOperationObject?.input ?? []) {
                            for (const headerElem of inputBinding.header ?? []) {
                                if (headerElem.attributes?.part) {
                                    headerPartNames.add(headerElem.attributes.part);
                                }
                            }
                        }
                        // Build per-arg namespace map and header arg list alongside the existing arg registration
                        const argNamespaceMap = {};
                        const headerArgNames = [];
                        for (const part of inputMessageObj.part) {
                            if (part.attributes.element) {
                                const [elementNamespaceAlias, elementName] = part.attributes.element.split(':');
                                const argName = (0, utils_1.sanitizeNameForGraphQL)(elementName);
                                const elementNs = aliasMap.get(elementNamespaceAlias) ||
                                    part.attributes[elementNamespaceAlias];
                                if (elementNs) {
                                    argNamespaceMap[argName] = elementNs;
                                }
                                if (headerPartNames.has(part.attributes.name || elementName)) {
                                    headerArgNames.push(argName);
                                }
                                rootTC.addFieldArgs(operationFieldName, {
                                    [argName]: {
                                        type: () => {
                                            const elementNamespace = aliasMap.get(elementNamespaceAlias) ||
                                                part.attributes[elementNamespaceAlias];
                                            if (!elementNamespace) {
                                                throw new Error(`Namespace alias: ${elementNamespaceAlias} is not defined.`);
                                            }
                                            return this.getInputTypeForTypeNameInNamespace({
                                                typeName: elementName,
                                                typeNamespace: elementNamespace,
                                            });
                                        },
                                        defaultValue: '',
                                    },
                                });
                            }
                            else if (part.attributes.name) {
                                const partName = part.attributes.name;
                                const argName = (0, utils_1.sanitizeNameForGraphQL)(partName);
                                // For RPC-style <wsdl:part name="X" type="ns:T"/>, the element
                                // wrapper QName is the part name in the binding's namespace —
                                // the type's namespace describes the value type, not the
                                // accessor element. Always use bindingNamespace here, never
                                // part@type's namespace.
                                if (bindingNamespace) {
                                    argNamespaceMap[argName] = bindingNamespace;
                                }
                                if (headerPartNames.has(partName)) {
                                    headerArgNames.push(argName);
                                }
                                rootTC.addFieldArgs(operationFieldName, {
                                    [argName]: {
                                        type: () => {
                                            const typeRef = part.attributes.type;
                                            const [typeNamespaceAlias, typeName] = typeRef.split(':');
                                            const typeNamespace = aliasMap.get(typeNamespaceAlias);
                                            if (!typeNamespace) {
                                                throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                            }
                                            const inputTC = this.getInputTypeForTypeNameInNamespace({
                                                typeName,
                                                typeNamespace,
                                            });
                                            if ('getFields' in inputTC && Object.keys(inputTC.getFields()).length === 0) {
                                                return graphql_compose_1.GraphQLJSON;
                                            }
                                            return inputTC;
                                        },
                                        defaultValue: '',
                                    },
                                });
                            }
                        }
                        if (headerArgNames.length > 0) {
                            soapAnnotations.headerArgNames = headerArgNames;
                        }
                        if (Object.keys(argNamespaceMap).length > 0) {
                            soapAnnotations.argNamespaces = argNamespaceMap;
                        }
                    }
                }
            }
        }
    }
    async fetchXSD(location, parentAliasMap = new Map(), baseUrl) {
        const resolved = resolveLocation(baseUrl ?? this.cwd, location);
        if (this.loadedLocations.has(resolved))
            return;
        let xsdText = await (0, utils_1.readFileOrUrl)(resolved, {
            allowUnknownExtensions: true,
            cwd: this.cwd,
            fetch: this.fetchFn,
            importFn: utils_1.defaultImportFn,
            logger: this.logger,
            headers: this.schemaHeadersFactory({ env: cross_helpers_1.process.env }),
        });
        xsdText = xsdText.split('xmlns:').join('namespace:');
        // WSDL Import is different than XS Import
        const xsdObj = this.xmlParser.parse(xsdText, utils_js_1.PARSE_XML_OPTIONS);
        // Pre-register before recursing so circular imports terminate.
        this.loadedLocations.set(resolved, xsdObj);
        for (const schemaObj of xsdObj.schema) {
            await this.loadSchema(schemaObj, parentAliasMap, resolved);
        }
    }
    async loadWSDL(wsdlText, baseUrl) {
        wsdlText = wsdlText.split('xmlns:').join('namespace:');
        let wsdlObject;
        try {
            wsdlObject = this.xmlParser.parse(wsdlText, utils_js_1.PARSE_XML_OPTIONS);
        }
        catch (e) {
            throw new Error(`Failed to parse WSDL: ${e.message}. \nReturned response;\n${wsdlText}`);
        }
        if (!Array.isArray(wsdlObject.definitions)) {
            throw new Error(`WSDL definitions not found! Please make sure if your WSDL source is correct, and it contains <definitions> tag.\nReturned response;\n${wsdlText}`);
        }
        // Anchor a relative filesystem baseUrl against this.cwd. Without this, a
        // caller passing a relative source (e.g. "./wsdl/foo.wsdl") would have
        // path.resolve fall back to process.cwd() — which can differ from the
        // loader's working directory in monorepos / containers, sending nested
        // imports to the wrong absolute paths.
        let resolvedBase = baseUrl;
        if (resolvedBase &&
            !URI_SCHEME_RE.test(resolvedBase) &&
            !cross_helpers_1.path.isAbsolute(resolvedBase) &&
            this.cwd) {
            resolvedBase = cross_helpers_1.path.resolve(this.cwd, resolvedBase);
        }
        // Pre-register before recursing so circular imports terminate.
        if (resolvedBase) {
            this.loadedLocations.set(resolvedBase, wsdlObject);
        }
        for (const definition of wsdlObject.definitions) {
            await this.loadDefinition(definition, resolvedBase);
        }
        return wsdlObject;
    }
    async fetchWSDL(location, baseUrl) {
        const resolved = resolveLocation(baseUrl ?? this.cwd, location);
        if (this.loadedLocations.has(resolved))
            return;
        const wsdlText = await (0, utils_1.readFileOrUrl)(resolved, {
            allowUnknownExtensions: true,
            cwd: this.cwd,
            fetch: this.fetchFn,
            importFn: utils_1.defaultImportFn,
            logger: this.logger,
            headers: this.schemaHeadersFactory({ env: cross_helpers_1.process.env }),
        });
        await this.loadWSDL(wsdlText, resolved);
    }
    getAliasMapFromAttributes(attributes) {
        const aliasMap = new Map();
        for (const attributeName in attributes) {
            const attributeValue = attributes[attributeName];
            if (attributeName !== 'targetNamespace') {
                aliasMap.set(attributeName, attributeValue);
            }
        }
        return aliasMap;
    }
    getTypeForSimpleType(simpleType, simpleTypeNamespace) {
        let simpleTypeTC = this.simpleTypeTCMap.get(simpleType);
        if (!simpleTypeTC) {
            const simpleTypeName = (0, utils_1.sanitizeNameForGraphQL)(simpleType.attributes.name);
            const restrictionObj = simpleType.restriction[0];
            const prefix = this.namespaceTypePrefixMap.get(simpleTypeNamespace);
            if (restrictionObj.enumeration) {
                const enumTypeName = `${prefix}_${simpleTypeName}`;
                const values = {};
                for (const enumerationObj of restrictionObj.enumeration) {
                    const enumValue = enumerationObj.attributes.value;
                    const enumKey = (0, utils_1.sanitizeNameForGraphQL)(enumValue);
                    values[enumKey] = {
                        value: enumValue,
                    };
                }
                simpleTypeTC = this.schemaComposer.createEnumTC({
                    name: enumTypeName,
                    values,
                });
            }
            else if (restrictionObj.pattern) {
                const patternObj = restrictionObj.pattern[0];
                const pattern = patternObj.attributes.value;
                const scalarTypeName = `${prefix}_${simpleTypeName}`;
                simpleTypeTC = this.schemaComposer.createScalarTC(new graphql_scalars_1.RegularExpression(scalarTypeName, new RegExp(pattern)));
            }
            else {
                // TODO: Other restrictions are not supported yet
                const aliasMap = this.aliasMap.get(simpleType);
                const [baseTypeNamespaceAlias, baseTypeName] = restrictionObj.attributes.base.split(':');
                const baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias);
                if (!baseTypeNamespace) {
                    throw new Error(`Invalid base type namespace: ${baseTypeNamespaceAlias}`);
                }
                const baseType = this.getNamespaceSimpleTypeMap(baseTypeNamespace)?.get(baseTypeName);
                if (!baseType) {
                    throw new Error(`Simple Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${simpleTypeName}`);
                }
                simpleTypeTC = this.getTypeForSimpleType(baseType, baseTypeNamespace);
            }
            this.simpleTypeTCMap.set(simpleType, simpleTypeTC);
        }
        return simpleTypeTC;
    }
    getInputTypeForTypeNameInNamespace({ typeName, typeNamespace, }) {
        // Check element aliases first — consistent with the eager path that overwrites the type map
        const elementRef = this.namespaceElementRefMap.get(typeNamespace)?.get(typeName);
        if (elementRef) {
            return this.getInputTypeForTypeNameInNamespace(elementRef);
        }
        const complexType = this.getNamespaceComplexTypeMap(typeNamespace)?.get(typeName);
        if (complexType) {
            return this.getInputTypeForComplexType(complexType, typeNamespace);
        }
        const simpleType = this.getNamespaceSimpleTypeMap(typeNamespace)?.get(typeName);
        if (simpleType) {
            return this.getTypeForSimpleType(simpleType, typeNamespace);
        }
        throw new Error(`Type: ${typeName} couldn't be found in ${typeNamespace}`);
    }
    getInputTypeForComplexType(complexType, complexTypeNamespace) {
        let complexTypeTC = this.complexTypeInputTCMap.get(complexType);
        if (!complexTypeTC) {
            const complexTypeName = (0, utils_1.sanitizeNameForGraphQL)(complexType.attributes.name);
            const prefix = this.namespaceTypePrefixMap.get(complexTypeNamespace);
            const aliasMap = this.aliasMap.get(complexType);
            const fieldMap = {};
            const choiceOrSequenceObjects = [
                ...(complexType.sequence || []),
                ...(complexType.choice || []),
            ];
            for (const sequenceOrChoiceObj of choiceOrSequenceObjects) {
                if (sequenceOrChoiceObj.element) {
                    for (const elementObj of sequenceOrChoiceObj.element) {
                        if (elementObj.attributes?.name) {
                            const fieldName = (0, utils_1.sanitizeNameForGraphQL)(elementObj.attributes.name);
                            fieldMap[fieldName] = {
                                type: () => {
                                    const maxOccurs = sequenceOrChoiceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                                    const minOccurs = sequenceOrChoiceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                                    const nillable = sequenceOrChoiceObj.attributes?.nillable || elementObj.attributes?.nillable;
                                    const isPlural = maxOccurs != null && maxOccurs !== '1';
                                    let isNullable = false;
                                    if (minOccurs == null || minOccurs === '0') {
                                        isNullable = true;
                                    }
                                    if (nillable === 'true') {
                                        isNullable = true;
                                    }
                                    if (nillable === 'false') {
                                        isNullable = false;
                                    }
                                    if (elementObj.attributes?.type) {
                                        const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
                                        let typeNamespace;
                                        if (elementObj.attributes[typeNamespaceAlias]) {
                                            typeNamespace = elementObj.attributes[typeNamespaceAlias];
                                        }
                                        else {
                                            typeNamespace = aliasMap.get(typeNamespaceAlias);
                                        }
                                        if (!typeNamespace) {
                                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                        }
                                        let finalTC = this.getInputTypeForTypeNameInNamespace({
                                            typeName,
                                            typeNamespace,
                                        });
                                        if (isPlural) {
                                            finalTC = finalTC.getTypePlural();
                                        }
                                        if (!isNullable) {
                                            finalTC = finalTC.getTypeNonNull();
                                        }
                                        return finalTC;
                                    }
                                    else if (elementObj.simpleType) {
                                        // eslint-disable-next-line no-unreachable-loop
                                        for (const simpleTypeObj of elementObj.simpleType) {
                                            // Dynamically defined simple type
                                            // So we need to define alias map for this type
                                            this.aliasMap.set(simpleTypeObj, aliasMap);
                                            // Inherit the name from elementObj, scoped to the parent type name
                                            // to avoid collisions when sibling types share an inline field name.
                                            // Skip scoping when complexTypeName already equals the namespace prefix
                                            // (i.e. it is a top-level-element anonymous type) to avoid double-prefixing
                                            // like ByNameDataSet_ByNameDataSet_ByName.
                                            simpleTypeObj.attributes = simpleTypeObj.attributes || {};
                                            simpleTypeObj.attributes.name =
                                                simpleTypeObj.attributes.name ||
                                                    (complexTypeName !== prefix
                                                        ? `${complexTypeName}_${elementObj.attributes.name}`
                                                        : elementObj.attributes.name);
                                            let finalTC = this.getTypeForSimpleType(simpleTypeObj, complexTypeNamespace);
                                            if (isPlural) {
                                                finalTC = finalTC.getTypePlural();
                                            }
                                            if (!isNullable) {
                                                finalTC = finalTC.getTypeNonNull();
                                            }
                                            return finalTC;
                                        }
                                    }
                                    else if (elementObj.complexType) {
                                        // eslint-disable-next-line no-unreachable-loop
                                        for (const complexTypeObj of elementObj.complexType) {
                                            // Dynamically defined type
                                            // So we need to define alias map for this type
                                            this.aliasMap.set(complexTypeObj, aliasMap);
                                            // Inherit the name from elementObj, scoped to the parent type name
                                            // to avoid collisions when sibling types share an inline field name.
                                            // Skip scoping when complexTypeName already equals the namespace prefix
                                            // (i.e. it is a top-level-element anonymous type) to avoid double-prefixing.
                                            complexTypeObj.attributes = complexTypeObj.attributes || {};
                                            complexTypeObj.attributes.name =
                                                complexTypeObj.attributes.name ||
                                                    (complexTypeName !== prefix
                                                        ? `${complexTypeName}_${elementObj.attributes.name}`
                                                        : elementObj.attributes.name);
                                            let finalTC = this.getInputTypeForComplexType(complexTypeObj, complexTypeNamespace);
                                            if (isPlural) {
                                                finalTC = finalTC.getTypePlural();
                                            }
                                            if (!isNullable) {
                                                finalTC = finalTC.getTypeNonNull();
                                            }
                                            return finalTC;
                                        }
                                    }
                                    throw new Error(`Invalid element type definition: ${complexTypeName}->${fieldName}`);
                                },
                            };
                        }
                        else {
                            if (elementObj.attributes?.ref) {
                                this.logger.warn(`element.ref isn't supported yet.`);
                            }
                            else {
                                this.logger.warn(`Element doesn't have a name in ${complexTypeName}. Ignoring...`);
                            }
                        }
                    }
                }
                if (sequenceOrChoiceObj.any) {
                    for (const anyObj of sequenceOrChoiceObj.any) {
                        // namespace may be a space-delimited list; trim each token and skip XSD wildcards
                        const anyNamespaces = (anyObj.attributes?.namespace ?? '')
                            .split(/\s+/)
                            .map((s) => s.trim())
                            .filter((ns) => ns && !ns.startsWith('##'));
                        for (const anyNamespace of anyNamespaces) {
                            const anyTypeTC = this.getInputTypeForTypeNameInNamespace({
                                typeName: complexType.attributes.name,
                                typeNamespace: anyNamespace,
                            });
                            if ('getFields' in anyTypeTC) {
                                for (const fieldName in anyTypeTC.getFields()) {
                                    fieldMap[fieldName] = anyTypeTC.getField(fieldName);
                                }
                            }
                        }
                    }
                }
            }
            if (complexType.complexContent) {
                for (const complexContentObj of complexType.complexContent) {
                    for (const extensionObj of complexContentObj.extension) {
                        const [baseTypeNamespaceAlias, baseTypeName] = extensionObj.attributes.base.split(':');
                        let baseTypeNamespace;
                        if (extensionObj.attributes[baseTypeNamespaceAlias]) {
                            baseTypeNamespace = extensionObj.attributes[baseTypeNamespaceAlias];
                        }
                        else {
                            baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias);
                        }
                        if (!baseTypeNamespace) {
                            throw new Error(`Namespace alias: ${baseTypeNamespaceAlias} is undefined!`);
                        }
                        const baseType = this.getNamespaceComplexTypeMap(baseTypeNamespace)?.get(baseTypeName);
                        if (!baseType) {
                            throw new Error(`Complex Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${complexTypeName}`);
                        }
                        const baseTypeTC = this.getInputTypeForComplexType(baseType, baseTypeNamespace);
                        if ('getFields' in baseTypeTC) {
                            for (const fieldName in baseTypeTC.getFields()) {
                                fieldMap[fieldName] = baseTypeTC.getField(fieldName);
                            }
                        }
                        for (const sequenceObj of extensionObj.sequence) {
                            for (const elementObj of sequenceObj.element) {
                                fieldMap[(0, utils_1.sanitizeNameForGraphQL)(elementObj.attributes.name)] = {
                                    type: () => {
                                        const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
                                        let typeNamespace;
                                        if (elementObj.attributes[typeNamespaceAlias]) {
                                            typeNamespace = elementObj.attributes[typeNamespaceAlias];
                                        }
                                        else {
                                            typeNamespace = aliasMap.get(typeNamespaceAlias);
                                        }
                                        if (!typeNamespace) {
                                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                                        }
                                        return this.getInputTypeForTypeNameInNamespace({ typeName, typeNamespace });
                                    },
                                };
                            }
                        }
                    }
                }
            }
            if (Object.keys(fieldMap).length === 0) {
                complexTypeTC = this.schemaComposer.createScalarTC(graphql_compose_1.GraphQLJSON);
            }
            else {
                // When two schemas share the same alias-derived prefix (e.g. both declare
                // xmlns:tns="<own namespace>"), types with the same name from different
                // namespaces collide. Detect the collision and fall back to a slug derived
                // from the namespace URI. Loop until unique in case two URIs produce the
                // same slug after normalization.
                const candidateName = `${prefix}_${complexTypeName}_Input`;
                let inputTypeName = candidateName;
                if (this.schemaComposer.has(candidateName)) {
                    const nsSlug = namespaceToSlug(complexTypeNamespace);
                    inputTypeName = `${nsSlug}_${complexTypeName}_Input`;
                    let i = 2;
                    while (this.schemaComposer.has(inputTypeName)) {
                        inputTypeName = `${nsSlug}_${complexTypeName}_Input_${i++}`;
                    }
                }
                complexTypeTC = this.schemaComposer.createInputTC({
                    name: inputTypeName,
                    fields: fieldMap,
                });
                this.typeNamespaceMap.set(inputTypeName, complexTypeNamespace);
            }
            this.complexTypeInputTCMap.set(complexType, complexTypeTC);
        }
        return complexTypeTC;
    }
    getOutputFieldTypeFromElement(elementObj, aliasMap, namespace, parentTypeName) {
        if (elementObj.attributes?.type) {
            const [typeNamespaceAlias, typeName] = elementObj.attributes.type.split(':');
            let typeNamespace;
            if (elementObj.attributes[typeNamespaceAlias]) {
                typeNamespace = elementObj.attributes[typeNamespaceAlias];
            }
            else {
                typeNamespace = aliasMap.get(typeNamespaceAlias);
            }
            if (!typeNamespace) {
                throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
            }
            const outputTC = this.getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace });
            return outputTC;
        }
        else if (elementObj.simpleType) {
            // eslint-disable-next-line no-unreachable-loop
            for (const simpleTypeObj of elementObj.simpleType) {
                // Dynamically defined simple type
                // So we need to define alias map for this type
                this.aliasMap.set(simpleTypeObj, aliasMap);
                // Inherit the name from elementObj, scoped to the parent type name
                // to avoid collisions when sibling types share an inline field name.
                // Skip scoping when parentTypeName already equals the namespace prefix
                // (i.e. it is a top-level-element anonymous type) to avoid double-prefixing.
                const nsPrefix = this.namespaceTypePrefixMap.get(namespace);
                const effectiveParentName = parentTypeName && parentTypeName !== nsPrefix ? parentTypeName : null;
                simpleTypeObj.attributes = simpleTypeObj.attributes || {};
                simpleTypeObj.attributes.name =
                    simpleTypeObj.attributes.name ||
                        (effectiveParentName
                            ? `${effectiveParentName}_${elementObj.attributes.name}`
                            : elementObj.attributes.name);
                const outputTC = this.getTypeForSimpleType(simpleTypeObj, namespace);
                return outputTC;
            }
        }
        else if (elementObj.complexType) {
            // eslint-disable-next-line no-unreachable-loop
            for (const complexTypeObj of elementObj.complexType) {
                // Dynamically defined type
                // So we need to define alias map for this type
                this.aliasMap.set(complexTypeObj, aliasMap);
                // Inherit the name from elementObj, scoped to the parent type name
                // to avoid collisions when sibling types share an inline field name.
                // Skip scoping when parentTypeName already equals the namespace prefix
                // (i.e. it is a top-level-element anonymous type) to avoid double-prefixing.
                const nsPrefix = this.namespaceTypePrefixMap.get(namespace);
                const effectiveParentName = parentTypeName && parentTypeName !== nsPrefix ? parentTypeName : null;
                complexTypeObj.attributes = complexTypeObj.attributes || {};
                complexTypeObj.attributes.name =
                    complexTypeObj.attributes.name ||
                        (effectiveParentName
                            ? `${effectiveParentName}_${elementObj.attributes.name}`
                            : elementObj.attributes.name);
                const outputTC = this.getOutputTypeForComplexType(complexTypeObj, namespace);
                return outputTC;
            }
        }
        throw new Error(`Invalid element type definition: ${elementObj.attributes.name}`);
    }
    getOutputTypeForComplexType(complexType, complexTypeNamespace) {
        let complexTypeTC = this.complexTypeOutputTCMap.get(complexType);
        if (!complexTypeTC) {
            const complexTypeName = (0, utils_1.sanitizeNameForGraphQL)(complexType.attributes.name);
            const prefix = this.namespaceTypePrefixMap.get(complexTypeNamespace);
            const aliasMap = this.aliasMap.get(complexType);
            const fieldMap = {};
            const choiceOrSequenceObjects = [
                ...(complexType.sequence || []),
                ...(complexType.choice || []),
            ];
            for (const choiceOrSequenceObj of choiceOrSequenceObjects) {
                if (choiceOrSequenceObj.element) {
                    for (const elementObj of choiceOrSequenceObj.element) {
                        if (elementObj.attributes?.name) {
                            const fieldName = (0, utils_1.sanitizeNameForGraphQL)(elementObj.attributes.name);
                            const maxOccurs = choiceOrSequenceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                            const minOccurs = choiceOrSequenceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                            const nillable = choiceOrSequenceObj.attributes?.nillable || elementObj.attributes?.nillable;
                            const isPlural = maxOccurs != null && maxOccurs !== '1';
                            let isNullable = false;
                            if (minOccurs == null || minOccurs === '0') {
                                isNullable = true;
                            }
                            if (nillable === 'true') {
                                isNullable = true;
                            }
                            if (nillable === 'false') {
                                isNullable = false;
                            }
                            fieldMap[fieldName] = {
                                type: () => {
                                    let outputTC = this.getOutputFieldTypeFromElement(elementObj, aliasMap, complexTypeNamespace, complexTypeName);
                                    if (isPlural) {
                                        outputTC = outputTC.getTypePlural();
                                    }
                                    if (!isNullable) {
                                        outputTC = outputTC.getTypeNonNull();
                                    }
                                    return outputTC;
                                },
                            };
                        }
                        else {
                            if (elementObj.attributes?.ref) {
                                this.logger.warn(`element.ref isn't supported yet.`, elementObj.attributes?.ref);
                            }
                            else {
                                this.logger.warn(`Element doesn't have a name in ${complexTypeName}. Ignoring...`);
                            }
                        }
                    }
                }
                if (choiceOrSequenceObj.any) {
                    for (const anyObj of choiceOrSequenceObj.any) {
                        // namespace may be a space-delimited list; trim each token and skip XSD wildcards
                        const anyNamespaces = (anyObj.attributes?.namespace ?? '')
                            .split(/\s+/)
                            .map((s) => s.trim())
                            .filter((ns) => ns && !ns.startsWith('##'));
                        for (const anyNamespace of anyNamespaces) {
                            const anyTypeTC = this.getOutputTypeForTypeNameInNamespace({
                                typeName: complexType.attributes.name,
                                typeNamespace: anyNamespace,
                            });
                            if ('getFields' in anyTypeTC) {
                                for (const fieldName in anyTypeTC.getFields()) {
                                    fieldMap[fieldName] = anyTypeTC.getField(fieldName);
                                }
                            }
                        }
                    }
                }
            }
            if (complexType.complexContent) {
                for (const complexContentObj of complexType.complexContent) {
                    for (const extensionObj of complexContentObj.extension) {
                        const [baseTypeNamespaceAlias, baseTypeName] = extensionObj.attributes.base.split(':');
                        const baseTypeNamespace = aliasMap.get(baseTypeNamespaceAlias) ||
                            extensionObj.attributes[baseTypeNamespaceAlias];
                        if (!baseTypeNamespace) {
                            throw new Error(`Namespace alias: ${baseTypeNamespaceAlias} is undefined!`);
                        }
                        const baseType = this.getNamespaceComplexTypeMap(baseTypeNamespace)?.get(baseTypeName);
                        if (!baseType) {
                            throw new Error(`Complex Type: ${baseTypeName} couldn't be found in ${baseTypeNamespace} needed for ${complexTypeName}`);
                        }
                        const baseTypeTC = this.getOutputTypeForComplexType(baseType, baseTypeNamespace);
                        if ('getFields' in baseTypeTC) {
                            for (const fieldName in baseTypeTC.getFields()) {
                                fieldMap[fieldName] = baseTypeTC.getField(fieldName);
                            }
                        }
                        const choiceOrSequenceObjects = [
                            ...(extensionObj.sequence || []),
                            ...(extensionObj.choice || []),
                        ];
                        for (const choiceOrSequenceObj of choiceOrSequenceObjects) {
                            for (const elementObj of choiceOrSequenceObj.element) {
                                if (!elementObj.attributes?.name)
                                    continue;
                                const fieldName = (0, utils_1.sanitizeNameForGraphQL)(elementObj.attributes.name);
                                const maxOccurs = choiceOrSequenceObj.attributes?.maxOccurs || elementObj.attributes?.maxOccurs;
                                const minOccurs = choiceOrSequenceObj.attributes?.minOccurs || elementObj.attributes?.minOccurs;
                                const nillable = choiceOrSequenceObj.attributes?.nillable || elementObj.attributes?.nillable;
                                const isPlural = maxOccurs != null && maxOccurs !== '1';
                                let isNullable = false;
                                if (minOccurs == null || minOccurs === '0') {
                                    isNullable = true;
                                }
                                if (nillable === 'true') {
                                    isNullable = true;
                                }
                                if (nillable === 'false') {
                                    isNullable = false;
                                }
                                fieldMap[fieldName] = {
                                    type: () => {
                                        let outputTC = this.getOutputFieldTypeFromElement(elementObj, aliasMap, complexTypeNamespace, complexTypeName);
                                        if (isPlural) {
                                            outputTC = outputTC.getTypePlural();
                                        }
                                        if (!isNullable) {
                                            outputTC = outputTC.getTypeNonNull();
                                        }
                                        return outputTC;
                                    },
                                };
                            }
                        }
                    }
                }
            }
            if (Object.keys(fieldMap).length === 0) {
                complexTypeTC = this.schemaComposer.createScalarTC(graphql_compose_1.GraphQLJSON);
            }
            else {
                // When two schemas share the same alias-derived prefix (e.g. both declare
                // xmlns:tns="<own namespace>"), types with the same name from different
                // namespaces collide. Detect the collision and fall back to a slug derived
                // from the namespace URI. Loop until unique in case two URIs produce the
                // same slug after normalization.
                const candidateName = `${prefix}_${complexTypeName}`;
                let outputTypeName = candidateName;
                if (this.schemaComposer.has(candidateName)) {
                    const nsSlug = namespaceToSlug(complexTypeNamespace);
                    outputTypeName = `${nsSlug}_${complexTypeName}`;
                    let i = 2;
                    while (this.schemaComposer.has(outputTypeName)) {
                        outputTypeName = `${nsSlug}_${complexTypeName}_${i++}`;
                    }
                }
                complexTypeTC = this.schemaComposer.createObjectTC({
                    name: outputTypeName,
                    fields: fieldMap,
                });
            }
            this.complexTypeOutputTCMap.set(complexType, complexTypeTC);
        }
        return complexTypeTC;
    }
    getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace, }) {
        // Check element aliases first — consistent with the eager path that overwrites the type map
        const elementRef = this.namespaceElementRefMap.get(typeNamespace)?.get(typeName);
        if (elementRef) {
            return this.getOutputTypeForTypeNameInNamespace(elementRef);
        }
        const complexType = this.getNamespaceComplexTypeMap(typeNamespace)?.get(typeName);
        if (complexType) {
            return this.getOutputTypeForComplexType(complexType, typeNamespace);
        }
        const simpleType = this.getNamespaceSimpleTypeMap(typeNamespace)?.get(typeName);
        if (simpleType) {
            return this.getTypeForSimpleType(simpleType, typeNamespace);
        }
        throw new Error(`Type: ${typeName} couldn't be found in ${typeNamespace}`);
    }
    getOutputTypeForMessage(message) {
        let outputTCAndName = this.messageOutputTCMap.get(message);
        if (!outputTCAndName) {
            const aliasMap = this.aliasMap.get(message);
            const partObj = message.part[0];
            if (partObj.attributes.element) {
                const [elementNamespaceAlias, elementName] = partObj.attributes.element.split(':');
                outputTCAndName = {
                    type: () => {
                        const elementTypeNamespace = aliasMap.get(elementNamespaceAlias) || partObj.attributes[elementNamespaceAlias];
                        if (!elementTypeNamespace) {
                            throw new Error(`Namespace alias: ${elementNamespaceAlias} is undefined!`);
                        }
                        return this.getOutputTypeForTypeNameInNamespace({
                            typeName: elementName,
                            typeNamespace: elementTypeNamespace,
                        });
                    },
                    elementName,
                };
            }
            else if (partObj.attributes.type) {
                const elementName = partObj.attributes.name;
                outputTCAndName = {
                    type: () => {
                        const [typeNamespaceAlias, typeName] = partObj.attributes.type.split(':');
                        const typeNamespace = aliasMap.get(typeNamespaceAlias);
                        if (!typeNamespace) {
                            throw new Error(`Namespace alias: ${typeNamespaceAlias} is undefined!`);
                        }
                        return this.getOutputTypeForTypeNameInNamespace({ typeName, typeNamespace });
                    },
                    elementName,
                };
            }
            this.messageOutputTCMap.set(message, outputTCAndName);
        }
        return outputTCAndName;
    }
    buildSchema() {
        if (this.schemaComposer.Query.getFieldNames().length === 0) {
            this.schemaComposer.Query.addFields({
                placeholder: {
                    type: graphql_scalars_1.GraphQLVoid,
                },
            });
        }
        const schema = this.schemaComposer.buildSchema();
        const schemaExts = (schema.extensions ||= {});
        schemaExts.directives ||= {};
        // The transport definition survives SDL roundtrip via
        // @extraSchemaDefinitionDirective, so subgraph routers that reconstruct
        // the schema from SDL still see this metadata. typeNamespacesJson is
        // included here (rather than on schema.extensions directly) so the
        // namespace-aware envelope path also works after federation composition.
        schemaExts.directives.transport = {
            kind: 'soap',
            subgraph: this.subgraphName,
            typeNamespacesJson: Object.fromEntries(this.typeNamespaceMap),
        };
        return schema;
    }
}
exports.SOAPLoader = SOAPLoader;