"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileSystemSceneJobArtifactStore = void 0;
/**
 * Out-of-band storage for Smart Scene job payloads.
 *
 * Smart reports persist only bounded projections in SceneReport JSON. When a
 * projection omits rows, the full job payload is written here and referenced by
 * jobs[].result.projection.artifactRef so the projection never points at lost
 * evidence.
 */
const crypto_1 = __importDefault(require("crypto"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
class FileSystemSceneJobArtifactStore {
    constructor(artifactDir) {
        this.artifactDir = artifactDir;
    }
    async save(payload) {
        await promises_1.default.mkdir(this.artifactDir, { recursive: true });
        const body = `${JSON.stringify(payload, null, 2)}\n`;
        const checksum = crypto_1.default.createHash('sha256').update(body).digest('hex');
        const sizeBytes = Buffer.byteLength(body, 'utf8');
        const artifactId = `scene-job-${sanitizeId(payload.jobId)}-${checksum.slice(0, 12)}`;
        const target = this.artifactPath(artifactId);
        const tmp = path_1.default.join(this.artifactDir, `${artifactId}.${process.pid}.${Date.now()}.tmp`);
        await promises_1.default.writeFile(tmp, body, 'utf8');
        await promises_1.default.rename(tmp, target);
        return {
            artifactId,
            artifactType: 'scene_job_envelopes',
            sizeBytes,
            checksum,
        };
    }
    async load(artifactId) {
        try {
            const raw = await promises_1.default.readFile(this.artifactPath(artifactId), 'utf8');
            return JSON.parse(raw);
        }
        catch (err) {
            if (err.code === 'ENOENT')
                return null;
            throw err;
        }
    }
    artifactPath(artifactId) {
        if (!/^scene-job-[a-zA-Z0-9_.-]+$/.test(artifactId)) {
            throw new Error(`Invalid scene job artifact id: ${artifactId}`);
        }
        return path_1.default.join(this.artifactDir, `${artifactId}.json`);
    }
}
exports.FileSystemSceneJobArtifactStore = FileSystemSceneJobArtifactStore;
function sanitizeId(value) {
    const safe = value.replace(/[^a-zA-Z0-9_.-]+/g, '-').slice(0, 120);
    return safe || 'job';
}
//# sourceMappingURL=sceneJobArtifactStore.js.map