const require_rolldown_runtime = require("./rolldown-runtime-D_mwlA32.cjs");
const require_logger = require("./logger-DuG3amVa.cjs");
const require_fetch = require("./fetch-Hdu2Q-w3.cjs");
const require_esm = require("./esm-4Cu04ouJ.cjs");
const require_util = require("./util-M3cOWPu3.cjs");
const require_pythonUtils = require("./pythonUtils-D438TbQt.cjs");
const require_util$1 = require("./util-DtSnbKEe.cjs");
let path = require("path");
path = require_rolldown_runtime.__toESM(path, 1);
let child_process = require("child_process");
//#region src/providers/google/live.ts
const formatContentMessage = (contents, contentIndex, useRealtimeTextInput = false) => {
	if (contents[contentIndex].role !== "user") throw new Error("Can only take user role inputs.");
	if (contents[contentIndex].parts.length !== 1) throw new Error("Unexpected number of parts in user input.");
	const userMessage = contents[contentIndex].parts[0].text;
	if (useRealtimeTextInput) return { realtime_input: { text: userMessage } };
	return { client_content: {
		turns: [{
			role: "user",
			parts: [{ text: userMessage }]
		}],
		turn_complete: true
	} };
};
/**
* Helper function to fetch JSON with error handling
*/
const fetchJson = async (url, options) => {
	const response = await require_fetch.fetchWithProxy(url, options);
	if (!response.ok) throw new Error(`HTTP error - status: ${response.status}`);
	return response.json();
};
/**
* Helper function to try GET with query params, fallback to POST with JSON body
*/
const tryGetThenPost = async (url, data) => {
	try {
		const urlWithParams = new URL(url);
		if (data) {
			const params = typeof data === "string" ? JSON.parse(data) : data;
			Object.entries(params).forEach(([key, value]) => {
				urlWithParams.searchParams.append(key, String(value));
			});
		}
		return await fetchJson(urlWithParams.href);
	} catch {
		return fetchJson(url, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: data ? JSON.stringify(typeof data === "string" ? JSON.parse(data) : data) : null
		});
	}
};
var GoogleLiveProvider = class {
	config;
	modelName;
	loadedFunctionCallbacks = {};
	constructor(modelName, options) {
		this.modelName = modelName;
		this.config = options.config || {};
	}
	validateFunctionToolCall(output, vars) {
		require_util$1.validateFunctionCall(output, this.config.tools, vars);
	}
	id() {
		return `google:live:${this.modelName}`;
	}
	toString() {
		return `[Google Live Provider ${this.modelName}]`;
	}
	convertPcmToWav(base64PcmData) {
		const pcmBuffer = Buffer.from(base64PcmData, "base64");
		const wavBuffer = this.createWavHeader(pcmBuffer.length, 24e3, 16, 1);
		return Buffer.concat([wavBuffer, pcmBuffer]).toString("base64");
	}
	createWavHeader(dataLength, sampleRate, bitsPerSample, channels) {
		const header = Buffer.alloc(44);
		header.write("RIFF", 0);
		header.writeUInt32LE(36 + dataLength, 4);
		header.write("WAVE", 8);
		header.write("fmt ", 12);
		header.writeUInt32LE(16, 16);
		header.writeUInt16LE(1, 20);
		header.writeUInt16LE(channels, 22);
		header.writeUInt32LE(sampleRate, 24);
		header.writeUInt32LE(sampleRate * channels * bitsPerSample / 8, 28);
		header.writeUInt16LE(channels * bitsPerSample / 8, 32);
		header.writeUInt16LE(bitsPerSample, 34);
		header.write("data", 36);
		header.writeUInt32LE(dataLength, 40);
		return header;
	}
	getApiKey() {
		return this.config.apiKey || require_logger.getEnvString("GOOGLE_API_KEY") || require_logger.getEnvString("GEMINI_API_KEY");
	}
	/**
	* Gets an OAuth2 access token from Google credentials for the Generative Language API.
	* Returns undefined if credentials are not available or if there's an error.
	*
	* Supports authentication via:
	* - Service account JSON (via config.credentials or GOOGLE_APPLICATION_CREDENTIALS)
	* - Application Default Credentials (via `gcloud auth application-default login`)
	*/
	async getAccessToken() {
		return require_util$1.getGoogleAccessToken(require_util$1.loadCredentials(this.config.credentials));
	}
	async callApi(prompt, context) {
		const accessToken = await this.getAccessToken();
		const apiKey = this.getApiKey();
		if (!accessToken && !apiKey) throw new Error("Google authentication is not configured. The Live API requires OAuth2 authentication.\n\nEither:\n1. Set up Application Default Credentials:\n   gcloud auth application-default login --client-id-file=client_secret.json --scopes=\"https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/generative-language.retriever\"\n2. Set GOOGLE_APPLICATION_CREDENTIALS to a service account key file, or\n3. Add `credentials` to the provider config with service account JSON\n\nNote: GOOGLE_API_KEY is NOT supported for the Live API WebSocket endpoint.\nFor OAuth2 setup instructions, see: https://ai.google.dev/gemini-api/docs/oauth\nThese options require the google-auth-library package to be installed.");
		const config = require_util$1.mergeGoogleCompletionOptions(this.config, context?.prompt?.config);
		const { toolConfig, toolsDisabled } = require_util$1.resolveGoogleToolConfig(config);
		const { contents, systemInstruction } = require_util$1.geminiFormatAndSystemInstructions(prompt, context?.vars, config.systemInstruction, { useAssistantRole: config.useAssistantRole });
		let contentIndex = 0;
		let statefulApi;
		if (!toolsDisabled && config.functionToolStatefulApi?.file) try {
			const pythonPath = await require_pythonUtils.validatePythonPath(config.functionToolStatefulApi.pythonExecutable || require_logger.getEnvString("PROMPTFOO_PYTHON") || "python3", !!config.functionToolStatefulApi.pythonExecutable || !!require_logger.getEnvString("PROMPTFOO_PYTHON"));
			require_logger.logger.debug(`Spawning API with Python executable: ${pythonPath}`);
			statefulApi = (0, child_process.spawn)(pythonPath, [config.functionToolStatefulApi.file]);
			statefulApi.on("error", (err) => {
				require_logger.logger.error(`Error spawning Python process: ${JSON.stringify(err)}`);
			});
			statefulApi.stdout?.on("data", (data) => {
				require_logger.logger.debug(`Python API stdout: ${data.toString()}`);
			});
			statefulApi.stderr?.on("data", (data) => {
				require_logger.logger.error(`Python API stderr: ${data.toString()}`);
			});
			await new Promise((resolve) => setTimeout(resolve, 1e3));
			require_logger.logger.debug("Stateful API process started");
		} catch (err) {
			require_logger.logger.error(`Failed to spawn Python API: ${JSON.stringify(err)}`);
		}
		const configTools = toolsDisabled ? require_util$1.stripExecutableToolFileReferences(config.tools, context?.vars) : config.tools;
		const fileTools = configTools ? await require_util.maybeLoadToolsFromExternalFile(configTools, context?.vars) : [];
		const normalizedTools = Array.isArray(fileTools) ? require_util$1.normalizeTools(fileTools) : fileTools ? [fileTools] : [];
		const requestTools = toolsDisabled ? require_util$1.removeGoogleFunctionDeclarations(normalizedTools) : normalizedTools;
		const WebSocketCtor = (await import("ws")).default;
		return new Promise((resolve) => {
			const isNativeAudioModel = this.modelName.includes("native-audio");
			const usesRealtimeTextInput = this.modelName.startsWith("gemini-3.1-flash-live");
			let isResolved = false;
			const safeResolve = (response) => {
				if (!isResolved) {
					isResolved = true;
					resolve(response);
				}
			};
			let { apiVersion } = config;
			if (!apiVersion) apiVersion = "v1alpha";
			let url;
			if (accessToken) {
				url = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.${apiVersion}.GenerativeService.BidiGenerateContent?access_token=${accessToken}`;
				require_logger.logger.debug("Using OAuth2 access token for Google Live API authentication");
			} else {
				url = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.${apiVersion}.GenerativeService.BidiGenerateContent?key=${apiKey}`;
				require_logger.logger.debug("Using API key for Google Live API authentication (may not be supported)");
			}
			const ws = new WebSocketCtor(url);
			let response_text_total = "";
			let response_audio_total = "";
			let response_audio_transcript = "";
			let hasAudioContent = false;
			const function_calls_total = [];
			let statefulApiState = void 0;
			let hasFinalized = false;
			const isTextExpected = config.generationConfig?.response_modalities?.includes("text") ?? false;
			const isAudioExpected = config.generationConfig?.response_modalities?.includes("audio") ?? false;
			let hasTextStreamEnded = !isTextExpected;
			let hasAudioStreamEnded = !isAudioExpected;
			const hasOutputTranscription = !!config.generationConfig?.outputAudioTranscription;
			const timeout = setTimeout(() => {
				require_logger.logger.error("WebSocket connection timed out after 30 seconds");
				ws.close();
				safeResolve({ error: "WebSocket request timed out" });
			}, config.timeoutMs || 3e4);
			const finalizeResponse = async () => {
				if (hasFinalized) {
					require_logger.logger.debug("finalizeResponse already called, skipping duplicate call");
					return;
				}
				hasFinalized = true;
				if (ws.readyState === WebSocketCtor.OPEN) ws.close();
				clearTimeout(timeout);
				if (!isResolved && !toolsDisabled && config.functionToolStatefulApi) try {
					const url = new URL("get_state", config.functionToolStatefulApi.url).href;
					statefulApiState = await fetchJson(url);
					require_logger.logger.debug(`Stateful api state: ${JSON.stringify(statefulApiState)}`);
				} catch (err) {
					require_logger.logger.error(`Error retrieving final state of api: ${JSON.stringify(err)}`);
				}
				if (statefulApi) statefulApi.kill();
				let outputText = response_text_total;
				let thinking = void 0;
				if (hasAudioContent && response_audio_transcript) if (response_text_total) {
					thinking = response_text_total;
					outputText = response_audio_transcript;
				} else outputText = response_audio_transcript;
				const result = {
					output: {
						text: outputText,
						toolCall: { functionCalls: function_calls_total },
						statefulApiState,
						...thinking && { thinking }
					},
					metadata: {}
				};
				if (hasAudioContent) result.audio = {
					data: this.convertPcmToWav(response_audio_total),
					format: "wav",
					transcript: response_audio_transcript || response_text_total || void 0
				};
				safeResolve(result);
			};
			ws.onopen = () => {
				require_logger.logger.debug("WebSocket connection is opening...");
				const { speechConfig, outputAudioTranscription, inputAudioTranscription, enableAffectiveDialog, proactivity, ...restGenerationConfig } = config.generationConfig || {};
				let formattedSpeechConfig;
				if (speechConfig) formattedSpeechConfig = {
					...speechConfig.voiceConfig && { voice_config: { prebuilt_voice_config: { voice_name: speechConfig.voiceConfig.prebuiltVoiceConfig?.voiceName } } },
					...speechConfig.languageCode && { language_code: speechConfig.languageCode }
				};
				let formattedProactivity;
				if (proactivity) formattedProactivity = { proactive_audio: proactivity.proactiveAudio };
				const setupMessage = { setup: {
					model: `models/${this.modelName}`,
					generation_config: {
						context: config.context,
						examples: config.examples,
						stopSequences: config.stopSequences,
						temperature: config.temperature,
						maxOutputTokens: config.maxOutputTokens,
						topP: config.topP,
						topK: config.topK,
						...restGenerationConfig,
						...formattedSpeechConfig ? { speech_config: formattedSpeechConfig } : {},
						...enableAffectiveDialog ? { enable_affective_dialog: enableAffectiveDialog } : {},
						...formattedProactivity ? { proactivity: formattedProactivity } : {}
					},
					...toolConfig ? { toolConfig } : {},
					...requestTools.length > 0 ? { tools: requestTools } : {},
					...systemInstruction ? { systemInstruction } : {},
					...outputAudioTranscription ? { output_audio_transcription: outputAudioTranscription } : {},
					...inputAudioTranscription ? { input_audio_transcription: inputAudioTranscription } : {}
				} };
				require_logger.logger.debug(`Sending setup message: ${JSON.stringify(setupMessage, null, 2)}`);
				ws.send(JSON.stringify(setupMessage));
			};
			ws.onmessage = async (event) => {
				if (isResolved) return;
				require_logger.logger.debug("WebSocket message received");
				let responseData;
				if (event.data instanceof ArrayBuffer || event.data instanceof Buffer) {
					const dataString = event.data.toString("utf-8");
					try {
						JSON.parse(dataString);
						responseData = dataString;
					} catch {
						hasAudioContent = true;
						const audioBuffer = Buffer.isBuffer(event.data) ? event.data : Buffer.from(event.data);
						response_audio_total += audioBuffer.toString("base64");
						clearTimeout(timeout);
						if (isAudioExpected) hasAudioStreamEnded = false;
						return;
					}
				} else if (typeof event.data === "string") responseData = event.data;
				else {
					require_logger.logger.warn(`Unexpected event.data type: ${typeof event.data}`);
					ws.close();
					safeResolve({ error: "Unexpected response data format" });
					return;
				}
				try {
					const responseText = await new Response(responseData).text();
					if (isResolved) return;
					const response = JSON.parse(responseText);
					if (response.error) {
						require_logger.logger.error(`Google Live API error: ${JSON.stringify(response.error)}`);
						ws.close();
						safeResolve({ error: `Google Live API error: ${JSON.stringify(response.error)}` });
						return;
					}
					const messageType = response.setupComplete ? "setupComplete" : response.serverContent?.modelTurn ? "modelTurn" : response.serverContent?.generationComplete ? "generationComplete" : response.serverContent?.turnComplete ? "turnComplete" : response.toolCall ? "toolCall" : response.streamingCustomOp ? "streamingCustomOp" : "unknown";
					require_logger.logger.debug(`Message type: ${messageType}, hasAudioContent: ${hasAudioContent}, hasOutputTranscription: ${hasOutputTranscription}`);
					if (response.setupComplete) {
						const contentMessage = formatContentMessage(contents, contentIndex, usesRealtimeTextInput);
						contentIndex += 1;
						require_logger.logger.debug(`WebSocket sent: ${JSON.stringify(contentMessage)}`);
						ws.send(JSON.stringify(contentMessage));
					} else if (response.serverContent) {
						const { serverContent } = response;
						if (serverContent.modelTurn?.parts) {
							for (const part of serverContent.modelTurn.parts) {
								if (part.text) {
									response_text_total += part.text;
									clearTimeout(timeout);
								}
								if (part.inlineData?.mimeType?.includes("audio")) {
									hasAudioContent = true;
									response_audio_total += part.inlineData.data;
									clearTimeout(timeout);
									if (isAudioExpected) hasAudioStreamEnded = false;
								}
							}
							if (serverContent.outputTranscription?.text) {
								response_audio_transcript += serverContent.outputTranscription.text;
								if (isAudioExpected) hasAudioContent = true;
								clearTimeout(timeout);
							}
						} else if (serverContent.outputTranscription?.text) {
							response_audio_transcript += serverContent.outputTranscription.text;
							clearTimeout(timeout);
						}
						if (serverContent.generationComplete) {
							require_logger.logger.debug(`Generation complete received - text expected: ${isTextExpected}, audio expected: ${isAudioExpected}, has transcription: ${hasOutputTranscription}`);
							if (isTextExpected && !hasTextStreamEnded) hasTextStreamEnded = true;
							if (isAudioExpected && !hasAudioStreamEnded && hasOutputTranscription) hasAudioStreamEnded = true;
							if (usesRealtimeTextInput && contentIndex < contents.length) {
								const contentMessage = formatContentMessage(contents, contentIndex, usesRealtimeTextInput);
								contentIndex += 1;
								require_logger.logger.debug(`WebSocket sent after generation complete: ${JSON.stringify(contentMessage)}`);
								ws.send(JSON.stringify(contentMessage));
								hasTextStreamEnded = !isTextExpected;
								hasAudioStreamEnded = !isAudioExpected;
								return;
							}
						}
						if (serverContent.turnComplete && contentIndex < contents.length) {
							const contentMessage = formatContentMessage(contents, contentIndex, usesRealtimeTextInput);
							contentIndex += 1;
							require_logger.logger.debug(`WebSocket sent (multi-turn): ${JSON.stringify(contentMessage)}`);
							ws.send(JSON.stringify(contentMessage));
							return;
						}
						if (serverContent.turnComplete && contentIndex >= contents.length) {
							require_logger.logger.debug(`Turn complete received - text expected: ${isTextExpected}, text ended: ${hasTextStreamEnded}, audio expected: ${isAudioExpected}, audio ended: ${hasAudioStreamEnded}, has audio: ${hasAudioContent}, has transcription: ${!!response_audio_transcript}`);
							if (isTextExpected && !hasTextStreamEnded) hasTextStreamEnded = true;
							if (isAudioExpected && !hasAudioStreamEnded) hasAudioStreamEnded = true;
						}
						if (hasTextStreamEnded && hasAudioStreamEnded && contentIndex >= contents.length) try {
							await finalizeResponse();
						} catch (err) {
							require_logger.logger.error(`Error in finalizeResponse: ${err}`);
							safeResolve({ error: `Error finalizing response: ${err}` });
						}
						return;
					} else if (response.toolCall?.functionCalls) {
						if (isResolved) return;
						if (toolsDisabled) {
							require_logger.logger.warn("Ignoring function calls received while tools are disabled.");
							for (const functionCall of response.toolCall.functionCalls) if (functionCall?.id && functionCall.name) {
								const toolMessage = { tool_response: { function_responses: {
									id: functionCall.id,
									name: functionCall.name,
									response: { error: "Tool calls are disabled for this request." }
								} } };
								ws.send(JSON.stringify(toolMessage));
							}
						} else for (const functionCall of response.toolCall.functionCalls) {
							function_calls_total.push(functionCall);
							if (functionCall && functionCall.id && functionCall.name) {
								let callbackResponse = {};
								const functionName = functionCall.name;
								try {
									if (config.functionToolCallbacks?.[functionName]) callbackResponse = await this.executeFunctionCallback(functionName, JSON.stringify(typeof functionCall.args === "string" ? JSON.parse(functionCall.args) : functionCall.args), config.functionToolCallbacks);
									else if (config.functionToolStatefulApi) {
										require_logger.logger.warn("functionToolStatefulApi configured but no HTTP client implemented for it after cleanup.");
										const baseUrl = new URL(functionName, config.functionToolStatefulApi.url).href;
										try {
											callbackResponse = await tryGetThenPost(baseUrl, functionCall.args);
											require_logger.logger.debug(`Stateful api response: ${JSON.stringify(callbackResponse)}`);
										} catch (err) {
											callbackResponse = { error: `Error executing function ${functionName}: ${JSON.stringify(err)}` };
											require_logger.logger.error(`Error executing function ${functionName}: ${JSON.stringify(err)}`);
										}
									}
								} catch (err) {
									callbackResponse = { error: `Error executing function ${functionName}: ${JSON.stringify(err)}` };
									require_logger.logger.error(`Error executing function ${functionName}: ${JSON.stringify(err)}`);
								}
								if (isResolved) return;
								const toolMessage = { tool_response: { function_responses: {
									id: functionCall.id,
									name: functionName,
									response: callbackResponse
								} } };
								require_logger.logger.debug(`WebSocket sent: ${JSON.stringify(toolMessage)}`);
								ws.send(JSON.stringify(toolMessage));
							}
						}
					} else if (response.sessionResumptionUpdate) require_logger.logger.debug(`Session resumption update received: ${JSON.stringify(response.sessionResumptionUpdate)}`);
					else if (response.realtimeInput?.mediaChunks) {
						for (const chunk of response.realtimeInput.mediaChunks) if (chunk.mimeType?.includes("audio")) {
							hasAudioContent = true;
							response_audio_total += chunk.data;
						}
					} else if (response.candidates?.[0]?.content?.parts) {
						for (const part of response.candidates[0].content.parts) if (part.inlineData?.mimeType?.includes("audio")) {
							hasAudioContent = true;
							response_audio_total += part.inlineData.data;
						}
					} else if (response.streamingCustomOp?.["type.googleapis.com/google.ai.generativelanguage.v1alpha.StreamingCustomOpOutput"]?.audioCompletionSignal) hasAudioStreamEnded = true;
					else if (!response.setupComplete && !response.serverContent && !response.toolCall && !response.realtimeInput && !response.candidates && !response.streamingCustomOp) {
						require_logger.logger.warn(`Received unhandled WebSocket message structure: ${JSON.stringify(response).substring(0, 200)}`);
						if (hasOutputTranscription && hasAudioContent && isAudioExpected && !hasAudioStreamEnded) {
							require_logger.logger.debug("Unknown message with transcription enabled - marking audio as complete");
							hasAudioStreamEnded = true;
							if (hasTextStreamEnded) try {
								await finalizeResponse();
							} catch (err) {
								require_logger.logger.error(`Error in finalizeResponse: ${err}`);
								safeResolve({ error: `Error finalizing response: ${err}` });
							}
						}
					}
				} catch (err) {
					require_logger.logger.error(`Failed to process WebSocket response: ${JSON.stringify(err)}`);
					ws.close();
					safeResolve({ error: `Failed to process WebSocket response: ${JSON.stringify(err)}` });
				}
			};
			ws.onerror = (err) => {
				require_logger.logger.error(`WebSocket error for model ${this.modelName}: ${JSON.stringify(err)}`);
				if (isNativeAudioModel) require_logger.logger.error(`Native audio model ${this.modelName} may not be available or may require different configuration`);
				clearTimeout(timeout);
				ws.close();
				safeResolve({ error: `WebSocket error for model ${this.modelName}: ${JSON.stringify(err)}` });
			};
			ws.onclose = (event) => {
				require_logger.logger.debug(`WebSocket connection closed. Code: ${event.code}, Reason: ${event.reason}, Clean: ${event.wasClean}`);
				if (statefulApi && !statefulApi.killed) statefulApi.kill("SIGTERM");
				clearTimeout(timeout);
				if (!isResolved) safeResolve({ error: `WebSocket connection closed unexpectedly. Code: ${event.code}, Reason: ${event.reason}` });
			};
		});
	}
	/**
	* Loads a function from an external file
	* @param fileRef The file reference in the format 'file://path/to/file:functionName'
	* @returns The loaded function
	*/
	async loadExternalFunction(fileRef) {
		const { filePath, functionName } = require_util.parseFileUrl(fileRef);
		try {
			const resolvedPath = path.default.resolve(require_logger.state.basePath || "", filePath);
			require_logger.logger.debug(`Loading function from ${resolvedPath}${functionName ? `:${functionName}` : ""}`);
			const requiredModule = await require_esm.importModule(resolvedPath, functionName);
			if (typeof requiredModule === "function") return requiredModule;
			else if (requiredModule && typeof requiredModule === "object" && functionName && functionName in requiredModule) {
				const fn = requiredModule[functionName];
				if (typeof fn === "function") return fn;
			}
			throw new Error(`Function callback malformed: ${filePath} must export ${functionName ? `a named function '${functionName}'` : "a function or have a default export as a function"}`);
		} catch (error) {
			throw new Error(`Error loading function from ${filePath}: ${error.message || String(error)}`);
		}
	}
	/**
	* Executes a function callback with proper error handling
	*/
	async executeFunctionCallback(functionName, args, callbacks = this.config.functionToolCallbacks) {
		try {
			const shouldUseSharedCache = callbacks === this.config.functionToolCallbacks;
			let callback = shouldUseSharedCache ? this.loadedFunctionCallbacks[functionName] : void 0;
			if (!callback) {
				const callbackRef = callbacks?.[functionName];
				if (callbackRef && typeof callbackRef === "string") {
					const callbackStr = callbackRef;
					if (callbackStr.startsWith("file://")) callback = await this.loadExternalFunction(callbackStr);
					else callback = new Function("return " + callbackStr)();
					if (shouldUseSharedCache && callback) this.loadedFunctionCallbacks[functionName] = callback;
				} else if (typeof callbackRef === "function") {
					callback = callbackRef;
					if (shouldUseSharedCache) this.loadedFunctionCallbacks[functionName] = callback;
				}
			}
			if (!callback) throw new Error(`No callback found for function '${functionName}'`);
			require_logger.logger.debug(`Executing function '${functionName}' with args: ${args}`);
			return await callback(args);
		} catch (error) {
			require_logger.logger.error(`Error executing function '${functionName}': ${error.message || String(error)}`);
			throw error;
		}
	}
};
//#endregion
exports.GoogleLiveProvider = GoogleLiveProvider;

//# sourceMappingURL=live-Pk4FSKN0.cjs.map