File size: 2,408 Bytes
c6129e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { promises as fs } from "node:fs";
import path from "node:path";

import type { GenerationReferenceImage } from "@/lib/generation-types";
import {
  getImageToCodeReference,
  type TaskContext,
  type TaskContextById,
  type TaskId,
} from "@/lib/tasks";

const MIME_BY_EXTENSION: Record<string, string> = {
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".webp": "image/webp",
  ".gif": "image/gif",
  ".svg": "image/svg+xml",
};

function normalizePublicAssetPath(assetPath: string): string | null {
  const stripped = assetPath.startsWith("/") ? assetPath.slice(1) : assetPath;
  const normalized = path.posix.normalize(stripped);

  if (!normalized || normalized === "." || normalized.startsWith("../")) {
    return null;
  }

  return normalized;
}

function resolveImageMimeType(assetPath: string): string | null {
  const extension = path.extname(assetPath).toLowerCase();
  return MIME_BY_EXTENSION[extension] ?? null;
}

async function readAssetFromPublic(relativeAssetPath: string): Promise<Buffer> {
  const candidateRoots = [
    path.resolve(process.env.PROJECT_ROOT ?? process.cwd()),
    path.resolve(process.cwd()),
  ];
  const uniqueRoots = [...new Set(candidateRoots)];
  const segments = relativeAssetPath.split("/").filter(Boolean);

  for (const root of uniqueRoots) {
    const absoluteAssetPath = path.join(root, "public", ...segments);
    try {
      return await fs.readFile(absoluteAssetPath);
    } catch (error) {
      const code = (error as NodeJS.ErrnoException).code;
      if (code === "ENOENT") {
        continue;
      }

      throw error;
    }
  }

  throw new Error(`Reference image asset was not found: ${relativeAssetPath}`);
}

export async function buildTaskReferenceImage(
  taskId: TaskId,
  taskContext: TaskContext,
): Promise<GenerationReferenceImage | null> {
  if (taskId !== "image_to_code") {
    return null;
  }

  const imageContext = taskContext as TaskContextById["image_to_code"];
  const reference = getImageToCodeReference(imageContext.imageId);
  const relativeAssetPath = normalizePublicAssetPath(reference.assetPath);
  if (!relativeAssetPath) {
    return null;
  }

  const mimeType = resolveImageMimeType(reference.assetPath);
  if (!mimeType) {
    return null;
  }

  const bytes = await readAssetFromPublic(relativeAssetPath);
  return {
    mimeType,
    base64Data: bytes.toString("base64"),
  };
}