globalThis.process ??= {};
globalThis.process.env ??= {};
import { E as eq, T as and } from "./d1_DTqC4o_t.mjs";
import { A as getById } from "./crypto_CY2zBtG3.mjs";
import { d as translationValue } from "./db_DZJ1iBSP.mjs";
import { a as markFailed, i as listLanguages, o as markStale, r as isLocaleActive, s as setTranslation, t as getDefaultLocale } from "./dist_BYPDVqt8.mjs";
import { n as getEntityTranslationStatusFor } from "./dist_3A1NvALK.mjs";
//#region ../../packages/ai/dist/index.js
var AIError = class extends Error {};
var RateLimitError = class extends AIError {
	provider;
	cause;
	name = "RateLimitError";
	retryable = true;
	constructor(message, provider, cause) {
		super(message);
		this.provider = provider;
		this.cause = cause;
	}
};
var TransientError = class extends AIError {
	provider;
	cause;
	name = "TransientError";
	retryable = true;
	constructor(message, provider, cause) {
		super(message);
		this.provider = provider;
		this.cause = cause;
	}
};
var QuotaExhaustedError = class extends AIError {
	provider;
	cause;
	name = "QuotaExhaustedError";
	retryable = true;
	constructor(message, provider, cause) {
		super(message);
		this.provider = provider;
		this.cause = cause;
	}
};
var AuthError = class extends AIError {
	provider;
	cause;
	name = "AuthError";
	retryable = false;
	constructor(message, provider, cause) {
		super(message);
		this.provider = provider;
		this.cause = cause;
	}
};
var FatalError = class extends AIError {
	provider;
	cause;
	name = "FatalError";
	retryable = false;
	constructor(message, provider, cause) {
		super(message);
		this.provider = provider;
		this.cause = cause;
	}
};
var AllModelsFailedError = class extends FatalError {
	lastError;
	constructor(message, lastError) {
		super(message);
		this.lastError = lastError;
	}
};
var adapterFactories = {};
/** Wire adapter subpath makers without importing them from core (tree-shake safe). */
function setAdapterFactories(factories) {
	adapterFactories = {
		...adapterFactories,
		...factories
	};
}
function getAdapter(provider, creds) {
	const factory = adapterFactories[provider];
	if (!factory) throw new FatalError(`No adapter factory registered for "${provider}". Import @platform-modules/ai/${provider} and register its maker via setAdapterFactories.`, provider);
	return factory(creds);
}
function withTimeout(promise, ms) {
	let timeoutId;
	const timeoutPromise = new Promise((_, reject) => {
		timeoutId = setTimeout(() => {
			reject(new TransientError(`Request timed out after ${ms}ms`));
		}, ms);
	});
	return Promise.race([promise, timeoutPromise]).finally(() => {
		if (timeoutId !== void 0) clearTimeout(timeoutId);
	});
}
function isRetryableError(err) {
	return err instanceof AIError && err.retryable;
}
var ALLOWED_USAGE_KEYS = /* @__PURE__ */ new Set(["promptTokens", "completionTokens"]);
/**
* Runtime boundary guard — usage must carry token counts and NOTHING else.
* Allowlist (not a denylist): the spec mandates `{ promptTokens, completionTokens }`
* exactly, so any extra key — cost/price/credits, a novel currency name, or a nested
* accounting object — is a host-domain leak and is rejected. Cost is host-domain.
*/
function assertUsageBoundary(usage) {
	const record = usage;
	for (const key of Object.keys(record)) if (!ALLOWED_USAGE_KEYS.has(key)) throw new FatalError(`AIResponse.usage must carry only promptTokens/completionTokens — found host-domain field "${key}"`);
	if (typeof usage.promptTokens !== "number" || typeof usage.completionTokens !== "number") throw new FatalError("AIResponse.usage requires promptTokens and completionTokens");
	if (!Number.isFinite(usage.promptTokens) || usage.promptTokens < 0 || !Number.isFinite(usage.completionTokens) || usage.completionTokens < 0) throw new FatalError("AIResponse.usage token counts must be finite non-negative numbers");
}
async function executeWithFallback(req, chain, opts) {
	if (chain.length === 0) throw new AllModelsFailedError("All models failed");
	const timeoutMs = opts?.timeoutMs ?? 3e4;
	let lastError;
	for (const entry of chain) {
		const callReq = entry.model !== void 0 && entry.model !== req.model ? {
			...req,
			model: entry.model
		} : req;
		try {
			const response = await withTimeout(entry.adapter.call(callReq), timeoutMs);
			assertUsageBoundary(response.usage);
			return response;
		} catch (err) {
			if (isRetryableError(err) || opts?.continueOnNonRetryable === true && err instanceof AIError) {
				lastError = err;
				continue;
			}
			throw err;
		}
	}
	throw new AllModelsFailedError(`All models failed${lastError ? `: ${lastError.message}` : ""}`, lastError);
}
//#endregion
//#region ../../packages/i18n-translator/dist/worker-BIsZ1056.js
var I18nTranslatorError = class extends Error {
	code;
	name = "I18nTranslatorError";
	_tag = "I18nTranslatorError";
	constructor(code, message) {
		super(message);
		this.code = code;
	}
};
var UnknownLocaleError = class extends I18nTranslatorError {
	locale;
	code = "UNKNOWN_LOCALE";
	name = "UnknownLocaleError";
	constructor(locale) {
		super("UNKNOWN_LOCALE", `locale '${locale}' is not active or does not exist`);
		this.locale = locale;
	}
};
var SourceUnavailableError = class extends I18nTranslatorError {
	entityType;
	entityId;
	code = "SOURCE_UNAVAILABLE";
	name = "SourceUnavailableError";
	constructor(entityType, entityId) {
		super("SOURCE_UNAVAILABLE", `source text unavailable for ${entityType}/${entityId}`);
		this.entityType = entityType;
		this.entityId = entityId;
	}
};
var NoAdaptersConfiguredError = class extends I18nTranslatorError {
	code = "NO_ADAPTERS_CONFIGURED";
	name = "NoAdaptersConfiguredError";
	constructor() {
		super("NO_ADAPTERS_CONFIGURED", "no AI adapters are configured; register at least one adapter via setAdapterFactories");
	}
};
/**
* Returns a SHA-256 hex digest of the normalised source text.
* NFC normalisation and trim are applied before hashing, so purely whitespace-only
* source edits (leading/trailing spaces, equivalent Unicode sequences) produce
* the same hash and are intentionally treated as unchanged.
*/
var encoder = new TextEncoder();
async function hashSource(text) {
	const normalized = text.normalize("NFC").trim();
	const data = encoder.encode(normalized);
	const digest = await crypto.subtle.digest("SHA-256", data);
	return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
}
var PROVIDER_TRY_ORDER = [
	"openai-compat",
	"anthropic",
	"google",
	"workers-ai"
];
var PROVIDER_DEFAULT_MODEL = {
	"openai-compat": "gpt-4o-mini",
	anthropic: "claude-haiku-4-5",
	google: "gemini-1.5-flash",
	"workers-ai": "@cf/meta/llama-3.1-8b-instruct"
};
function resolveFallbackChain(modelId) {
	const resolvedModelId = modelId || void 0;
	const chain = [];
	for (const provider of PROVIDER_TRY_ORDER) try {
		chain.push({
			adapter: getAdapter(provider, {}),
			model: PROVIDER_DEFAULT_MODEL[provider]
		});
	} catch {
		continue;
	}
	if (chain.length === 0) throw new NoAdaptersConfiguredError();
	if (resolvedModelId) chain[0].model = resolvedModelId;
	return chain;
}
function buildTranslationPrompt(sourceLocale, targetLocale, fields) {
	const fieldLines = fields.map((f) => `- ${f.fieldKey}: ${f.value}`).join("\n");
	return [
		`Translate the following fields from ${sourceLocale} to ${targetLocale}.`,
		"Return ONLY valid JSON with the same field keys and translated string values.",
		"Fields:",
		fieldLines
	].join("\n");
}
/**
* Strip markdown code fences and/or prose preamble before JSON.parse.
* Models (gpt-4o-mini, gemini-1.5-flash, llama) routinely emit ```json
* wrappers or leading prose — raw JSON.parse throws on those, marking the
* entire field-batch FAILED. This extracts the JSON object robustly.
*/
function extractJson(content) {
	const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/);
	if (fenced) return fenced[1].trim();
	const start = content.indexOf("{");
	const end = content.lastIndexOf("}");
	if (start !== -1 && end !== -1 && end > start) return content.slice(start, end + 1);
	return content.trim();
}
/** Maximum allowed length (chars) for a single translated field value. */
var MAX_TRANSLATION_FIELD_LENGTH = 5e4;
function parseTranslationResponse(content, fieldKeys) {
	const parsed = JSON.parse(extractJson(content));
	if (typeof parsed !== "object" || parsed === null) throw new Error("translation response is not a JSON object");
	const record = parsed;
	const out = {};
	for (const fieldKey of fieldKeys) {
		const value = record[fieldKey];
		if (typeof value !== "string") throw new Error(`missing or invalid translation for field '${fieldKey}'`);
		if (value.length === 0) throw new Error(`empty translation returned for field '${fieldKey}'`);
		if (value.length > MAX_TRANSLATION_FIELD_LENGTH) throw new Error(`translation for field '${fieldKey}' exceeds maximum length (${value.length} chars)`);
		out[fieldKey] = value;
	}
	return out;
}
async function translateEntity(q, args) {
	for (const locale of args.targetLocales) if (!await isLocaleActive(q, locale)) throw new UnknownLocaleError(locale);
	let sourceLocale;
	let sourceFields;
	try {
		const sourceResult = await args.source.getSourceText({
			entityType: args.entityType,
			entityId: args.entityId,
			fields: args.fields
		});
		sourceLocale = sourceResult.locale;
		sourceFields = sourceResult.fields;
	} catch {
		throw new SourceUnavailableError(args.entityType, args.entityId);
	}
	const fieldHashes = /* @__PURE__ */ new Map();
	for (const fieldKey of args.fields) if (fieldKey in sourceFields) fieldHashes.set(fieldKey, await hashSource(sourceFields[fieldKey]));
	const skipUnchanged = args.skipUnchanged !== false;
	const normalizedModelId = args.modelId || null;
	const results = [];
	for (const targetLocale of args.targetLocales) {
		const fieldOutcomes = [];
		const toTranslate = [];
		if (targetLocale === sourceLocale) {
			for (const fieldKey of args.fields) fieldOutcomes.push({
				fieldKey,
				outcome: "SKIPPED"
			});
			results.push({
				locale: targetLocale,
				fields: fieldOutcomes
			});
			continue;
		}
		for (const fieldKey of args.fields) {
			if (!(fieldKey in sourceFields)) {
				fieldOutcomes.push({
					fieldKey,
					outcome: "SKIPPED"
				});
				continue;
			}
			const sourceHash = fieldHashes.get(fieldKey);
			const sourceValue = sourceFields[fieldKey];
			if (sourceValue === "") {
				fieldOutcomes.push({
					fieldKey,
					outcome: "SKIPPED"
				});
				continue;
			}
			if (skipUnchanged) {
				const [existing] = await q.select({
					sourceHash: translationValue.sourceHash,
					status: translationValue.status
				}).from(translationValue).where(and(eq(translationValue.entityType, args.entityType), eq(translationValue.entityId, args.entityId), eq(translationValue.fieldKey, fieldKey), eq(translationValue.locale, targetLocale))).limit(1);
				if (existing?.sourceHash === sourceHash && existing.status === "OK") {
					fieldOutcomes.push({
						fieldKey,
						outcome: "SKIPPED"
					});
					continue;
				}
			}
			toTranslate.push({
				fieldKey,
				value: sourceValue,
				sourceHash
			});
		}
		if (toTranslate.length === 0) {
			results.push({
				locale: targetLocale,
				fields: fieldOutcomes
			});
			continue;
		}
		const chain = resolveFallbackChain(args.modelId);
		const reqModel = (args.modelId || void 0) ?? chain[0].model;
		const prompt = buildTranslationPrompt(sourceLocale, targetLocale, toTranslate.map((f) => ({
			fieldKey: f.fieldKey,
			value: f.value
		})));
		let translated;
		try {
			translated = parseTranslationResponse((await executeWithFallback({
				model: reqModel,
				messages: [{
					role: "user",
					content: prompt
				}],
				maxTokens: 8192,
				temperature: 0
			}, [...chain], { continueOnNonRetryable: true })).content, toTranslate.map((f) => f.fieldKey));
		} catch {
			for (const item of toTranslate) {
				await markFailed(q, {
					entityType: args.entityType,
					entityId: args.entityId,
					fieldKey: item.fieldKey,
					locale: targetLocale,
					sourceHash: item.sourceHash,
					modelId: normalizedModelId
				});
				fieldOutcomes.push({
					fieldKey: item.fieldKey,
					outcome: "FAILED"
				});
			}
			results.push({
				locale: targetLocale,
				fields: fieldOutcomes
			});
			continue;
		}
		for (const item of toTranslate) try {
			await setTranslation(q, {
				entityType: args.entityType,
				entityId: args.entityId,
				fieldKey: item.fieldKey,
				locale: targetLocale,
				value: translated[item.fieldKey],
				sourceHash: item.sourceHash,
				modelId: normalizedModelId,
				manualOverride: false
			});
			fieldOutcomes.push({
				fieldKey: item.fieldKey,
				outcome: "OK"
			});
		} catch {
			await markFailed(q, {
				entityType: args.entityType,
				entityId: args.entityId,
				fieldKey: item.fieldKey,
				locale: targetLocale,
				sourceHash: item.sourceHash,
				modelId: normalizedModelId
			});
			fieldOutcomes.push({
				fieldKey: item.fieldKey,
				outcome: "FAILED"
			});
		}
		results.push({
			locale: targetLocale,
			fields: fieldOutcomes
		});
	}
	return { results };
}
//#endregion
//#region ../../packages/ai/dist/mock.js
var SENTINEL_RE = /@@MOCK:([A-Z_]+)(?::([^@]*))?@@/;
var THROW_DIRECTIVES = /* @__PURE__ */ new Set([
	"ERROR",
	"RATELIMIT",
	"TRANSIENT",
	"QUOTA",
	"AUTH"
]);
function parseSentinel(text) {
	const m = SENTINEL_RE.exec(text);
	if (!m) return null;
	return {
		kind: m[1],
		arg: m[2]?.trim() || void 0
	};
}
function modelDirective(model) {
	if (!model.startsWith("TEST")) return null;
	const suffix = model.slice(4).replace(/^-/, "");
	if (suffix === "RATELIMIT") return { kind: "RATELIMIT" };
	if (suffix === "TRANSIENT") return { kind: "TRANSIENT" };
	if (suffix === "QUOTA") return { kind: "QUOTA" };
	if (suffix === "AUTH") return { kind: "AUTH" };
	if (suffix === "ERROR") return { kind: "ERROR" };
	return null;
}
function extractMessageText(messages) {
	return messages.map((message) => typeof message.content === "string" ? message.content : message.content.filter((block) => block.type === "text").map((block) => block.text).join("")).join("\n");
}
function resolveMockDirective(req) {
	return parseSentinel(extractMessageText(req.messages)) ?? modelDirective(req.model);
}
function throwForMockDirective(kind, arg) {
	const msg = arg ?? `[TEST] mock ${kind}`;
	if (kind === "RATELIMIT") throw new RateLimitError(msg, "mock");
	if (kind === "TRANSIENT") throw new TransientError(msg, "mock");
	if (kind === "QUOTA") throw new QuotaExhaustedError(msg, "mock");
	if (kind === "AUTH") throw new AuthError(msg, "mock");
	throw new FatalError(msg, "mock");
}
function makeMockAdapter() {
	return {
		provider: "mock",
		supportsImage: false,
		async call(req) {
			const directive = resolveMockDirective(req);
			if (directive && THROW_DIRECTIVES.has(directive.kind)) throwForMockDirective(directive.kind, directive.arg);
			return {
				content: "[TEST] mock ok",
				model: req.model,
				provider: "mock",
				usage: {
					promptTokens: 1,
					completionTokens: 1
				}
			};
		}
	};
}
//#endregion
//#region ../../packages/ai/dist/openai-compat.js
function mapMessagesToOpenAi(messages) {
	return messages.map((message) => ({
		role: message.role,
		content: mapContentToOpenAi(message.content)
	}));
}
function mapContentToOpenAi(content) {
	if (typeof content === "string") return content;
	return content.map((block) => {
		if (block.type === "text") return {
			type: "text",
			text: block.text
		};
		const img = block.image;
		return {
			type: "image_url",
			image_url: { url: `data:${img.mediaType};base64,${img.data}` }
		};
	});
}
function mapOpenAiResponse(body, provider, model) {
	const choice = body.choices[0];
	return {
		content: choice?.message.content ?? "",
		model: body.model || model,
		provider,
		usage: {
			promptTokens: body.usage?.prompt_tokens ?? 0,
			completionTokens: body.usage?.completion_tokens ?? 0
		},
		finishReason: choice?.finish_reason ?? void 0
	};
}
function classifyOpenAiCompatError(status, message, provider) {
	if (status === 429) {
		if (/quota/i.test(message)) return new QuotaExhaustedError(message, provider);
		return new RateLimitError(message, provider);
	}
	if (status === 401 || status === 403) return new AuthError(message, provider);
	if (status >= 500) return new TransientError(message, provider);
	return new FatalError(message, provider);
}
function makeOpenAiCompatAdapter(creds) {
	const baseUrl = creds.baseUrl ?? "https://api.openai.com/v1";
	const fetchImpl = creds.fetch ?? fetch;
	return {
		provider: "openai-compat",
		supportsImage: true,
		async call(req) {
			const payload = {
				model: req.model,
				messages: mapMessagesToOpenAi(req.messages),
				...req.maxTokens !== void 0 ? { max_tokens: req.maxTokens } : {},
				...req.temperature !== void 0 ? { temperature: req.temperature } : {},
				...req.stop !== void 0 ? { stop: req.stop } : {}
			};
			const response = await fetchImpl(`${baseUrl}/chat/completions`, {
				method: "POST",
				headers: {
					Authorization: `Bearer ${creds.apiKey}`,
					"Content-Type": "application/json"
				},
				body: JSON.stringify(payload)
			});
			const text = await response.text();
			let body;
			try {
				body = JSON.parse(text);
			} catch {
				throw new TransientError(`Invalid JSON from provider: ${text.slice(0, 120)}`, "openai-compat");
			}
			if (!response.ok) {
				const message = typeof body === "object" && body !== null && "error" in body && typeof body.error?.message === "string" ? body.error.message : text;
				throw classifyOpenAiCompatError(response.status, message, "openai-compat");
			}
			return mapOpenAiResponse(body, "openai-compat", req.model);
		}
	};
}
//#endregion
//#region ../../packages/ai/dist/anthropic.js
function extractAnthropicSystem(messages) {
	const systemParts = messages.filter((m) => m.role === "system").map((m) => typeof m.content === "string" ? m.content : m.content.filter((b) => b.type === "text").map((b) => b.text).join(""));
	return systemParts.length > 0 ? systemParts.join("\n\n") : void 0;
}
function mapMessagesToAnthropic(messages) {
	return messages.filter((m) => m.role !== "system").map((message) => ({
		role: message.role === "assistant" ? "assistant" : "user",
		content: mapContentToAnthropic(message.content)
	}));
}
function mapContentToAnthropic(content) {
	if (typeof content === "string") return content;
	return content.map((block) => {
		if (block.type === "text") return {
			type: "text",
			text: block.text
		};
		const img = block.image;
		return {
			type: "image",
			source: {
				type: "base64",
				media_type: img.mediaType,
				data: img.data
			}
		};
	});
}
function mapAnthropicResponse(body, provider, model) {
	return {
		content: body.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(""),
		model: body.model || model,
		provider,
		usage: {
			promptTokens: body.usage.input_tokens,
			completionTokens: body.usage.output_tokens
		},
		finishReason: body.stop_reason ?? void 0
	};
}
function classifyAnthropicError(status, message, provider) {
	if (status === 429) {
		if (/quota/i.test(message)) return new QuotaExhaustedError(message, provider);
		return new RateLimitError(message, provider);
	}
	if (status === 401 || status === 403) return new AuthError(message, provider);
	if (status >= 500) return new TransientError(message, provider);
	return new FatalError(message, provider);
}
function makeAnthropicAdapter(creds) {
	const baseUrl = creds.baseUrl ?? "https://api.anthropic.com/v1";
	const fetchImpl = creds.fetch ?? fetch;
	return {
		provider: "anthropic",
		supportsImage: true,
		async call(req) {
			const system = extractAnthropicSystem(req.messages);
			const payload = {
				model: req.model,
				max_tokens: req.maxTokens ?? 1024,
				messages: mapMessagesToAnthropic(req.messages),
				...system ? { system } : {},
				...req.temperature !== void 0 ? { temperature: req.temperature } : {},
				...req.stop !== void 0 ? { stop_sequences: Array.isArray(req.stop) ? req.stop : [req.stop] } : {}
			};
			const response = await fetchImpl(`${baseUrl}/messages`, {
				method: "POST",
				headers: {
					"x-api-key": creds.apiKey,
					"anthropic-version": "2023-06-01",
					"Content-Type": "application/json"
				},
				body: JSON.stringify(payload)
			});
			const text = await response.text();
			let body;
			try {
				body = JSON.parse(text);
			} catch {
				throw new TransientError(`Invalid JSON from provider: ${text.slice(0, 120)}`, "anthropic");
			}
			if (!response.ok) {
				const message = typeof body === "object" && body !== null && "error" in body && typeof body.error?.message === "string" ? body.error.message : text;
				throw classifyAnthropicError(response.status, message, "anthropic");
			}
			return mapAnthropicResponse(body, "anthropic", req.model);
		}
	};
}
//#endregion
//#region ../../packages/ai/dist/google.js
function extractGeminiSystem(messages) {
	const systemParts = messages.filter((m) => m.role === "system").map((m) => typeof m.content === "string" ? m.content : m.content.filter((b) => b.type === "text").map((b) => b.text).join(""));
	return systemParts.length > 0 ? systemParts.join("\n\n") : void 0;
}
function mapContentToGemini(content) {
	if (typeof content === "string") return content;
	return content.map((block) => {
		if (block.type === "text") return { text: block.text };
		const img = block.image;
		return { inlineData: {
			mimeType: img.mediaType,
			data: img.data
		} };
	});
}
function mapMessagesToGemini(messages) {
	return messages.filter((m) => m.role !== "system").map((message) => {
		const mapped = mapContentToGemini(message.content);
		const parts = typeof mapped === "string" ? [{ text: mapped }] : mapped;
		return {
			role: message.role === "assistant" ? "model" : "user",
			parts
		};
	});
}
function mapGeminiResponse(body, provider, model) {
	return {
		content: body.text?.trim() ?? "",
		model,
		provider,
		usage: {
			promptTokens: body.usageMetadata?.promptTokenCount ?? 0,
			completionTokens: body.usageMetadata?.candidatesTokenCount ?? 0
		},
		finishReason: void 0
	};
}
function classifyGoogleError(status, message, provider) {
	if (status === 429) {
		if (/RESOURCE_EXHAUSTED|quota/i.test(message)) return new QuotaExhaustedError(message, provider);
		return new RateLimitError(message, provider);
	}
	if (status === 401 || status === 403) return new AuthError(message, provider);
	if (status !== void 0 && status >= 500) return new TransientError(message, provider);
	return new FatalError(message, provider);
}
function mapSdkError(err, provider) {
	const status = err.status;
	const code = err.code;
	const msg = err.message ?? String(err);
	if (status === 429) {
		if (/RESOURCE_EXHAUSTED|quota/i.test(msg)) throw new QuotaExhaustedError(msg, provider);
		throw new RateLimitError(msg, provider);
	}
	if (status === 401 || status === 403) throw new AuthError(msg, provider);
	if (code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ENOTFOUND" || status !== void 0 && status >= 500) throw new TransientError(msg, provider);
	throw classifyGoogleError(status, msg, provider);
}
async function createGoogleClient(creds) {
	if (creds.createClient) return creds.createClient(creds.apiKey);
	const { GoogleGenAI } = await import("./web_CJpi52Cu.mjs");
	return new GoogleGenAI({ apiKey: creds.apiKey });
}
function makeGoogleAdapter(creds) {
	return {
		provider: "google",
		supportsImage: true,
		async call(req) {
			const client = await createGoogleClient(creds);
			const system = extractGeminiSystem(req.messages);
			const contents = mapMessagesToGemini(req.messages);
			try {
				return mapGeminiResponse(await client.models.generateContent({
					model: req.model,
					contents,
					config: {
						...system ? { systemInstruction: system } : {},
						...req.maxTokens !== void 0 ? { maxOutputTokens: req.maxTokens } : {},
						...req.temperature !== void 0 ? { temperature: req.temperature } : {},
						...req.stop !== void 0 ? { stopSequences: Array.isArray(req.stop) ? req.stop : [req.stop] } : {}
					}
				}), "google", req.model);
			} catch (err) {
				mapSdkError(err, "google");
			}
		}
	};
}
//#endregion
//#region src/lib/content-translation.ts
var CONTENT_TRANSLATABLE_FIELDS = ["title", "body"];
function createTranslationMockAdapter() {
	return {
		...makeMockAdapter(),
		async call(req) {
			const text = req.messages.map((message) => typeof message.content === "string" ? message.content : message.content.filter((block) => block.type === "text").map((block) => block.text).join("")).join("\n");
			const targetLocale = text.match(/to ([a-z-]+)/i)?.[1] ?? "translated";
			const result = {};
			for (const match of text.matchAll(/^- (\w+): (.*)$/gm)) result[match[1]] = `[${targetLocale}] ${match[2]}`;
			return {
				content: JSON.stringify(result),
				model: req.model,
				provider: "mock",
				usage: {
					promptTokens: 1,
					completionTokens: 1
				}
			};
		}
	};
}
function mockTranslationsEnabled(env) {
	return env.MOD_CMS_TRANSLATION_MOCK === "1" || env.MOD_CMS_TRANSLATION_MOCK === "true" || globalThis.process?.env?.MOD_CMS_TRANSLATION_MOCK === "1";
}
function ensureTranslationAdapters(env) {
	const factories = {};
	if (mockTranslationsEnabled(env)) factories["openai-compat"] = () => createTranslationMockAdapter();
	if (env.OPENAI_API_KEY) factories["openai-compat"] = () => makeOpenAiCompatAdapter({
		apiKey: env.OPENAI_API_KEY,
		...env.OPENAI_BASE_URL ? { baseUrl: env.OPENAI_BASE_URL } : {}
	});
	if (env.ANTHROPIC_API_KEY) factories.anthropic = () => makeAnthropicAdapter({
		apiKey: env.ANTHROPIC_API_KEY,
		...env.ANTHROPIC_BASE_URL ? { baseUrl: env.ANTHROPIC_BASE_URL } : {}
	});
	if (env.GOOGLE_API_KEY) factories.google = () => makeGoogleAdapter({ apiKey: env.GOOGLE_API_KEY });
	setAdapterFactories(factories);
}
function emptyStatusMap(entityIds) {
	return new Map(entityIds.map((entityId) => [entityId, {}]));
}
async function resolveContentTargetLocales(db) {
	return (await listLanguages(db, { activeOnly: true })).filter((language) => language.isActive && !language.isDefault).map((language) => language.code);
}
function createContentSourceProvider(db, actor) {
	return { async getSourceText({ entityId, fields }) {
		const locale = await getDefaultLocale(db);
		const entry = await getById(db, entityId, actor);
		if (entry == null) throw new Error(`content entry not found: id=${entityId}`);
		const sourceFields = {};
		for (const field of fields) if (field === "title" || field === "body") sourceFields[field] = entry[field];
		return {
			locale,
			fields: sourceFields
		};
	} };
}
async function translateContentEntity(db, actor, entityId) {
	return translateEntity(db, {
		entityType: "content",
		entityId,
		fields: CONTENT_TRANSLATABLE_FIELDS,
		targetLocales: await resolveContentTargetLocales(db),
		source: createContentSourceProvider(db, actor)
	});
}
async function markContentTranslationsStale(db, previous, current) {
	if (previous == null) return;
	const fieldHashes = {};
	for (const field of CONTENT_TRANSLATABLE_FIELDS) if (previous[field] !== current[field]) fieldHashes[field] = await hashSource(current[field]);
	if (Object.keys(fieldHashes).length === 0) return;
	await markStale(db, {
		entityType: "content",
		entityId: current.id,
		fieldHashes
	});
}
async function getContentTranslationStatusesFor(db, entityIds) {
	if (entityIds.length === 0) return /* @__PURE__ */ new Map();
	const nonDefaultLocales = (await listLanguages(db, { activeOnly: true })).filter((language) => !language.isDefault).map((language) => language.code);
	if (nonDefaultLocales.length === 0) return emptyStatusMap(entityIds);
	const result = emptyStatusMap(entityIds);
	for (const locale of nonDefaultLocales) {
		const statuses = await getEntityTranslationStatusFor(db, {
			entityType: "content",
			entityIds,
			locale,
			fields: CONTENT_TRANSLATABLE_FIELDS
		});
		for (const entityId of entityIds) result.get(entityId)[locale] = statuses.get(entityId) ?? "missing";
	}
	return result;
}
//#endregion
export { translateContentEntity as i, getContentTranslationStatusesFor as n, markContentTranslationsStale as r, ensureTranslationAdapters as t };
