const require_rolldown_runtime = require("./rolldown-runtime-D_mwlA32.cjs");
const require_logger = require("./logger-DuG3amVa.cjs");
const require_tables = require("./tables-S91SJifa.cjs");
let node_fs_promises = require("node:fs/promises");
node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises, 1);
let node_crypto = require("node:crypto");
let node_fs = require("node:fs");
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
let node_path = require("node:path");
node_path = require_rolldown_runtime.__toESM(node_path, 1);
let drizzle_orm = require("drizzle-orm");
//#region src/blobs/constants.ts
const BLOB_MIN_SIZE = 1024;
const BLOB_MAX_SIZE = 52428800;
const BLOB_SCHEME = "promptfoo://blob/";
const DEFAULT_FILESYSTEM_SUBDIR = "blobs";
//#endregion
//#region src/blobs/filesystemProvider.ts
const BLOB_HASH_REGEX = /^[a-f0-9]{64}$/i;
function computeHash(data) {
	return (0, node_crypto.createHash)("sha256").update(data).digest("hex");
}
function buildUri(hash) {
	return `${BLOB_SCHEME}${hash}`;
}
var FilesystemBlobStorageProvider = class {
	providerId = "filesystem";
	basePath;
	constructor(config) {
		const defaultBase = node_path.join(require_logger.getConfigDirectoryPath(true), DEFAULT_FILESYSTEM_SUBDIR);
		this.basePath = node_path.resolve(config?.basePath || defaultBase);
		this.ensureDirectory();
	}
	ensureDirectory() {
		if (!node_fs.existsSync(this.basePath)) {
			node_fs.mkdirSync(this.basePath, { recursive: true });
			require_logger.logger.debug("[BlobFS] Created blob directory", { basePath: this.basePath });
		}
	}
	assertValidHash(hash) {
		if (!BLOB_HASH_REGEX.test(hash)) throw new Error(`[BlobFS] Invalid blob hash: "${hash}"`);
	}
	resolvePathInBase(unsafePath) {
		const targetPath = node_path.isAbsolute(unsafePath) ? node_path.resolve(unsafePath) : node_path.resolve(this.basePath, unsafePath);
		const safeBase = node_path.resolve(this.basePath) + node_path.sep;
		if (!targetPath.startsWith(safeBase)) throw new Error("[BlobFS] Path traversal attempt detected");
		return targetPath;
	}
	hashToPath(hash) {
		this.assertValidHash(hash);
		const dirRelative = node_path.join(hash.slice(0, 2), hash.slice(2, 4));
		const fileRelative = node_path.join(dirRelative, hash);
		return this.resolvePathInBase(fileRelative);
	}
	async ensureHashDir(hash) {
		this.assertValidHash(hash);
		const dirRelative = node_path.join(hash.slice(0, 2), hash.slice(2, 4));
		const dirPath = this.resolvePathInBase(dirRelative);
		await node_fs_promises.mkdir(dirPath, { recursive: true });
	}
	metadataPath(filePath) {
		return `${filePath}.meta.json`;
	}
	async store(data, mimeType) {
		const hash = computeHash(data);
		await this.ensureHashDir(hash);
		const filePath = this.hashToPath(hash);
		try {
			await node_fs_promises.access(filePath);
			const meta = await this.readMetadata(filePath);
			return {
				ref: this.buildRef(hash, meta?.mimeType ?? mimeType, meta?.sizeBytes ?? data.length, meta?.provider ?? this.providerId),
				deduplicated: true
			};
		} catch {}
		await node_fs_promises.writeFile(filePath, data);
		const metadata = {
			mimeType,
			sizeBytes: data.length,
			createdAt: (/* @__PURE__ */ new Date()).toISOString(),
			provider: this.providerId,
			key: filePath
		};
		await node_fs_promises.writeFile(this.metadataPath(filePath), JSON.stringify(metadata, null, 2));
		return {
			ref: this.buildRef(hash, mimeType, data.length, this.providerId),
			deduplicated: false
		};
	}
	async getByHash(hash) {
		const filePath = this.hashToPath(hash);
		let data;
		try {
			data = await node_fs_promises.readFile(filePath);
		} catch (error) {
			if (error.code === "ENOENT") throw new Error(`Blob not found: ${hash}`);
			throw error;
		}
		const metadata = await this.readMetadata(filePath) || {
			mimeType: "application/octet-stream",
			sizeBytes: data.length,
			createdAt: (/* @__PURE__ */ new Date()).toISOString(),
			provider: this.providerId,
			key: filePath
		};
		return {
			data,
			metadata
		};
	}
	async exists(hash) {
		try {
			const filePath = this.hashToPath(hash);
			await node_fs_promises.access(filePath);
			return true;
		} catch {
			return false;
		}
	}
	async deleteByHash(hash) {
		try {
			const filePath = this.hashToPath(hash);
			const metaPath = this.metadataPath(filePath);
			try {
				await node_fs_promises.unlink(filePath);
			} catch (error) {
				if (error.code !== "ENOENT") throw error;
			}
			try {
				await node_fs_promises.unlink(metaPath);
			} catch (error) {
				if (error.code !== "ENOENT") throw error;
			}
		} catch {}
	}
	async getUrl(_hash, _expiresInSeconds) {
		return null;
	}
	buildRef(hash, mimeType, sizeBytes, provider) {
		return {
			uri: buildUri(hash),
			hash,
			mimeType,
			sizeBytes,
			provider
		};
	}
	async readMetadata(filePath) {
		const safeFilePath = this.resolvePathInBase(filePath);
		const metaPath = this.metadataPath(safeFilePath);
		try {
			const raw = await node_fs_promises.readFile(metaPath, "utf8");
			return JSON.parse(raw);
		} catch (error) {
			if (error.code === "ENOENT") return null;
			require_logger.logger.warn("[BlobFS] Failed to read metadata", { error });
			return null;
		}
	}
};
//#endregion
//#region src/blobs/index.ts
var blobs_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
	BLOB_MAX_SIZE: () => BLOB_MAX_SIZE,
	BLOB_MIN_SIZE: () => BLOB_MIN_SIZE,
	BLOB_SCHEME: () => BLOB_SCHEME,
	getBlobByHash: () => getBlobByHash,
	getBlobStorageProvider: () => getBlobStorageProvider,
	recordBlobReference: () => recordBlobReference,
	storeBlob: () => storeBlob
});
let defaultProvider = null;
function createDefaultProvider() {
	return new FilesystemBlobStorageProvider();
}
function getBlobStorageProvider() {
	if (!defaultProvider) {
		defaultProvider = createDefaultProvider();
		require_logger.logger.debug("[BlobStorage] Initialized provider", { provider: defaultProvider.providerId });
	}
	return defaultProvider;
}
async function storeBlob(data, mimeType, refContext) {
	const provider = getBlobStorageProvider();
	const result = await provider.store(data, mimeType);
	const db = await require_tables.getDb();
	try {
		await db.transaction(async (tx) => {
			await tx.insert(require_tables.blobAssetsTable).values({
				hash: result.ref.hash,
				sizeBytes: result.ref.sizeBytes,
				mimeType: result.ref.mimeType,
				provider: result.ref.provider
			}).onConflictDoNothing().run();
			if (refContext?.evalId) await tx.insert(require_tables.blobReferencesTable).values({
				id: (0, node_crypto.randomUUID)(),
				blobHash: result.ref.hash,
				evalId: refContext.evalId,
				testIdx: refContext.testIdx,
				promptIdx: refContext.promptIdx,
				location: refContext.location,
				kind: refContext.kind
			}).onConflictDoNothing().run();
		});
	} catch (error) {
		try {
			await provider.deleteByHash(result.ref.hash);
		} catch (cleanupError) {
			require_logger.logger.warn("[BlobStorage] Failed to rollback blob after DB error", {
				error: cleanupError,
				hash: result.ref.hash
			});
		}
		throw error;
	}
	return result;
}
async function getBlobByHash(hash) {
	return getBlobStorageProvider().getByHash(hash);
}
async function recordBlobReference(hash, refContext) {
	if (!refContext.evalId) return;
	if (!await getBlobStorageProvider().exists(hash).catch(() => false)) {
		require_logger.logger.debug("[BlobStorage] Attempted to record reference for missing blob", {
			hash,
			evalId: refContext.evalId,
			location: refContext.location
		});
		return;
	}
	const db = await require_tables.getDb();
	if (await db.select({ id: require_tables.blobReferencesTable.id }).from(require_tables.blobReferencesTable).where((0, drizzle_orm.and)((0, drizzle_orm.eq)(require_tables.blobReferencesTable.blobHash, hash), (0, drizzle_orm.eq)(require_tables.blobReferencesTable.evalId, refContext.evalId))).get()) return;
	await db.insert(require_tables.blobReferencesTable).values({
		id: (0, node_crypto.randomUUID)(),
		blobHash: hash,
		evalId: refContext.evalId,
		testIdx: refContext.testIdx,
		promptIdx: refContext.promptIdx,
		location: refContext.location,
		kind: refContext.kind
	}).run();
}
//#endregion
Object.defineProperty(exports, "BLOB_MAX_SIZE", {
	enumerable: true,
	get: function() {
		return BLOB_MAX_SIZE;
	}
});
Object.defineProperty(exports, "BLOB_MIN_SIZE", {
	enumerable: true,
	get: function() {
		return BLOB_MIN_SIZE;
	}
});
Object.defineProperty(exports, "BLOB_SCHEME", {
	enumerable: true,
	get: function() {
		return BLOB_SCHEME;
	}
});
Object.defineProperty(exports, "blobs_exports", {
	enumerable: true,
	get: function() {
		return blobs_exports;
	}
});
Object.defineProperty(exports, "getBlobByHash", {
	enumerable: true,
	get: function() {
		return getBlobByHash;
	}
});
Object.defineProperty(exports, "recordBlobReference", {
	enumerable: true,
	get: function() {
		return recordBlobReference;
	}
});
Object.defineProperty(exports, "storeBlob", {
	enumerable: true,
	get: function() {
		return storeBlob;
	}
});

//# sourceMappingURL=blobs-BKxNc-PT.cjs.map