diff --git a/package.json b/package.json
index a52180c5..f64b00a4 100644
--- a/package.json
+++ b/package.json
@@ -8,13 +8,15 @@
"start": "docusaurus start",
"predev": "node ./scripts/update-endpoints.js",
"dev": "yarn start",
- "prebuild": "node ./scripts/update-endpoints.js && node ./scripts/validate-blog-social-images.js",
+ "prebuild": "node ./scripts/update-endpoints.js && node ./scripts/validate-blog-social-images.js && node ./scripts/generate-og-images.js",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"update-endpoints": "node ./scripts/update-endpoints.js",
"update-cli-help": "node ./scripts/update-cli-help.js",
"update-evm-config-schema": "curl https://raw.githubusercontent.com/enviodev/hyperindex/main/codegenerator/cli/npm/envio/evm.schema.json > ./static/schemas/config.evm.json",
"generate-config-schema-doc": "node ./scripts/generate-config-schema-doc.js",
+ "generate-og-images": "node ./scripts/generate-og-images.js",
+ "preview-og": "node ./scripts/generate-og-images.js --preview",
"predeploy": "yarn update-endpoints && yarn update-cli-help && yarn update-evm-config-schema && yarn generate-config-schema-doc",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
@@ -45,6 +47,7 @@
"@docusaurus/module-type-aliases": "^3.3.2",
"@docusaurus/types": "3.0.0",
"docusaurus-plugin-llms": "^0.2.2",
+ "sharp": "^0.34.5",
"vercel": "^50.40.0"
},
"browserslist": {
diff --git a/scripts/generate-og-images.js b/scripts/generate-og-images.js
new file mode 100644
index 00000000..92cd681b
--- /dev/null
+++ b/scripts/generate-og-images.js
@@ -0,0 +1,445 @@
+#!/usr/bin/env node
+
+/**
+ * Generates OG (Open Graph) meta images for all docs pages.
+ *
+ * Usage:
+ * node scripts/generate-og-images.js # generate for all docs
+ * node scripts/generate-og-images.js --force # overwrite existing images
+ * node scripts/generate-og-images.js --dry-run # print what would be done
+ */
+
+const fs = require("fs");
+const path = require("path");
+const sharp = require("sharp");
+
+const FORCE = process.argv.includes("--force");
+const DRY_RUN = process.argv.includes("--dry-run");
+// --preview [path/to/doc.md] — render one image to static/og-preview.png, no file edits
+const PREVIEW_IDX = process.argv.indexOf("--preview");
+const PREVIEW = PREVIEW_IDX !== -1;
+const PREVIEW_FILE = PREVIEW && process.argv[PREVIEW_IDX + 1] && !process.argv[PREVIEW_IDX + 1].startsWith("--")
+ ? path.resolve(process.argv[PREVIEW_IDX + 1])
+ : null;
+
+const REPO_ROOT = path.resolve(__dirname, "..");
+const DOCS_DIR = path.join(REPO_ROOT, "docs");
+const STATIC_DIR = path.join(REPO_ROOT, "static");
+const OG_DIR = path.join(STATIC_DIR, "docs-assets", "og");
+
+// Image dimensions (standard OG image)
+const WIDTH = 1200;
+const HEIGHT = 630;
+
+// Brand colors
+const COLOR_BG = "#0F0F0F";
+const COLOR_ACCENT = "#FF8267";
+const COLOR_WHITE = "#FFFFFF";
+const COLOR_GRAY = "#A0A0A0";
+
+// Envio logo as base64
+const logoPath = path.join(STATIC_DIR, "img", "envio-logo.png");
+const logoBase64 = fs.readFileSync(logoPath).toString("base64");
+const logoDataUri = `data:image/png;base64,${logoBase64}`;
+
+// Logo rendered at 25% of original (459x145 → ~172x54)
+const LOGO_W = 172;
+const LOGO_H = 54;
+const LOGO_X = 90;
+const LOGO_Y = 60;
+
+// ─── LAYOUT CONFIG ────────────────────────────────────────────────────────────
+// Edit these values to adjust the visual design, then run `yarn preview-og`.
+
+const SECTION_FONT_SIZE = 28; // font size of the small section label (e.g. "HyperIndex")
+const SECTION_X = 90; // horizontal position (px from left) of the section label
+const SECTION_Y = 280; // vertical position (px from top) of the section label
+
+// Title font sizes tried largest-first; the first one that fits within the image
+// width is used. Each entry is [fontSize, lineHeight].
+const TITLE_SIZE_STEPS = [
+ [72, 86], // short titles (≤ ~24 chars)
+ [60, 72], // medium titles (≤ ~29 chars)
+ [52, 64], // longer titles (≤ ~34 chars)
+ [44, 54], // very long (≤ ~40 chars)
+];
+
+const TITLE_START_Y = 360; // vertical position of the first title line
+
+// Gap (px) between the last title line and the first description line
+const TITLE_DESC_GAP = 0;
+
+// Gradient opacities (0 = invisible, 1 = full color)
+const GLOW_LARGE_OPACITY = 0.18; // top-right glow — main accent burst
+const GLOW_SMALL_OPACITY = 0.05; // bottom-left glow — subtle secondary
+
+const DESC_X = 85; // horizontal position (px from left) of the description text
+const DESC_FONT_SIZE = 30; // font size of the description text
+const DESC_LINE_HEIGHT = 40; // vertical spacing between description lines
+
+// ─── END LAYOUT CONFIG ────────────────────────────────────────────────────────
+
+/** Escape XML special characters for SVG text */
+function escXml(str) {
+ return String(str)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+/**
+ * Split a string into plain/code segments on backtick boundaries.
+ * "`config.yaml` file" → [{type:"code", content:"config.yaml"}, {type:"text", content:" file"}]
+ */
+function parseSegments(text) {
+ const segments = [];
+ const parts = text.split("`");
+ for (let i = 0; i < parts.length; i++) {
+ if (parts[i] === "") continue;
+ segments.push({ type: i % 2 === 0 ? "text" : "code", content: parts[i] });
+ }
+ return segments;
+}
+
+/**
+ * Render one line of text as SVG, turning `backtick` spans into code boxes.
+ *
+ * @param {number} startX - left edge of the line
+ * @param {number} y - baseline y of the line
+ * @param {string} text - raw text, may contain `backtick` spans
+ * @param {number} fontSize - font size in px
+ * @param {string} color - default text color (hex)
+ * @param {boolean} bold - whether the default text is bold
+ */
+function renderLine(startX, y, text, fontSize, color, bold = false) {
+ const segments = parseSegments(text);
+ const svgParts = [];
+
+ // Approximate char widths (px per character at given fontSize)
+ const plainCharW = fontSize * (bold ? 0.58 : 0.52); // DejaVu Sans (bold is wider)
+ const codeCharW = fontSize * 0.60; // DejaVu Sans Mono
+
+ // Code box vertical metrics
+ const boxPadX = fontSize * 0.20; // horizontal padding inside the box
+ const boxPadY = fontSize * 0.12; // vertical padding inside the box
+ const boxH = fontSize + boxPadY * 2;
+ const boxY = y - fontSize * 0.82 - boxPadY; // top of the box rectangle
+ const boxColor = "#2a2a2a"; // code box background fill
+ const codeColor = "#e8e8e8"; // code text color
+
+ let curX = startX;
+
+ for (const seg of segments) {
+ if (seg.type === "text") {
+ const w = seg.content.length * plainCharW;
+ const weight = bold ? ' font-weight="bold"' : "";
+ svgParts.push(
+ `${escXml(seg.content)}`
+ );
+ curX += w;
+ } else {
+ const innerW = seg.content.length * codeCharW;
+ const boxW = innerW + boxPadX * 2;
+ // Background rect
+ svgParts.push(
+ ``
+ );
+ // Monospace text
+ svgParts.push(
+ `${escXml(seg.content)}`
+ );
+ curX += boxW + fontSize * 0.20; // gap after code box
+ }
+ }
+
+ return svgParts.join("\n ");
+}
+
+/**
+ * Wrap text into lines no longer than maxChars.
+ * Returns an array of line strings.
+ */
+function wrapText(text, maxChars) {
+ const words = text.split(/\s+/);
+ const lines = [];
+ let current = "";
+ for (const word of words) {
+ if ((current + (current ? " " : "") + word).length <= maxChars) {
+ current = current ? `${current} ${word}` : word;
+ } else {
+ if (current) lines.push(current);
+ current = word.length > maxChars ? word.slice(0, maxChars - 1) + "…" : word;
+ }
+ }
+ if (current) lines.push(current);
+ return lines;
+}
+
+/**
+ * Pick title font size and chars-per-line based on title length.
+ * Approximate: DejaVu Sans Bold avg char width ≈ fontSize * 0.58.
+ * Available width = 1200 - 80 (left) - 80 (right) = 1040px.
+ */
+function titleLayout(title) {
+ const avail = 1040; // px
+ for (const [fontSize, lineHeight] of TITLE_SIZE_STEPS) {
+ const avgCharPx = fontSize * 0.58;
+ const charsPerLine = Math.floor(avail / avgCharPx);
+ const lines = wrapText(title, charsPerLine).slice(0, 2);
+ // Truncate second line with ellipsis if more would follow
+ if (lines.length === 2 && wrapText(title, charsPerLine).length > 2) {
+ const maxChars = charsPerLine;
+ lines[1] =
+ lines[1].length >= maxChars - 1
+ ? lines[1].slice(0, maxChars - 1) + "…"
+ : lines[1];
+ }
+ return { lines, fontSize, lineHeight };
+ }
+ // Fallback
+ return { lines: [title.slice(0, 24) + "…"], fontSize: 44, lineHeight: 54 };
+}
+
+/**
+ * Build an SVG string for an OG image.
+ */
+function buildSvg({ section, title, description }) {
+ const { lines: titleLines, fontSize: TITLE_FONT_SIZE, lineHeight: TITLE_LINE_HEIGHT } =
+ titleLayout(title);
+
+ // Description: max 2 lines. Chars per line derived from DESC_FONT_SIZE so it
+ // stays within the image width (avgCharPx ≈ fontSize * 0.52).
+ const DESC_CHARS = Math.floor(1040 / (DESC_FONT_SIZE * 0.52));
+ const descLines = description ? wrapText(description, DESC_CHARS).slice(0, 2) : [];
+ if (descLines.length === 2 && description && wrapText(description, DESC_CHARS).length > 2) {
+ descLines[1] =
+ descLines[1].length >= DESC_CHARS - 1
+ ? descLines[1].slice(0, DESC_CHARS - 1) + "…"
+ : descLines[1];
+ }
+
+ const DESC_START_Y = TITLE_START_Y + titleLines.length * TITLE_LINE_HEIGHT + TITLE_DESC_GAP;
+
+ const titleSvg = titleLines
+ .map((line, i) =>
+ renderLine(90, TITLE_START_Y + i * TITLE_LINE_HEIGHT, line, TITLE_FONT_SIZE, COLOR_WHITE, true)
+ )
+ .join("\n ");
+
+ const descSvg = descLines
+ .map((line, i) =>
+ renderLine(DESC_X, DESC_START_Y + i * DESC_LINE_HEIGHT, line, DESC_FONT_SIZE, COLOR_GRAY)
+ )
+ .join("\n ");
+
+ return `
+`;
+}
+
+/** Parse YAML-ish frontmatter from markdown content. Returns object or null. */
+function parseFrontmatter(content) {
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
+ if (!match) return null;
+ const fm = {};
+ for (const line of match[1].split(/\r?\n/)) {
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)/);
+ if (!kv) continue;
+ fm[kv[1]] = kv[2].trim().replace(/^["']|["']$/g, "");
+ }
+ return fm;
+}
+
+/** Return the byte range of the frontmatter block (including ---). */
+function frontmatterRange(content) {
+ const match = content.match(/^(---\r?\n[\s\S]*?\r?\n---)/);
+ if (!match) return null;
+ return { block: match[1], end: match[1].length };
+}
+
+/** Add or update the `image` field inside an existing frontmatter block. */
+function setFrontmatterImage(content, imagePath) {
+ const range = frontmatterRange(content);
+ if (!range) return content;
+
+ const { block, end } = range;
+ const rest = content.slice(end);
+
+ // If `image:` already exists in frontmatter, replace it
+ if (/^image:/m.test(block)) {
+ const updated = block.replace(/^image:.*$/m, `image: ${imagePath}`);
+ return updated + rest;
+ }
+
+ // Otherwise insert before closing ---
+ const updatedBlock = block.replace(/(\r?\n---$)/, `\nimage: ${imagePath}$1`);
+ return updatedBlock + rest;
+}
+
+/** Determine section label from file path relative to docs/ */
+function getSection(filePath) {
+ const rel = path.relative(DOCS_DIR, filePath);
+ const parts = rel.split(path.sep);
+ // parts[0] = HyperIndex | HyperSync | HyperRPC
+ // parts[1] = Advanced | Guides | ... (optional)
+ return parts[0] || "Envio";
+}
+
+/** Convert a file path to an output PNG path under static/docs-assets/og/ */
+function getOutputPath(filePath) {
+ const rel = path.relative(DOCS_DIR, filePath);
+ const withoutExt = rel.replace(/\.(md|mdx)$/, "");
+ return path.join(OG_DIR, `${withoutExt}.png`);
+}
+
+/** Convert output path to static-relative URL for frontmatter */
+function toStaticUrl(outputPath) {
+ return "/" + path.relative(STATIC_DIR, outputPath);
+}
+
+/** Recursively collect all .md and .mdx files, skipping LLM variants */
+function collectDocs(dir) {
+ const results = [];
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ // Skip LLM variant directories
+ if (entry.name.endsWith("-LLM") || entry.name === "unused") continue;
+ results.push(...collectDocs(full));
+ } else if (entry.isFile() && /\.(md|mdx)$/.test(entry.name)) {
+ results.push(full);
+ }
+ }
+ return results;
+}
+
+async function processFile(filePath) {
+ const content = fs.readFileSync(filePath, "utf8");
+ const fm = parseFrontmatter(content);
+
+ if (!fm) {
+ console.warn(` SKIP (no frontmatter): ${path.relative(REPO_ROOT, filePath)}`);
+ return { status: "skip" };
+ }
+
+ const title = fm.title || fm.sidebar_label || fm.id || path.basename(filePath, path.extname(filePath));
+ const description = fm.description || "";
+ const section = getSection(filePath);
+ const outputPath = getOutputPath(filePath);
+ const staticUrl = toStaticUrl(outputPath);
+
+ // Skip if image already set and not forcing
+ if (fm.image && !FORCE) {
+ console.log(` SKIP (has image): ${path.relative(REPO_ROOT, filePath)}`);
+ return { status: "skip" };
+ }
+
+ if (DRY_RUN) {
+ console.log(` WOULD generate: ${staticUrl}`);
+ return { status: "dry-run" };
+ }
+
+ // Ensure output directory exists
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
+
+ // Build SVG and convert to PNG
+ const svg = buildSvg({ section, title, description });
+ await sharp(Buffer.from(svg)).png().toFile(outputPath);
+
+ // Update frontmatter in source file
+ const updated = setFrontmatterImage(content, staticUrl);
+ fs.writeFileSync(filePath, updated, "utf8");
+
+ console.log(` OK: ${path.relative(REPO_ROOT, filePath)} → ${staticUrl}`);
+ return { status: "ok" };
+}
+
+async function runPreview() {
+ const PREVIEW_OUT = path.join(STATIC_DIR, "og-preview.png");
+
+ let section, title, description;
+
+ if (PREVIEW_FILE) {
+ const content = fs.readFileSync(PREVIEW_FILE, "utf8");
+ const fm = parseFrontmatter(content);
+ if (!fm) {
+ console.error("No frontmatter found in the specified file.");
+ process.exit(1);
+ }
+ section = getSection(PREVIEW_FILE);
+ title = fm.title || fm.sidebar_label || fm.id || path.basename(PREVIEW_FILE, path.extname(PREVIEW_FILE));
+ description = fm.description || "";
+ console.log(`Preview source: ${path.relative(REPO_ROOT, PREVIEW_FILE)}`);
+ } else {
+ // Default sample data so you can tweak the design without needing a real file
+ section = "HyperIndex";
+ title = "Getting Started with Envio";
+ description = "Get started with Envio indexer setup, templates, and local or hosted deployment quickly.";
+ console.log("Preview source: sample data (pass a file path to use real content)");
+ }
+
+ const svg = buildSvg({ section, title, description });
+ await sharp(Buffer.from(svg)).png().toFile(PREVIEW_OUT);
+ console.log(`Preview written to: ${path.relative(REPO_ROOT, PREVIEW_OUT)}`);
+}
+
+async function main() {
+ if (PREVIEW) {
+ await runPreview();
+ return;
+ }
+
+ console.log(`Generating OG images for docs (force=${FORCE}, dry-run=${DRY_RUN})…`);
+
+ const files = collectDocs(DOCS_DIR);
+ console.log(`Found ${files.length} docs files.`);
+
+ let ok = 0, skipped = 0, errors = 0;
+
+ for (const file of files) {
+ try {
+ const result = await processFile(file);
+ if (result.status === "ok") ok++;
+ else skipped++;
+ } catch (err) {
+ console.error(` ERROR: ${path.relative(REPO_ROOT, file)}: ${err.message}`);
+ errors++;
+ }
+ }
+
+ console.log(`\nDone. Generated: ${ok}, Skipped: ${skipped}, Errors: ${errors}`);
+ if (errors > 0) process.exit(1);
+}
+
+main();
diff --git a/yarn.lock b/yarn.lock
index 6d573056..750892fa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2634,6 +2634,13 @@
dependencies:
"@edge-runtime/primitives" "4.1.0"
+"@emnapi/runtime@^1.7.0":
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.9.2.tgz#8b469a3db160817cadb1de9050211a9d1ea84fa2"
+ integrity sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==
+ dependencies:
+ tslib "^2.4.0"
+
"@emotion/is-prop-valid@1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337"
@@ -2995,6 +3002,153 @@
resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.13.tgz#4838c766a1237253d4dde3281cf7d5c65186fd32"
integrity sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==
+"@img/colour@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.1.0.tgz#b0c2c2fa661adf75effd6b4964497cd80010bb9d"
+ integrity sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==
+
+"@img/sharp-darwin-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86"
+ integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-arm64" "1.2.4"
+
+"@img/sharp-darwin-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b"
+ integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-x64" "1.2.4"
+
+"@img/sharp-libvips-darwin-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43"
+ integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==
+
+"@img/sharp-libvips-darwin-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc"
+ integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==
+
+"@img/sharp-libvips-linux-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318"
+ integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==
+
+"@img/sharp-libvips-linux-arm@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d"
+ integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==
+
+"@img/sharp-libvips-linux-ppc64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7"
+ integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==
+
+"@img/sharp-libvips-linux-riscv64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de"
+ integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==
+
+"@img/sharp-libvips-linux-s390x@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec"
+ integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==
+
+"@img/sharp-libvips-linux-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce"
+ integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==
+
+"@img/sharp-libvips-linuxmusl-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06"
+ integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==
+
+"@img/sharp-libvips-linuxmusl-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75"
+ integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==
+
+"@img/sharp-linux-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc"
+ integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm64" "1.2.4"
+
+"@img/sharp-linux-arm@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d"
+ integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm" "1.2.4"
+
+"@img/sharp-linux-ppc64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813"
+ integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-ppc64" "1.2.4"
+
+"@img/sharp-linux-riscv64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60"
+ integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-riscv64" "1.2.4"
+
+"@img/sharp-linux-s390x@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7"
+ integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-s390x" "1.2.4"
+
+"@img/sharp-linux-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8"
+ integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-x64" "1.2.4"
+
+"@img/sharp-linuxmusl-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086"
+ integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
+
+"@img/sharp-linuxmusl-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f"
+ integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-x64" "1.2.4"
+
+"@img/sharp-wasm32@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0"
+ integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==
+ dependencies:
+ "@emnapi/runtime" "^1.7.0"
+
+"@img/sharp-win32-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a"
+ integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==
+
+"@img/sharp-win32-ia32@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de"
+ integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==
+
+"@img/sharp-win32-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8"
+ integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==
+
"@isaacs/balanced-match@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29"
@@ -7891,7 +8045,7 @@ destroy@1.2.0:
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
-detect-libc@^2.0.0:
+detect-libc@^2.0.0, detect-libc@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
@@ -16396,7 +16550,7 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4:
resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz"
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
-semver@^7.5.3:
+semver@^7.5.3, semver@^7.7.3:
version "7.7.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==
@@ -16562,6 +16716,40 @@ shallowequal@1.1.0, shallowequal@^1.1.0:
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
+sharp@^0.34.5:
+ version "0.34.5"
+ resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0"
+ integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==
+ dependencies:
+ "@img/colour" "^1.0.0"
+ detect-libc "^2.1.2"
+ semver "^7.7.3"
+ optionalDependencies:
+ "@img/sharp-darwin-arm64" "0.34.5"
+ "@img/sharp-darwin-x64" "0.34.5"
+ "@img/sharp-libvips-darwin-arm64" "1.2.4"
+ "@img/sharp-libvips-darwin-x64" "1.2.4"
+ "@img/sharp-libvips-linux-arm" "1.2.4"
+ "@img/sharp-libvips-linux-arm64" "1.2.4"
+ "@img/sharp-libvips-linux-ppc64" "1.2.4"
+ "@img/sharp-libvips-linux-riscv64" "1.2.4"
+ "@img/sharp-libvips-linux-s390x" "1.2.4"
+ "@img/sharp-libvips-linux-x64" "1.2.4"
+ "@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
+ "@img/sharp-libvips-linuxmusl-x64" "1.2.4"
+ "@img/sharp-linux-arm" "0.34.5"
+ "@img/sharp-linux-arm64" "0.34.5"
+ "@img/sharp-linux-ppc64" "0.34.5"
+ "@img/sharp-linux-riscv64" "0.34.5"
+ "@img/sharp-linux-s390x" "0.34.5"
+ "@img/sharp-linux-x64" "0.34.5"
+ "@img/sharp-linuxmusl-arm64" "0.34.5"
+ "@img/sharp-linuxmusl-x64" "0.34.5"
+ "@img/sharp-wasm32" "0.34.5"
+ "@img/sharp-win32-arm64" "0.34.5"
+ "@img/sharp-win32-ia32" "0.34.5"
+ "@img/sharp-win32-x64" "0.34.5"
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"