Skip to content
Open
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
7 changes: 7 additions & 0 deletions .github/actions/zizmor-noop/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: zizmor-noop
description: No-op action to satisfy org-level zizmor input collection
runs:
using: composite
steps:
- shell: bash
run: ":"
15 changes: 11 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

default: contracts-generate format check
default: contracts-generate llms-generate format check

help:
@echo "Common docs tasks:"
Expand All @@ -14,6 +14,8 @@ help:
@echo " make check-redirects # Verify all redirects land on HTTP 200 (needs running server)"
@echo " # Override port: BASE_URL=http://localhost:3335 make check-redirects"
@echo " make contracts-generate # Regenerate contract pages/snippets from data/contracts.json"
@echo " make llms-generate # Regenerate llms.txt from docs.json + page frontmatter"
@echo " make llms-check # Fail if llms.txt is stale"
@echo " make check-pol-addresses # POLAddresses vs contracts.json summary (fails if not_ok > 0)"
@echo " make list-pol-addresses-not-ok # List all not_ok POL address rows"
@echo " make format # Reformat all .md and .mdx files with Prettier"
Expand All @@ -23,9 +25,9 @@ help:
dev:
mint dev

check: check-validate check-links check-assets check-a11y check-vale check-redirects check-pol-addresses
check: llms-check check-validate check-links check-assets check-a11y check-vale check-redirects check-pol-addresses

check-validate:
check-validate: llms-check
mint validate

check-links:
Expand Down Expand Up @@ -82,6 +84,12 @@ contracts-generate:
node scripts/contracts/generate-pages.mjs
make format

llms-generate:
node scripts/generate-llms.mjs

llms-check:
node scripts/generate-llms.mjs --check

POL_ADDRESSES_SOL := ../contracts-internal/script/pol/POLAddresses.sol

check-pol-addresses:
Expand All @@ -102,4 +110,3 @@ list-pol-addresses-not-ok:

mint-install:
npm i -g mint

8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

- **Berachain (General)** — L1 overview, Proof-of-Liquidity, tokens, governance
- **Build** — Getting started, BEX, Bend, nodes, validators
- **Reference** — APIs, contracts, and technical reference

Check warning on line 19 in README.md

View check run for this annotation

Mintlify / Mintlify Validation (berachain-422fce37) - vale-spellcheck

README.md#L19

Did you really mean 'APIs'?

Content is in MDX. Navigation and branding are configured in `docs.json`.
Content is in MDX. Navigation and branding are configured in `docs.json`. `llms.txt` is generated from navigation and page metadata; do not edit it by hand.

---

Expand All @@ -28,7 +28,7 @@

- **Node.js** v20.17.0+ (LTS recommended)
- **Git**
- **npm** or **pnpm**

Check warning on line 31 in README.md

View check run for this annotation

Mintlify / Mintlify Validation (berachain-422fce37) - vale-spellcheck

README.md#L31

Did you really mean 'npm'?

Check warning on line 31 in README.md

View check run for this annotation

Mintlify / Mintlify Validation (berachain-422fce37) - vale-spellcheck

README.md#L31

Did you really mean 'pnpm'?

### Install Mintlify CLI

Expand All @@ -52,6 +52,12 @@
mint validate
```

If page metadata or navigation changed, regenerate the committed LLM index too:

```bash
make llms-generate
```

---

## Contributing
Expand Down
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"theme": "palm",
"mode": "dark",
"name": "Berachain",
"description": "Official Berachain documentation. Berachain is an EVM-identical L1 secured by Proof-of-Liquidity (PoL). Covers the BERA, BGT, WBERA, sWBERA, and HONEY tokens; reward vaults, incentives, boosts, block rewards, and the BeraChef, BlockRewardController, and Distributor contracts; BEX (the DEX) swaps, weighted and stable pools, liquidity provision, and the Smart Order Router (SOR); Bend lending, borrow/repay, collateral, LTV, liquidation, interest rate models, and oracles; running nodes and validators with Beacon Kit and Bera-Reth, staking pools, RPC endpoints, genesis, and snapshots; mainnet (chain ID 80094) and Bepolia testnet (80069); and building on Berachain with deployed contract addresses, ABIs, SDKs, Foundry, Hardhat, viem, and wagmi. Consult these docs for any Berachain, PoL, BGT, HONEY, BEX, Bend, or validator question.",
"colors": {
"primary": "#92400E",
"light": "#FBBF24",
Expand Down Expand Up @@ -1352,4 +1353,4 @@
"permanent": true
}
]
}
}
2 changes: 1 addition & 1 deletion index.mdx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: "Introduction"
description: "Official documentation for Berachain - the first blockchain powered by Proof-of-Liquidity"
description: "Official Berachain documentation for Proof-of-Liquidity, BEX, Bend, nodes, and building on Berachain."
---

<img
src="/images/general/docs-bear.png"
alt="Berachain Docs"
style={{ maxWidth: "280px", margin: "0 auto 1.5rem auto", display: "block" }}

Check warning on line 9 in index.mdx

View check run for this annotation

Mintlify / Mintlify Validation (berachain-422fce37) - vale-spellcheck

index.mdx#L9

Did you really mean 'maxWidth'?
/>

Berachain is a high-performance EVM-identical Layer 1 blockchain utilizing Proof-of-Liquidity (PoL) consensus.
Expand Down Expand Up @@ -46,7 +46,7 @@
Native gas and staking token.
</Card>
<Card title="$HONEY" href="/general/tokens/honey">
Native stablecoin.

Check warning on line 49 in index.mdx

View check run for this annotation

Mintlify / Mintlify Validation (berachain-422fce37) - vale-spellcheck

index.mdx#L49

Did you really mean 'stablecoin'?
</Card>
<Card title="$sWBERA" href="/general/tokens/swbera">
Yield-bearing staking vault token.
Expand Down
153 changes: 153 additions & 0 deletions llms.txt

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion nodes/operations/fusaka.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The old style of `--chain <path>` still works, but you must be sure to fetch and
1. **If you are running Bera-Geth, you [have to migrate to Bera-Reth](/nodes/operations/bera-geth-to-reth).**
2. Download the [recommended versions](/nodes/architecture/evm-execution).
3. Adjust Bera-Reth startup:
- Change your <span style={{ whiteSpace: "nowrap" }}>`--chain`</span> option to <span style={{ whiteSpace: "nowrap" }}>`--chain bepolia`</span> and remove <span style={{ whiteSpace: "nowrap" }}>`--bootnodes [enode-list]`</span>.
- Change your <span style={{ whiteSpace: "nowrap" }}>`--chain`</span> option to <span style={{ whiteSpace: "nowrap" }}>`--chain (mainnet|bepolia)`</span> and remove <span style={{ whiteSpace: "nowrap" }}>`--bootnodes [enode-list]`</span>.
- OR download and fetch the current bepolia genesis file from the [recommended versions](/nodes/architecture/evm-execution) page, and overwrite your current genesis file with it.
4. No changes are needed to BeaconKit options.
5. Start the new `beacond` and `bera-reth` binaries.
Expand Down
268 changes: 268 additions & 0 deletions scripts/generate-llms.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
#!/usr/bin/env node

import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const docsConfigPath = path.join(repoRoot, "docs.json");
const outputPath = path.join(repoRoot, "llms.txt");
const checkMode = process.argv.includes("--check");

const docsConfig = JSON.parse(fs.readFileSync(docsConfigPath, "utf8"));
const siteName = normalizeInlineText(docsConfig.name || "Documentation");
const siteDescription = normalizeInlineText(docsConfig.description || "");
const siteUrl = normalizeSiteUrl(docsConfig.logo?.href || "https://docs.berachain.com");

const pagePaths = collectPagePaths(docsConfig.navigation?.tabs || []);
const lines = pagePaths.map(renderPageLine).filter(Boolean);
const nextContent = `${[`# ${siteName}`, siteDescription ? `> ${siteDescription}` : "", "## Docs", lines.join("\n")]
.filter(Boolean)
.join("\n\n")}\n`;
const prevContent = fs.existsSync(outputPath) ? fs.readFileSync(outputPath, "utf8") : null;

if (prevContent === nextContent) {
console.log(`Unchanged ${path.relative(repoRoot, outputPath)}`);
process.exit(0);
}

if (checkMode) {
console.error("llms.txt is stale. Run: node scripts/generate-llms.mjs");
process.exit(1);
}

fs.writeFileSync(outputPath, nextContent);
console.log(`Generated ${path.relative(repoRoot, outputPath)}`);

function collectPagePaths(tabs) {
const seen = new Set();

function addPagePath(pagePath) {
if (typeof pagePath !== "string" || !pagePath.trim()) {
return;
}
seen.add(pagePath);
}

function visitPage(entry) {
if (typeof entry === "string") {
addPagePath(entry);
return;
}

if (!entry || typeof entry !== "object") {
return;
}

addPagePath(entry.root);

if (Array.isArray(entry.pages)) {
entry.pages.forEach(visitPage);
}
}

for (const tab of tabs) {
for (const group of tab.groups || []) {
for (const page of group.pages || []) {
visitPage(page);
}
}
}

return Array.from(seen).sort((left, right) => left.localeCompare(right));
}

function renderPageLine(pagePath) {
const meta = readPageMeta(pagePath);
if (!meta) {
console.warn(`Skipping ${pagePath}; missing source file`);
return "";
}

const label = meta.title || slugToTitle(pagePath);
const description = meta.description ? `: ${meta.description}` : "";
return `- [${label}](${siteUrl}/${pagePath}.md)${description}`;
}

function readPageMeta(pagePath) {
const filePath = resolvePageFile(pagePath);
if (!filePath) {
return null;
}

const raw = fs.readFileSync(filePath, "utf8");
const { frontmatter, body } = splitFrontmatter(raw);

const title =
extractFrontmatterValue(frontmatter, "title") ||
extractHeadMetaContent(frontmatter, "og:title") ||
extractFirstHeading(body) ||
slugToTitle(pagePath);
const description =
extractFrontmatterValue(frontmatter, "description") ||
extractHeadDescription(frontmatter) ||
extractFirstParagraph(body);

return {
title: normalizeInlineText(title),
description: normalizeInlineText(description)
};
}

function resolvePageFile(pagePath) {
for (const extension of [".mdx", ".md"]) {
const absolutePath = path.join(repoRoot, `${pagePath}${extension}`);
if (fs.existsSync(absolutePath)) {
return absolutePath;
}
}

return null;
}

function splitFrontmatter(raw) {
if (!raw.startsWith("---\n")) {
return { frontmatter: "", body: raw };
}

const end = raw.indexOf("\n---\n", 4);
if (end === -1) {
return { frontmatter: "", body: raw };
}

return {
frontmatter: raw.slice(4, end),
body: raw.slice(end + 5)
};
}

function extractFrontmatterValue(frontmatter, key) {
const match = frontmatter.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
return match ? stripWrappingQuotes(match[1].trim()) : "";
}

function extractHeadMetaContent(frontmatter, property) {
const lines = frontmatter.split("\n");
for (let index = 0; index < lines.length; index += 1) {
if (!lines[index].includes(property)) {
continue;
}

for (let nextIndex = index + 1; nextIndex < Math.min(index + 4, lines.length); nextIndex += 1) {
const match = lines[nextIndex].match(/^\s*content:\s*(.+)$/);
if (match) {
return stripWrappingQuotes(match[1].trim());
}
}
}

return "";
}

function extractHeadDescription(frontmatter) {
const lines = frontmatter.split("\n");
for (let index = 0; index < lines.length; index += 1) {
if (!lines[index].match(/^\s*-?\s*name:\s*description\s*$/)) {
continue;
}

for (let nextIndex = index + 1; nextIndex < Math.min(index + 4, lines.length); nextIndex += 1) {
const match = lines[nextIndex].match(/^\s*content:\s*(.+)$/);
if (match) {
return stripWrappingQuotes(match[1].trim());
}
}
}

return "";
}

function extractFirstHeading(body) {
const match = body.match(/^#\s+(.+)$/m);
return match ? match[1].trim() : "";
}

function extractFirstParagraph(body) {
const lines = body.split("\n");
const paragraph = [];
let inCodeBlock = false;

for (const line of lines) {
const trimmed = line.trim();

if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
inCodeBlock = !inCodeBlock;
continue;
}

if (inCodeBlock) {
continue;
}

if (!paragraph.length && trimmed.startsWith("## ")) {
return "";
}

if (!trimmed) {
if (paragraph.length) {
break;
}
continue;
}

if (!paragraph.length && shouldSkipParagraphLine(trimmed)) {
continue;
}

paragraph.push(trimmed);
}

return paragraph.join(" ");
}

function shouldSkipParagraphLine(line) {
return (
line.startsWith("#") ||
line.startsWith("<") ||
line.startsWith("import ") ||
line.startsWith("export ") ||
line.startsWith(":::") ||
line.startsWith("|") ||
/^[-*+]\s/.test(line) ||
/^\d+\.\s/.test(line)
);
}

function normalizeInlineText(value) {
return String(value || "")
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
.replace(/[`*_~]/g, "")
.replace(/<[^>]+>/g, "")
.replace(/\\\$/g, "$")
.replace(/\s+/g, " ")
.trim();
}

function stripWrappingQuotes(value) {
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
return value.slice(1, -1);
}

return value;
}

function slugToTitle(pagePath) {
const slug = pagePath.split("/").at(-1) || pagePath;
return slug
.split("-")
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ");
}

function normalizeSiteUrl(url) {
return String(url || "https://docs.berachain.com").replace(/\/+$/, "");
}
Loading