Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
120 changes: 118 additions & 2 deletions apps/developer-hub/src/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,121 @@
import { createFromSource } from "fumadocs-core/search/server";
import type { AdvancedIndex } from "fumadocs-core/search/server";
import { createSearchAPI } from "fumadocs-core/search/server";
import { z } from "zod";

import { source } from "../../../lib/source";

export const { GET } = createFromSource(source);
// Define schemas for type safety
const hermesSchema = z.array(
z.object({
id: z.string(),
attributes: z.object({ symbol: z.string() }),
}),
);

const lazerSchema = z.array(
z.object({
symbol: z.string(),
name: z.string(),
pyth_lazer_id: z.number(),
description: z.string(),
}),
);

async function getHermesFeeds(): Promise<AdvancedIndex[]> {
try {
const endpoints = [
"https://hermes.pyth.network",
"https://hermes-beta.pyth.network",
];
const responses = await Promise.all(
endpoints.map((url) =>
fetch(`${url}/v2/price_feeds`, {
next: { revalidate: 3600 },
}).then((res) => res.json() as Promise<unknown>),
),
);

const allFeeds: AdvancedIndex[] = [];

for (const data of responses) {
const parsed = hermesSchema.safeParse(data);
if (parsed.success) {
for (const feed of parsed.data) {
allFeeds.push({
title: `${feed.attributes.symbol} (Core)`,
description: `Price Feed ID: ${feed.id}`,
url: `/price-feeds/core/price-feeds/price-feed-ids?search=${feed.attributes.symbol}`,
id: feed.id,
tag: "price-feed-core",
structuredData: {
headings: [],
contents: [
{ heading: "Symbol", content: feed.attributes.symbol },
{ heading: "ID", content: feed.id },
],
},
});
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const endpoints = [
"https://hermes.pyth.network",
"https://hermes-beta.pyth.network",
];
const responses = await Promise.all(
endpoints.map((url) =>
fetch(`${url}/v2/price_feeds`, {
next: { revalidate: 3600 },
}).then((res) => res.json() as Promise<unknown>),
),
);
const allFeeds: AdvancedIndex[] = [];
for (const data of responses) {
const parsed = hermesSchema.safeParse(data);
if (parsed.success) {
for (const feed of parsed.data) {
allFeeds.push({
title: `${feed.attributes.symbol} (Core)`,
description: `Price Feed ID: ${feed.id}`,
url: `/price-feeds/core/price-feeds/price-feed-ids?search=${feed.attributes.symbol}`,
id: feed.id,
tag: "price-feed-core",
structuredData: {
headings: [],
contents: [
{ heading: "Symbol", content: feed.attributes.symbol },
{ heading: "ID", content: feed.id },
],
},
});
}
}
}
const [hermesFeeds, hermesBetaFeeds] = await Promise.all(
[HERMES_URL, HERMES_BETA_URL].map((url) => {
const hermesResult = await fetch(new URL('/v2/price_feeds', url), {
next: { revalidate: 3600 },
});
const parsed = hermesSchema.safeParse(await hermesResult.json());
return parsed.success
? parsed.data.map(feed => toAdvancedIndex(feed))
: undefined
})
);
return [...hermesFeeds, ...hermesBetaFeeds];

And then just add a toAdvancedIndex: (fee: z.infer<typeof hermesSchema>[number]) => AdvancedFeed function.

Simpler, requires less procedural / mutable object building, and keeps processing close to data fetching.


return allFeeds;
} catch (error: unknown) {
throw new Error("Failed to fetch Hermes feeds", { cause: error });
}
}

async function getLazerFeeds(): Promise<AdvancedIndex[]> {
try {
const res = await fetch(
"https://history.pyth-lazer.dourolabs.app/history/v1/symbols",
{ next: { revalidate: 3600 } },
);
const data = (await res.json()) as unknown;
const parsed = lazerSchema.safeParse(data);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const data = (await res.json()) as unknown;
const parsed = lazerSchema.safeParse(data);
const parsed = lazerSchema.safeParse(await res.json());

Little trick when using zod schemas with fetch that avoids the awkward need to typecast to unknown


if (!parsed.success) {
return [];
}

return parsed.data.map((feed) => ({
title: `${feed.name} (Pro)`,
description: `${feed.symbol} - ${feed.description} (ID: ${String(feed.pyth_lazer_id)})`,
url: `/price-feeds/pro/price-feed-ids?search=${feed.symbol}`,
id: `lazer-${String(feed.pyth_lazer_id)}`,
tag: "price-feed-pro",
structuredData: {
headings: [],
contents: [
{ heading: "Symbol", content: feed.symbol },
{ heading: "Name", content: feed.name },
{ heading: "Description", content: feed.description },
{ heading: "ID", content: String(feed.pyth_lazer_id) },
],
},
}));
} catch (error: unknown) {
throw new Error("Failed to fetch Lazer feeds", { cause: error });
}
}

export const { GET } = createSearchAPI("advanced", {
indexes: async () => {
const staticPages = source.getPages().map((page) => ({
title: page.data.title,
description: page.data.description,
url: page.url,
id: page.url,
structuredData: page.data.structuredData,
})) as AdvancedIndex[];

// Added these two functions to get the price feeds from the Hermes and Pro APIs
const [hermesFeeds, lazerFeeds] = await Promise.all([
getHermesFeeds(),
getLazerFeeds(),
]);

// Combine the static pages, Hermes feeds, and Pro feeds
return [...staticPages, ...hermesFeeds, ...lazerFeeds];
},
});
6 changes: 3 additions & 3 deletions apps/developer-hub/src/components/Pages/Homepage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,21 @@ export const Homepage = () => {
title="Pyth Token"
description="The native token powering governance and staking across the Pyth Network."
url="/pyth-token"
urlLabel="Link"
urlLabel="Read more"
image={<SignalImage />}
/>
<SectionCard
title="Oracle Integrity Staking"
description="Stake PYTH to support data publishers and secure the integrity of Pyth price feeds."
url="/oracle-integrity-staking"
urlLabel="Link"
urlLabel="Read more"
image={<SignalImage />}
/>
<SectionCard
title="Pyth Metrics"
description="Track network performance, feed activity, and ecosystem growth in real time."
url="/metrics"
urlLabel="Link"
urlLabel="Read more"
image={<SignalImage />}
/>
</SectionCards>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const PriceFeedIdsProTable = () => {
return items;
}
return matchSorter(items, searchString, {
keys: ["pyth_lazer_id"],
keys: ["pyth_lazer_id", "symbol", "name", "description"],
});
},
{
Expand Down
Loading