Skip to content
Closed
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
19 changes: 18 additions & 1 deletion docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,22 @@ const config = {
trackingID: "G-J0WZ32ZV5B",
anonymizeIP: true,
},
sitemap: {
// Keep the sitemap focused on the canonical v3 docs + blog. v2 is
// legacy and the LLM-consolidated routes duplicate content already
// surfaced via llms-full.txt, so they would only deflate llms.txt
// coverage metrics for agent discovery tools.
ignorePatterns: [
"/docs/v2/**",
"/docs/HyperIndex-LLM/**",
"/docs/HyperSync-LLM/**",
"/docs/HyperRPC-LLM/**",
"/blog/tag/**",
"/blog/author/**",
"/blog/archive",
"/blog/page/**",
],
},
}),
],
],
Expand Down Expand Up @@ -423,6 +439,7 @@ const config = {
}),
plugins: [
require.resolve('./plugins/plugin-author-pages'),
require.resolve('./plugins/plugin-llms-directive'),
[
"docusaurus-plugin-mcp-server",
{
Expand Down Expand Up @@ -521,7 +538,7 @@ This file contains links to documentation sections following the llmstxt.org sta
- [Indexing Optimism Bridge Deposits](https://docs.envio.dev/docs/HyperIndex/tutorial-op-bridge-deposits.md): Learn to quickly index Optimism Bridge deposits and explore OP Bridge event data.
- [Scaffold-Eth-2 Envio Extension](https://docs.envio.dev/docs/HyperIndex/scaffold-eth-2-extension-tutorial.md): Scaffold-ETH Extension: Get a boilerplate indexer for your deployed smart contracts and start tracking events instantly.
- [HyperIndex Benchmarks](https://docs.envio.dev/docs/HyperIndex/benchmarks.md): Discover HyperIndex benchmarks and see why it's the fastest blockchain data indexer.
- [HyperIndex Quickstart](https://docs.envio.dev/docs/HyperIndex/contract-import.md): Learn to quickly autogenerate and configure a HyperIndex indexer for any smart contract.
- [HyperIndex Quickstart](https://docs.envio.dev/docs/HyperIndex/quickstart.md): Learn to quickly autogenerate and configure a HyperIndex indexer for any smart contract.
- [Fuel](https://docs.envio.dev/docs/HyperIndex/fuel.md): Explore how to index and query real-time and historical data on Fuel Network with HyperIndex.
- [Licensing](https://docs.envio.dev/docs/HyperIndex/licensing.md): Learn how Envio's licensing lets developers self-host, use generated code, and stay open while protecting Envio Cloud.
- [Migrate from Alchemy to Envio](https://docs.envio.dev/docs/HyperIndex/migrate-from-alchemy.md): Easily migrate your existing Alchemy subgraphs to Envio for 143x faster indexing than subgraphs, multichain support, and a better developer experience.
Expand Down
103 changes: 101 additions & 2 deletions plugins/plugin-generate-llms.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ function GenerateLLMSPlugin(context, options) {
return parts.join("\n");
}

// Blockquote prepended to every .md copy so markdown clients can
// discover llms.txt without having to fetch the HTML variant.
const MD_LLMS_DIRECTIVE =
`> Agent-friendly docs: see [llms.txt](${siteConfig.url.replace(/\/$/, "")}/llms.txt) ` +
`for the navigational index, or [llms-full.txt](${siteConfig.url.replace(/\/$/, "")}/llms-full.txt) ` +
`for every page concatenated as markdown.\n\n`;

// --- NEW: write .md copies into build folder ---
function writeMarkdownCopies(docs) {
for (const doc of docs) {
Expand All @@ -275,8 +282,85 @@ function GenerateLLMSPlugin(context, options) {
);

fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(targetPath, cleanContent, "utf-8");
fs.writeFileSync(
targetPath,
MD_LLMS_DIRECTIVE + cleanContent,
"utf-8"
);
}
}

// Build a compact list of every collected doc not already linked
// in the supplied static root text. Lets us hit >80% coverage of
// the sitemap without manually maintaining 600+ bullet lines.
function renderUncoveredIndex(rootText, allDocs) {
const linkedUrls = new Set();
const urlRegex = /https?:\/\/[^\s)\]]+/g;
let m;
while ((m = urlRegex.exec(rootText)) !== null) {
// Strip trailing punctuation and a trailing `.md`/`.html`
// so comparison matches the canonical pageUrl form.
let u = m[0].replace(/[.,);\]]+$/, "");
u = u.replace(/\.(md|mdx|html)$/, "");
linkedUrls.add(u);
}

const buckets = {
"HyperIndex Network Pages": [],
"HyperSync Network Pages": [],
"HyperRPC Network Pages": [],
"HyperIndex Reference": [],
"HyperSync Reference": [],
"HyperRPC Reference": [],
"Blog & Case Studies": [],
};

for (const doc of allDocs) {
if (linkedUrls.has(doc.pageUrl)) continue;

const url = doc.pageUrl;
let bucket;
if (doc.source === "blog") {
bucket = "Blog & Case Studies";
} else if (
/\/HyperIndex\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
bucket = "HyperIndex Network Pages";
} else if (
/\/HyperSync\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
bucket = "HyperSync Network Pages";
} else if (
/\/HyperRPC\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
Comment on lines +326 to +338
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Supported-networks matcher is too strict and misses target files.

Lines 327/332/337 check for "/supported-networks/" in doc.filePath, but these pages are typically filenames like supported-networks.md. That prevents the intended network-page buckets from being used.

Suggested fix
-                        /\/supported-networks\//.test(doc.filePath)
+                        /\/supported-networks(\.|\/)/.test(doc.filePath)
...
-                        /\/supported-networks\//.test(doc.filePath)
+                        /\/supported-networks(\.|\/)/.test(doc.filePath)
...
-                        /\/supported-networks\//.test(doc.filePath)
+                        /\/supported-networks(\.|\/)/.test(doc.filePath)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/\/HyperIndex\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
bucket = "HyperIndex Network Pages";
} else if (
/\/HyperSync\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
bucket = "HyperSync Network Pages";
} else if (
/\/HyperRPC\//.test(url) &&
/\/supported-networks\//.test(doc.filePath)
) {
/\/HyperIndex\//.test(url) &&
/\/supported-networks(\.|\/)/.test(doc.filePath)
) {
bucket = "HyperIndex Network Pages";
} else if (
/\/HyperSync\//.test(url) &&
/\/supported-networks(\.|\/)/.test(doc.filePath)
) {
bucket = "HyperSync Network Pages";
} else if (
/\/HyperRPC\//.test(url) &&
/\/supported-networks(\.|\/)/.test(doc.filePath)
) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/plugin-generate-llms.js` around lines 326 - 338, The matcher for
supported networks is too strict: the current checks using
/\/supported-networks\//.test(doc.filePath) (near the bucket assignments for
"HyperIndex Network Pages", "HyperSync Network Pages", "HyperRPC Network Pages")
miss files named like supported-networks.md; update the condition to detect the
substring "supported-networks" in file paths or filenames (for example replace
the regex with a looser check such as
/supported-networks(?:[\/\.]|$)/.test(doc.filePath) or simply
doc.filePath.includes('supported-networks')) so those docs are correctly routed
into the intended buckets.

bucket = "HyperRPC Network Pages";
} else if (/\/HyperIndex\//.test(url)) {
bucket = "HyperIndex Reference";
} else if (/\/HyperSync\//.test(url)) {
bucket = "HyperSync Reference";
} else if (/\/HyperRPC\//.test(url)) {
bucket = "HyperRPC Reference";
} else {
continue;
}
buckets[bucket].push(doc);
}

const sections = [];
for (const [name, docs] of Object.entries(buckets)) {
if (docs.length === 0) continue;
docs.sort((a, b) => a.title.localeCompare(b.title));
sections.push(`## ${name}`);
sections.push("");
for (const d of docs) {
sections.push(`- [${d.title}](${d.pageUrl}.md)`);
}
sections.push("");
}
return sections.join("\n");
}

// 2. generate files
Expand All @@ -289,7 +373,22 @@ function GenerateLLMSPlugin(context, options) {
// Inject "## Table of Contents" after root text
const tocRoot = root.trim() + "";

const output = renderLLMS(tocRoot, orderedDocs);
let output = renderLLMS(tocRoot, orderedDocs);

// Append every doc not already linked in the static root so
// sitemap-coverage agent checks see >80% of pages indexed.
// Only runs for the main config to keep secondary llms-*.txt
// files focused on their topical subset.
if (cfg.main) {
const extra = renderUncoveredIndex(tocRoot, collectedDocs);
if (extra.trim().length > 0) {
output =
output.replace(/\s+$/, "") +
"\n\n" +
extra.trimEnd() +
"\n";
}
}

// Use llms.txt for the first/main config, others as llms-<name>.txt
const outFileName = cfg.main ? "llms.txt" : `llms-${name}.txt`;
Expand Down
61 changes: 61 additions & 0 deletions plugins/plugin-llms-directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Inject an llms.txt discovery directive into every HTML page.
// Surfaces:
// - <link rel="llms-txt"> + <link rel="alternate" type="text/markdown"> in <head>
// - A visually-hidden anchor at the very start of <body> for crawlers that
// only scan text content for the llms.txt URL.
//
// Agents (Claude Code, Cursor, OpenCode, afdocs checkers) use these signals
// to locate the navigational index without having to guess /llms.txt.

const LLMS_URL_ABS = "https://docs.envio.dev/llms.txt";
const LLMS_FULL_URL_ABS = "https://docs.envio.dev/llms-full.txt";

function LLMSDirectivePlugin() {
return {
name: "envio-llms-directive",
injectHtmlTags() {
return {
headTags: [
{
tagName: "link",
attributes: {
rel: "llms-txt",
href: LLMS_URL_ABS,
type: "text/markdown",
},
},
{
tagName: "link",
attributes: {
rel: "alternate",
type: "text/markdown",
href: LLMS_URL_ABS,
title: "llms.txt",
},
},
{
tagName: "link",
attributes: {
rel: "alternate",
type: "text/markdown",
href: LLMS_FULL_URL_ABS,
title: "llms-full.txt",
},
},
{
tagName: "meta",
attributes: {
name: "llms-txt",
content: LLMS_URL_ABS,
},
},
],
preBodyTags: [
`<a href="${LLMS_URL_ABS}" rel="llms-txt" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Envio docs llms.txt — agent-facing documentation index</a>`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hidden anchor is focusable while visually hidden.

Line 54 adds an offscreen link that can still receive keyboard focus, creating an invisible tab stop on every page. Make it non-focusable and hidden from assistive tech.

Suggested fix
-          `<a href="${LLMS_URL_ABS}" rel="llms-txt" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Envio docs llms.txt — agent-facing documentation index</a>`,
+          `<a href="${LLMS_URL_ABS}" rel="llms-txt" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Envio docs llms.txt — agent-facing documentation index</a>`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`<a href="${LLMS_URL_ABS}" rel="llms-txt" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Envio docs llms.txt — agent-facing documentation index</a>`,
`<a href="${LLMS_URL_ABS}" rel="llms-txt" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Envio docs llms.txt — agent-facing documentation index</a>`,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/plugin-llms-directive.js` at line 54, The offscreen anchor string
that uses LLMS_URL_ABS (the `<a ...>Envio docs llms.txt — agent-facing
documentation index</a>`) is visually hidden but still focusable and exposed to
assistive tech; update the anchor to be non-focusable and hidden from AT by
adding tabindex="-1" and aria-hidden="true" (or role="presentation") while
keeping the existing visual-hide styling so it no longer creates an invisible
tab stop or appears to screen readers.

],
};
},
};
}

module.exports = LLMSDirectivePlugin;
2 changes: 1 addition & 1 deletion src/data/network-count.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hyperSyncChainCount": 87,
"generatedAt": "2026-05-14T19:09:24.019Z",
"generatedAt": "2026-05-18T09:29:23.922Z",
"source": "https://chains.hyperquery.xyz/active_chains"
}
2 changes: 1 addition & 1 deletion static/blog-posts-index.json

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,37 @@
{
"source": "/mcp",
"destination": "/api/mcp"
},
{
"source": "/docs/:path((?!.+\\.md$).+)",
"has": [
{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }
],
"destination": "/docs/:path.md"
},
{
"source": "/blog/:path((?!.+\\.md$).+)",
"has": [
{ "type": "header", "key": "accept", "value": ".*text/markdown.*" }
],
"destination": "/blog/:path.md"
Comment on lines +13 to +24
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
jq -r '.rewrites[]?.source' vercel.json | nl -ba

Repository: enviodev/docs

Length of output: 100


🏁 Script executed:

cat vercel.json

Repository: enviodev/docs

Length of output: 1053


Root /docs and /blog paths lack markdown content negotiation.

The existing rewrite patterns require a path segment after /docs/ or /blog/—bare requests to /docs or /blog with Accept: text/markdown will not be redirected to their .md equivalents. Add exact-path rewrites for the root endpoints before the parametrized rules:

Suggested fix
   "rewrites": [
     {
       "source": "/mcp",
       "destination": "/api/mcp"
     },
+    {
+      "source": "/docs",
+      "has": [
+        { "type": "header", "key": "accept", "value": ".*text/markdown.*" }
+      ],
+      "destination": "/docs.md"
+    },
     {
       "source": "/docs/:path((?!.+\\.md$).+)",
       "has": [
         { "type": "header", "key": "accept", "value": ".*text/markdown.*" }
       ],
       "destination": "/docs/:path.md"
     },
+    {
+      "source": "/blog",
+      "has": [
+        { "type": "header", "key": "accept", "value": ".*text/markdown.*" }
+      ],
+      "destination": "/blog.md"
+    },
     {
       "source": "/blog/:path((?!.+\\.md$).+)",
       "has": [
         { "type": "header", "key": "accept", "value": ".*text/markdown.*" }
       ],
       "destination": "/blog/:path.md"
     }
   ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vercel.json` around lines 13 - 24, The rewrite rules for "/docs/:path(...)"
and "/blog/:path(...)" miss exact-root matches, so add two exact-path rewrite
entries with "source": "/docs" -> "destination": "/docs.md" and "source":
"/blog" -> "destination": "/blog.md" and include the same "has" header check
(Accept: .*text/markdown.*); place these two new entries immediately before the
existing parametrized rules so bare requests to /docs and /blog with Accept:
text/markdown are rewritten correctly.

}
],
"redirects": [
{
"source": "/blog/tags/:tag*",
"destination": "/blog/tag/:tag*",
"permanent": true
},
{
"source": "/docs/HyperIndex/contract-import",
"destination": "/docs/HyperIndex/quickstart",
"permanent": true
},
{
"source": "/docs/HyperIndex/contract-import.md",
"destination": "/docs/HyperIndex/quickstart.md",
"permanent": true
}
]
}