async function resolveAdTreeRequests(url, maxDepth, baseNode = {}, currentDepth = 0, followAdditionalWrappers = true) {
        const adTree = { ...baseNode, children: [] };
        const { responseXML } = await playerInstance.sendRequestAsync(url, true, playerInstance.displayOptions.vastOptions.vastTimeout);
        const adElements = Array.from(responseXML.getElementsByTagName('Ad'));

        for (const adElement of adElements) {
            const vastAdTagUri = playerInstance.getVastAdTagUriFromWrapper(adElement);
            const isAdPod = adElement.attributes.sequence !== undefined;
            const adNode = { data: adElement };

            const adType = (adElement.getElementsByTagName('Linear').length && 'linear') ||
            (adElement.getElementsByTagName('NonLinearAds').length && 'nonLinear') || '';

            if (vastAdTagUri && currentDepth <= maxDepth && followAdditionalWrappers) {
                const [wrapperElement] = adElement.getElementsByTagName('Wrapper');
                const disableAdditionalWrappers = wrapperElement.attributes.followAdditionalWrappers && ["false", "0"].includes(wrapperElement.attributes.followAdditionalWrappers.value); // See VAST Wrapper spec
                const allowMultipleAds = wrapperElement.attributes.allowMultipleAds && ["true", "1"].includes(wrapperElement.attributes.allowMultipleAds.value); // See VAST Wrapper spec
                const fallbackOnNoAd = wrapperElement.attributes.fallbackOnNoAd && ["true", "1"].includes(wrapperElement.attributes.fallbackOnNoAd.value);

                try {
                    const wrapperResponse = await resolveAdTreeRequests(vastAdTagUri, maxDepth, { tagType: 'wrapper', ...adNode, fallbackOnNoAd }, currentDepth+1, !disableAdditionalWrappers);
                    wrapperResponse.fallbackOnNoAd = fallbackOnNoAd;

                    if (!allowMultipleAds || isAdPod) {
                        wrapperResponse.children = getFirstStandAloneAd(wrapperResponse.children);
                    }

                    adTree.children.push(wrapperResponse);
                } catch (e) {
                    adTree.children.push({ tagType: `wrapper`, fallbackOnNoAd, httpError: true })
                    playerInstance.debugMessage(`Error when loading Wrapper, will trigger fallback if available`, e);
                }
            } else if (!vastAdTagUri) {
                if (adType === 'nonLinear') {
                    // No extra checks needed on fallback for non linear ads
                    adTree.children.push({ tagType: 'inLine', ...adNode });
                } else if (Array.from(adElement.getElementsByTagName('MediaFiles')).length) {
                    // Check if there are mediaFiles
                    const mediaFiles = Array.from(adElement.getElementsByTagName('MediaFiles'));
                    const mediaFile = mediaFiles[0]?.getElementsByTagName('MediaFile');
                    const mediaFileUrl = mediaFile[0]?.textContent.trim();

                    // In case of vpaid, no need to check if media file is valid
                    // VPAID uses a JS file in this attribute, not a media file
                    if (mediaFile[0].getAttribute('apiFramework') === 'VPAID') {
                        adTree.children.push({ tagType: 'inLine', ...adNode });
                    } else {
                        // If valid, add it to the ad tree
                        const mediaFileIsValid = await validateMediaFile(mediaFileUrl);
                        if (mediaFileIsValid) {
                            adTree.children.push({ tagType: 'inLine', ...adNode });
                        }
                    }
                } else if (Array.from(adElement.getElementsByTagName('AdParameters')).length) {
                    // check if there are adParameters
                    const adParameters = Array.from(adElement.getElementsByTagName('AdParameters'));
                    for (const adParameter of adParameters) {
                        try {
                            const mediaFileObj = JSON.parse(adParameter.textContent.trim());
                            const mediaFileUrl = mediaFileObj?.videos?.length ? mediaFileObj?.videos[0]?.url : '';
                            const mediaFileIsValid = await validateMediaFile(mediaFileUrl);
                            // If valid, add it to the ad tree
                            if (mediaFileIsValid) {
                                adTree.children.push({ tagType: 'inLine', ...adNode });
                            }
                        } catch (error) {
                            console.error("Error parsing media file URL:", error);
                        }
                    }
                }
            }
        }

        return adTree;
    }
Resolves ad requests recursively and returns a tree of "Ad" and "Wrapper" elements

@param {string} url vast resource url
@param {number} maxDepth depth of recursive calls (wrapper depth)
@param {Partial<RawAdTree>} baseNode used for recursive calls as base node
@param {number} currentDepth used internally to track depth
@param {boolean} followAdditionalWrappers used internally to track nested wrapper calls
@returns {Promise<RawAdTree>}