Skip to content

Commit 4b953e7

Browse files
committed
chore(dev-hub) Add API card component
1 parent 3e2fd22 commit 4b953e7

File tree

6 files changed

+312
-47
lines changed

6 files changed

+312
-47
lines changed

apps/developer-hub/content/docs/api-reference/entropy/fortuna/index.mdx

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ title: Overview
44

55
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
66

7-
<Cards>
8-
<Card
7+
<APICards>
8+
<APICard
99
href="/api-reference/entropy/fortuna/chain_ids"
10-
title="Get the list of supported chain ids"
10+
title="/v1/chains"
11+
method="GET"
1112
description="Get the list of supported chain ids"
1213
/>
13-
<Card
14+
<APICard
1415
href="/api-reference/entropy/fortuna/revelation"
15-
title="Reveal the random value for a given sequence number and blockchain."
16-
description="Reveal the random value for a given sequence number and blockchain.\n\nGiven a sequence number, retrieve the corresponding random value that this provider has committed to.\nThis endpoint will not return the random value unless someone has requested the sequence number on-chain.\n\nEvery blockchain supported by this service has a distinct sequence of random numbers and chain_id.\nCallers must pass the appropriate chain_id to ensure they fetch the correct random number."
16+
title="/v1/chains/{chain_id}/revelations/{sequence}"
17+
method="GET"
18+
description="Reveal the random value for a given sequence number and blockchain."
1719
/>
18-
<Card
20+
<APICard
1921
href="/api-reference/entropy/fortuna/explorer"
20-
title="Returns the logs of all requests captured by the keeper."
21-
description="Returns the logs of all requests captured by the keeper.\n\nThis endpoint allows you to filter the logs by a specific network ID, a query string (which can be a transaction hash, sender address, or sequence number), and a time range.\nThis is useful for debugging and monitoring the requests made to the Entropy contracts on various chains."
22+
title="/v1/logs"
23+
method="GET"
24+
description="Returns the logs of all requests captured by the keeper."
2225
/>
23-
</Cards>
26+
</APICards>

apps/developer-hub/content/docs/api-reference/pyth-core/hermes/index.mdx

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,77 @@ title: Overview
44

55
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
66

7-
<Cards>
8-
<Card
7+
<APICards>
8+
<APICard
99
href="/api-reference/pyth-core/hermes/get_price_feed"
10-
title="**Deprecated: use /v2/updates/price/{publish_time} instead**"
11-
description="**Deprecated: use /v2/updates/price/{publish_time} instead**\n\nGet a price update for a price feed with a specific timestamp\n\nGiven a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp."
10+
title="/api/get_price_feed"
11+
method="GET"
12+
description="Get a price update for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp."
1213
/>
13-
<Card
14+
<APICard
1415
href="/api-reference/pyth-core/hermes/get_vaa"
15-
title="**Deprecated: use /v2/updates/price/{publish_time} instead**"
16-
description="**Deprecated: use /v2/updates/price/{publish_time} instead**\n\nGet a VAA for a price feed with a specific timestamp\n\nGiven a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp."
16+
title="/api/get_vaa"
17+
method="GET"
18+
description="Get a VAA for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp."
1719
/>
18-
<Card
20+
<APICard
1921
href="/api-reference/pyth-core/hermes/get_vaa_ccip"
20-
title="**Deprecated: use /v2/updates/price/{publish_time} instead**"
21-
description="**Deprecated: use /v2/updates/price/{publish_time} instead**\n\nGet a VAA for a price feed using CCIP\n\nThis endpoint accepts a single argument which is a hex-encoded byte string of the following form:\n`<price feed id (32 bytes> <publish time as unix timestamp (8 bytes, big endian)>`"
22+
title="/api/get_vaa_ccip"
23+
method="GET"
24+
description="Get a VAA for a price feed using CCIP This endpoint accepts a single argument which is a hex-encoded byte string of the following form: `<price feed id (32 bytes> <publish time as unix timestamp (8 bytes, big endian)>`"
2225
/>
23-
<Card
26+
<APICard
2427
href="/api-reference/pyth-core/hermes/latest_price_feeds"
25-
title="**Deprecated: use /v2/updates/price/latest instead**"
26-
description="**Deprecated: use /v2/updates/price/latest instead**\n\nGet the latest price updates by price feed id.\n\nGiven a collection of price feed ids, retrieve the latest Pyth price for each price feed."
28+
title="/api/latest_price_feeds"
29+
method="GET"
30+
description="Get the latest price updates by price feed id."
2731
/>
28-
<Card
32+
<APICard
2933
href="/api-reference/pyth-core/hermes/latest_vaas"
30-
title="**Deprecated: use /v2/updates/price/latest instead**"
31-
description="**Deprecated: use /v2/updates/price/latest instead**\n\nGet VAAs for a set of price feed ids.\n\nGiven a collection of price feed ids, retrieve the latest VAA for each. The returned VAA(s) can\nbe submitted to the Pyth contract to update the on-chain price. If VAAs are not found for every\nprovided price ID the call will fail."
34+
title="/api/latest_vaas"
35+
method="GET"
36+
description="Get VAAs for a set of price feed ids."
3237
/>
33-
<Card
38+
<APICard
3439
href="/api-reference/pyth-core/hermes/price_feed_ids"
35-
title="**Deprecated: use /v2/price_feeds instead**"
36-
description="**Deprecated: use /v2/price_feeds instead**\n\nGet the set of price feed IDs.\n\nThis endpoint fetches all of the price feed IDs for which price updates can be retrieved."
40+
title="/api/price_feed_ids"
41+
method="GET"
42+
description="Get the set of price feed IDs."
3743
/>
38-
<Card
44+
<APICard
3945
href="/api-reference/pyth-core/hermes/price_feeds_metadata"
40-
title="Get the set of price feeds."
41-
description="Get the set of price feeds.\n\nThis endpoint fetches all price feeds from the Pyth network. It can be filtered by asset type\nand query string."
46+
title="/v2/price_feeds"
47+
method="GET"
48+
description="Get the set of price feeds."
4249
/>
43-
<Card
50+
<APICard
4451
href="/api-reference/pyth-core/hermes/latest_price_updates"
45-
title="Get the latest price updates by price feed id."
46-
description="Get the latest price updates by price feed id.\n\nGiven a collection of price feed ids, retrieve the latest Pyth price for each price feed."
52+
title="/v2/updates/price/latest"
53+
method="GET"
54+
description="Get the latest price updates by price feed id."
4755
/>
48-
<Card
56+
<APICard
4957
href="/api-reference/pyth-core/hermes/price_stream_sse_handler"
50-
title="SSE route handler for streaming price updates."
51-
description="SSE route handler for streaming price updates.\n\nThe connection will automatically close after 24 hours to prevent resource leaks.\nClients should implement reconnection logic to maintain continuous price updates."
58+
title="/v2/updates/price/stream"
59+
method="GET"
60+
description="SSE route handler for streaming price updates."
5261
/>
53-
<Card
62+
<APICard
5463
href="/api-reference/pyth-core/hermes/timestamp_price_updates"
55-
title="Get the latest price updates by price feed id."
56-
description="Get the latest price updates by price feed id.\n\nGiven a collection of price feed ids, retrieve the latest Pyth price for each price feed."
64+
title="/v2/updates/price/{publish_time}"
65+
method="GET"
66+
description="Get the latest price updates by price feed id."
5767
/>
58-
<Card
68+
<APICard
5969
href="/api-reference/pyth-core/hermes/latest_publisher_stake_caps"
60-
title="Get the most recent publisher stake caps update data."
70+
title="/v2/updates/publisher_stake_caps/latest"
71+
method="GET"
6172
description="Get the most recent publisher stake caps update data."
6273
/>
63-
<Card
74+
<APICard
6475
href="/api-reference/pyth-core/hermes/latest_twaps"
65-
title="Get the latest TWAP by price feed id with a custom time window."
66-
description="Get the latest TWAP by price feed id with a custom time window.\n\nGiven a collection of price feed ids, retrieve the latest Pyth TWAP price for each price feed."
76+
title="/v2/updates/twap/{window_seconds}/latest"
77+
method="GET"
78+
description="Get the latest TWAP by price feed id with a custom time window."
6779
/>
68-
</Cards>
80+
</APICards>

apps/developer-hub/scripts/generate-docs.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ export async function generateDocs() {
7676

7777
// Post-process MDX files to use endpoint paths as titles
7878
await updateMdxTitles();
79+
80+
// Rewrite index cards to use path as title and first sentence as description
81+
await updateIndexCards();
7982
}
8083

8184
async function generateMetaFiles() {
@@ -186,4 +189,126 @@ async function updateMdxTitles(): Promise<void> {
186189
}
187190
}
188191

192+
async function updateIndexCards(): Promise<void> {
193+
// eslint-disable-next-line no-console
194+
console.log(
195+
"\nUpdating index cards to use path titles + first-sentence descriptions...",
196+
);
197+
198+
for (const [serviceName, config] of Object.entries(products)) {
199+
const serviceDir = path.join(outDir, config.product, serviceName);
200+
const indexPath = path.join(serviceDir, "index.mdx");
201+
202+
try {
203+
await fs.access(indexPath);
204+
} catch {
205+
continue;
206+
}
207+
208+
// Read all endpoint MDX files to extract route, method, and description
209+
const endpoints = generatedEndpoints[serviceName] ?? [];
210+
const cardData: {
211+
href: string;
212+
route: string;
213+
method: string;
214+
description: string;
215+
}[] = [];
216+
217+
for (const operationId of endpoints) {
218+
const mdxPath = path.join(serviceDir, `${operationId}.mdx`);
219+
try {
220+
const content = await fs.readFile(mdxPath, "utf8");
221+
222+
// Extract route
223+
const routeMatch = /route:\s*([^\n]+)/.exec(content);
224+
const route = routeMatch?.[1]?.trim() ?? operationId;
225+
226+
// Extract method
227+
const methodMatch = /method:\s*([^\n]+)/.exec(content);
228+
const method = methodMatch?.[1]?.trim().toUpperCase() ?? "GET";
229+
230+
// Extract description (handle both single-line and multiline formats)
231+
let descText = "";
232+
233+
// Try multiline format first: description: >-\n text
234+
const multilineMatch = /description:\s*>-\s*\n((?:\s{2}.*\n)+)/.exec(
235+
content,
236+
);
237+
if (multilineMatch?.[1]) {
238+
descText = multilineMatch[1]
239+
.split("\n")
240+
.map((line) => line.trim())
241+
.filter(Boolean)
242+
.join(" ");
243+
} else {
244+
// Try single-line format: description: text
245+
const singleMatch = /description:\s*([^\n]+)/.exec(content);
246+
if (singleMatch?.[1]) {
247+
descText = singleMatch[1].trim();
248+
}
249+
}
250+
251+
const firstSentence = getFirstSentence(descText);
252+
253+
cardData.push({
254+
href: `/api-reference/${config.product}/${serviceName}/${operationId}`,
255+
route,
256+
method,
257+
description: firstSentence,
258+
});
259+
} catch {
260+
// Skip if file doesn't exist
261+
}
262+
}
263+
264+
// Build new index content with APICards grid and APICard components
265+
const cards = cardData
266+
.map(
267+
(card) =>
268+
` <APICard href="${card.href}" title="${escapeQuotes(card.route)}" method="${card.method}" description="${escapeQuotes(card.description)}" />`,
269+
)
270+
.join("\n");
271+
272+
const newIndex = `---
273+
title: Overview
274+
---
275+
276+
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
277+
278+
<APICards>
279+
${cards}
280+
</APICards>
281+
`;
282+
283+
await fs.writeFile(indexPath, newIndex);
284+
// eslint-disable-next-line no-console
285+
console.log(
286+
` - Updated index: ${config.product}/${serviceName}/index.mdx`,
287+
);
288+
}
289+
}
290+
291+
function getFirstSentence(text: string): string {
292+
if (!text) return "";
293+
294+
// Remove markdown formatting (bold, italic, etc.)
295+
let cleaned = text.replaceAll(/\*\*[^*]+\*\*/g, "").trim();
296+
297+
// If text starts with "Deprecated..." or similar, skip to the actual description
298+
if (cleaned.toLowerCase().startsWith("deprecated")) {
299+
const afterDeprecated = cleaned.indexOf(")");
300+
if (afterDeprecated > 0) {
301+
cleaned = cleaned.slice(afterDeprecated + 1).trim();
302+
}
303+
}
304+
305+
const sentenceEnd = cleaned.search(/[.!?](\s|$)/);
306+
if (sentenceEnd === -1) return cleaned.trim();
307+
return cleaned.slice(0, sentenceEnd + 1).trim();
308+
}
309+
310+
function escapeQuotes(text: string): string {
311+
return text.replaceAll('"', String.raw`\"`);
312+
}
313+
189314
await generateDocs();
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@use "@pythnetwork/component-library/theme";
2+
3+
.grid {
4+
display: grid;
5+
grid-template-columns: repeat(2, 1fr);
6+
gap: theme.spacing(4);
7+
8+
@include theme.breakpoint("md") {
9+
grid-template-columns: repeat(2, 1fr);
10+
}
11+
12+
@media (width <= 768px) {
13+
grid-template-columns: 1fr;
14+
}
15+
}
16+
17+
.card {
18+
display: flex;
19+
flex-direction: column;
20+
gap: theme.spacing(2);
21+
padding: theme.spacing(5);
22+
border-radius: theme.border-radius("lg");
23+
background-color: theme.color("background", "secondary");
24+
text-decoration: none;
25+
transition: background-color 200ms ease-out;
26+
27+
&:hover {
28+
background-color: theme.color("background", "card-highlight");
29+
}
30+
}
31+
32+
.title {
33+
display: flex;
34+
align-items: center;
35+
gap: theme.spacing(2);
36+
flex-wrap: wrap;
37+
}
38+
39+
.name {
40+
font-size: theme.font-size("sm");
41+
font-weight: theme.font-weight("semibold");
42+
color: theme.color("foreground");
43+
}
44+
45+
.badge {
46+
font-size: theme.font-size("xxs");
47+
font-weight: theme.font-weight("semibold");
48+
text-transform: uppercase;
49+
letter-spacing: theme.letter-spacing("wide");
50+
}
51+
52+
.get {
53+
color: theme.pallette-color("green", 400);
54+
}
55+
56+
.post {
57+
color: theme.pallette-color("amber", 400);
58+
}
59+
60+
.put {
61+
color: theme.pallette-color("blue", 400);
62+
}
63+
64+
.patch {
65+
color: theme.pallette-color("purple", 400);
66+
}
67+
68+
.delete {
69+
color: theme.pallette-color("red", 400);
70+
}
71+
72+
.description {
73+
font-size: theme.font-size("sm");
74+
font-weight: theme.font-weight("normal");
75+
line-height: 1.6;
76+
color: theme.color("muted");
77+
}

0 commit comments

Comments
 (0)