<!-- SOLUV · part 21 -->
 warning messages to prevent duplicate warnings.
 *
 * @private
 * @type {Object<string, boolean>}
 */
const _cache = {};

/**
 * Custom console function handler for intercepting log, warn, and error calls.
 *
 * @private
 * @type {Function|null}
 */
let _setConsoleFunction = null;

/**
 * Sets a custom function to handle console output.
 *
 * This allows external code to intercept and handle console.log, console.warn,
 * and console.error calls made by three.js, which is useful for custom logging,
 * testing, or debugging workflows.
 *
 * @param {Function} fn - The function to handle console output. Should accept
 *                        (type, message, ...params) where type is 'log', 'warn', or 'error'.
 */
function setConsoleFunction( fn ) {

	_setConsoleFunction = fn;

}

/**
 * Gets the currently set custom console function.
 *
 * @return {Function|null} The custom console function, or null if not set.
 */
function getConsoleFunction() {

	return _setConsoleFunction;

}

/**
 * Logs an informational message with the 'THREE.' prefix.
 *
 * If a custom console function is set via setConsoleFunction(), it will be used
 * instead of the native console.log. The first parameter is treated as the
 * method name and is automatically prefixed with 'THREE.'.
 *
 * @param {...any} params - The message components. The first param is used as
 *                          the method name and prefixed with 'THREE.'.
 */
function log( ...params ) {

	const message = 'THREE.' + params.shift();

	if ( _setConsoleFunction ) {

		_setConsoleFunction( 'log', message, ...params );

	} else {

		console.log( message, ...params );

	}

}

/**
 * Enhances log/warn/error messages related to TSL.
 *
 * @param {Array<any>} params - The original message parameters.
 * @returns {Array<any>} The filtered and enhanced message parameters.
 */
function enhanceLogMessage( params ) {

	const message = params[ 0 ];

	if ( typeof message === 'string' && message.startsWith( 'TSL:' ) ) {

		const stackTrace = params[ 1 ];

		if ( stackTrace && stackTrace.isStackTrace ) {

			params[ 0 ] += ' ' + stackTrace.getLocation();

		} else {

			params[ 1 ] = 'Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.';

		}

	}

	return params;

}

/**
 * Logs a warning message with the 'THREE.' prefix.
 *
 * If a custom console function is set via setConsoleFunction(), it will be used
 * instead of the native console.warn. The first parameter is treated as the
 * method name and is automatically prefixed with 'THREE.'.
 *
 * @param {...any} params - The message components. The first param is used as
 *                          the method name and prefixed with 'THREE.'.
 */
function warn( ...params ) {

	params = enhanceLogMessage( params );

	const message = 'THREE.' + params.shift();

	if ( _setConsoleFunction ) {

		_setConsoleFunction( 'warn', message, ...params );

	} else {

		const stackTrace = params[ 0 ];

		if ( stackTrace && stackTrace.isStackTrace ) {

			console.warn( stackTrace.getError( message ) );

		} else {

			console.warn( message, ...params );

		}

	}

}

/**
 * Logs an error message with the 'THREE.' prefix.
 *
 * If a custom console function is set via setConsoleFunction(), it will be used
 * instead of the native console.error. The first parameter is treated as the
 * method name and is automatically prefixed with 'THREE.'.
 *
 * @param {...any} params - The message components. The first param is used as
 *                          the method name and prefixed with 'THREE.'.
 */
function error( ...params ) {

	params = enhanceLogMessage( params );

	const message = 'THREE.' + params.shift();

	if ( _setConsoleFunction ) {

		_setConsoleFunction( 'error', message, ...params );

	} else {

		const stackTrace = params[ 0 ];

		if ( stackTrace && stackTrace.isStackTrace ) {

			console.error( stackTrace.getError( message ) );

		} else {

			console.error( message, ...params );

		}

	}

}

/**
 * Logs a warning message only once, preventing duplicate warnings.
 *
 * This function maintains an internal cache of warning messages and will only
 * output each unique warning message once. Useful for warnings that may be
 * triggered repeatedly but should only be shown to the user once.
 *
 * @param {...any} params - The warning message components.
 */
function warnOnce( ...params ) {

	const message = params.join( ' ' );

	if ( message in _cache ) return;

	_cache[ message ] = true;

	warn( ...params );

}

/**
 * Yields execution to the main thread to allow rendering and other tasks.
 * Uses scheduler.yield() when available (Chrome 115+), falls back to requestAnimationFrame.
 *
 * @return {Promise<void>}
 */
function yieldToMain() {

	if ( typeof self !== 'undefined' && typeof self.scheduler !== 'undefined' && typeof self.scheduler.yield !== 'undefined' ) {

		return self.scheduler.yield();

	}

	return new Promise( resolve => {

		requestAnimationFrame( resolve );

	} );
