Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,18 +395,20 @@ export function createApp(config: AppConfig = {}) {
// Escape `<` so `</script>` in report markdown can't break out.
const payload = JSON.stringify(diagnostic).replace(/</g, "\\u003c");
const injected = `<script>window.__PUBLIC_DIAGNOSTIC__=${payload};</script>`;
// Replace generic title then inject SEO tags + payload before </head>.
const htmlWithTitle = html.replace(
/<title>[^<]*<\/title>/,
`<title>${pageTitle}</title>`,
);
const base = htmlWithTitle.includes("<title>") ? htmlWithTitle : html;
const withPayload = base.includes("</head>")
? base.replace(
// Strip the static SEO tags from index.html so crawlers (which
// pick the first occurrence) don't read the generic defaults
// instead of our dynamic ones.
const stripped = html
.replace(/<title>[^<]*<\/title>\s*/i, "")
.replace(/<meta\s+name=["']description["'][^>]*\/?>\s*/gi, "")
.replace(/<meta\s+property=["']og:[^"']+["'][^>]*\/?>\s*/gi, "")
.replace(/<meta\s+name=["']twitter:[^"']+["'][^>]*\/?>\s*/gi, "");
const withPayload = stripped.includes("</head>")
? stripped.replace(
"</head>",
`\t\t${seoTags}\n\t\t${injected}\n\t</head>`,
)
: `${injected}${base}`;
: `${injected}${stripped}`;
return new Response(withPayload, {
headers: {
"content-type": "text/html; charset=utf-8",
Expand Down
Binary file removed api/assets/Inter-Bold.ttf
Binary file not shown.
Binary file removed api/assets/Inter-Regular.ttf
Binary file not shown.
53 changes: 30 additions & 23 deletions api/lib/og-image.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { initWasm, Resvg } from "@resvg/resvg-wasm";
import satori from "satori";
import type { Diagnostic } from "../types/diagnostic.ts";

// ── Singletons initialised once per process ──────────────────────────────────
// Fetched from CDN once and held in memory. Works in both Bun (local dev) and
// Cloudflare Workers (production) — no filesystem required.

let resvgReady = false;
const RESVG_WASM_URL =
"https://unpkg.com/@resvg/resvg-wasm@2.6.2/index_bg.wasm";
const INTER_REGULAR_URL =
"https://github.com/rsms/inter/raw/v4.0/extras/ttf/Inter-Regular.ttf";
const INTER_BOLD_URL =
"https://github.com/rsms/inter/raw/v4.0/extras/ttf/Inter-Bold.ttf";

let initPromise: Promise<void> | null = null;
let fontRegular: ArrayBuffer | null = null;
let fontBold: ArrayBuffer | null = null;

async function init() {
if (resvgReady && fontRegular && fontBold) return;

const assetsDir = join(import.meta.dir, "../assets");
const [wasmBuf, reg, bold] = await Promise.all([
readFile(
join(
import.meta.dir,
"../../node_modules/@resvg/resvg-wasm/index_bg.wasm",
),
),
readFile(join(assetsDir, "Inter-Regular.ttf")),
readFile(join(assetsDir, "Inter-Bold.ttf")),
]);

if (!resvgReady) {
await initWasm(wasmBuf.buffer as ArrayBuffer);
resvgReady = true;
async function fetchBytes(url: string): Promise<ArrayBuffer> {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to fetch ${url}: ${res.status}`);
}
fontRegular = reg.buffer as ArrayBuffer;
fontBold = bold.buffer as ArrayBuffer;
return res.arrayBuffer();
}

function init(): Promise<void> {
if (initPromise) return initPromise;
initPromise = (async () => {
const [wasm, reg, bold] = await Promise.all([
fetchBytes(RESVG_WASM_URL),
fetchBytes(INTER_REGULAR_URL),
fetchBytes(INTER_BOLD_URL),
]);
await initWasm(wasm);
fontRegular = reg;
fontBold = bold;
})();
return initPromise;
}

// ── Helpers ───────────────────────────────────────────────────────────────────
Expand Down
Loading