diff --git a/apps/playground-web/package.json b/apps/playground-web/package.json index 240ac91fa5a..61397daf003 100644 --- a/apps/playground-web/package.json +++ b/apps/playground-web/package.json @@ -11,7 +11,8 @@ "lint": "eslint ./src", "prefix": "biome check ./src --fix", "fix": "eslint ./src --fix", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "update-insight-blueprints": "bun scripts/updateInsightBlueprints.ts" }, "dependencies": { "@abstract-foundation/agw-client": "^1.6.2", diff --git a/apps/playground-web/scripts/updateInsightBlueprints.ts b/apps/playground-web/scripts/updateInsightBlueprints.ts new file mode 100644 index 00000000000..7dbec1fe402 --- /dev/null +++ b/apps/playground-web/scripts/updateInsightBlueprints.ts @@ -0,0 +1,20 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import { fetchAllBlueprints } from "./utils"; + +const blueprints = await fetchAllBlueprints(); + +const fileContent = `\ +// This file is auto-generated by the update-insight-blueprints script +import type { MinimalBlueprintSpec } from "./utils"; + +export const insightBlueprints: MinimalBlueprintSpec[] = ${JSON.stringify(blueprints, null, 2)}`; + +fs.writeFileSync( + path.join(__dirname, "../src/app/insight/insightBlueprints.ts"), + fileContent, +); + +console.log( + "Updated insightBlueprints.ts - Please format this file and commit it", +); diff --git a/apps/playground-web/scripts/utils.ts b/apps/playground-web/scripts/utils.ts new file mode 100644 index 00000000000..74527fa0376 --- /dev/null +++ b/apps/playground-web/scripts/utils.ts @@ -0,0 +1,56 @@ +import { + type BlueprintListItem, + type MinimalBlueprintSpec, + fetchBlueprintSpec, +} from "../src/app/insight/utils"; + +async function fetchBlueprintList() { + const res = await fetch("https://insight.thirdweb.com/v1/blueprints"); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to fetch blueprints: ${text}`); + } + + const json = (await res.json()) as { data: BlueprintListItem[] }; + + return json.data; +} + +export const fetchAllBlueprints = async (): Promise => { + try { + // fetch list + const blueprintSpecs = await fetchBlueprintList(); + + // fetch all blueprints + const blueprints = await Promise.all( + blueprintSpecs.map((spec) => + fetchBlueprintSpec({ + blueprintId: spec.id, + }), + ), + ); + + return blueprints.map((blueprint) => { + const paths = Object.keys(blueprint.openapiJson.paths); + return { + id: blueprint.id, + name: blueprint.name, + paths: paths.map((pathName) => { + const pathObj = blueprint.openapiJson.paths[pathName]; + if (!pathObj) { + throw new Error(`Path not found: ${pathName}`); + } + + return { + name: pathObj.get?.summary || "Unknown", + path: pathName, + }; + }), + } satisfies MinimalBlueprintSpec; + }); + } catch (error) { + console.error(error); + return []; + } +}; diff --git a/apps/playground-web/src/app/insight/insightBlueprints.ts b/apps/playground-web/src/app/insight/insightBlueprints.ts new file mode 100644 index 00000000000..7d996fe5563 --- /dev/null +++ b/apps/playground-web/src/app/insight/insightBlueprints.ts @@ -0,0 +1,187 @@ +// This file is auto-generated by the update-insight-blueprints script +import type { MinimalBlueprintSpec } from "./utils"; + +export const insightBlueprints: MinimalBlueprintSpec[] = [ + { + id: "transactions", + name: "Transactions", + paths: [ + { + name: "Get transactions", + path: "/v1/transactions", + }, + { + name: "Get contract transactions", + path: "/v1/transactions/:contractAddress", + }, + { + name: "Get contract transactions with specific signature", + path: "/v1/transactions/:contractAddress/:signature", + }, + { + name: "Get wallet transactions", + path: "/v1/wallets/:wallet_address/transactions", + }, + ], + }, + { + id: "events", + name: "Events", + paths: [ + { + name: "Get events", + path: "/v1/events", + }, + { + name: "Get contract events", + path: "/v1/events/:contractAddress", + }, + { + name: "Get contract events with specific signature", + path: "/v1/events/:contractAddress/:signature", + }, + ], + }, + { + id: "tokens", + name: "Tokens", + paths: [ + { + name: "Get token transfers by transaction", + path: "/v1/tokens/transfers/transaction/:transaction_hash", + }, + { + name: "Get token transfers by contract", + path: "/v1/tokens/transfers/:contract_address", + }, + { + name: "Get token transfers by wallet address", + path: "/v1/tokens/transfers", + }, + { + name: "Get ERC-20 balances by address", + path: "/v1/tokens/erc20/:ownerAddress", + }, + { + name: "Get ERC-721 balances by address", + path: "/v1/tokens/erc721/:ownerAddress", + }, + { + name: "Get ERC-1155 balances by address", + path: "/v1/tokens/erc1155/:ownerAddress", + }, + { + name: "Get supported tokens for price data", + path: "/v1/tokens/price/supported", + }, + { + name: "Get token price", + path: "/v1/tokens/price", + }, + ], + }, + { + id: "resolve", + name: "Resolve", + paths: [ + { + name: "Resolve", + path: "/v1/resolve/:input", + }, + ], + }, + { + id: "blocks", + name: "Blocks", + paths: [ + { + name: "Get blocks", + path: "/v1/blocks", + }, + ], + }, + { + id: "contracts", + name: "Contracts", + paths: [ + { + name: "Get contract ABI​", + path: "/v1/contracts/abi/:contractAddress", + }, + { + name: "Get contract metadata​", + path: "/v1/contracts/metadata/:contractAddress", + }, + ], + }, + { + id: "decode", + name: "Decode", + paths: [ + { + name: "Unknown", + path: "/v1/decode/:contractAddress", + }, + ], + }, + { + id: "nfts", + name: "Nfts", + paths: [ + { + name: "Get NFTs by owner", + path: "/v1/nfts", + }, + { + name: "Get NFT owners by contract", + path: "/v1/nfts/owners/:contract_address", + }, + { + name: "Get NFT owners by token", + path: "/v1/nfts/owners/:contract_address/:token_id", + }, + { + name: "Get NFT transfers by owner", + path: "/v1/nfts/transfers", + }, + { + name: "Get NFT transfers by transaction", + path: "/v1/nfts/transfers/transaction/:transaction_hash", + }, + { + name: "Get NFT transfers by contract", + path: "/v1/nfts/transfers/:contract_address", + }, + { + name: "Get NFTs by contract", + path: "/v1/nfts/:contract_address", + }, + { + name: "Get NFT transfers by token", + path: "/v1/nfts/transfers/:contract_address/:token_id", + }, + { + name: "Get NFT by token", + path: "/v1/nfts/:contract_address/:token_id", + }, + { + name: "Force refresh collection metadata", + path: "/v1/nfts/metadata/refresh/:contract_address", + }, + { + name: "Force refresh token metadata", + path: "/v1/nfts/metadata/refresh/:contract_address/:token_id", + }, + ], + }, + { + id: "wallets", + name: "Wallets", + paths: [ + { + name: "Get wallet transactions", + path: "/v1/wallets/:wallet_address/transactions", + }, + ], + }, +]; diff --git a/apps/playground-web/src/app/insight/page.tsx b/apps/playground-web/src/app/insight/page.tsx index 0b023ded214..c07db74c430 100644 --- a/apps/playground-web/src/app/insight/page.tsx +++ b/apps/playground-web/src/app/insight/page.tsx @@ -1,11 +1,8 @@ import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; -import {} from "lucide-react"; import Link from "next/link"; -import { fetchAllBlueprints } from "./utils"; - -export default async function Page() { - const blueprints = await fetchAllBlueprints(); +import { insightBlueprints } from "./insightBlueprints"; +export default function Page() { return (

Blueprints

@@ -23,22 +20,15 @@ export default async function Page() {

- {blueprints.map((blueprint) => { - const paths = Object.keys(blueprint.openapiJson.paths); - + {insightBlueprints.map((blueprint) => { return ( { - const pathObj = blueprint.openapiJson.paths[pathName]; - if (!pathObj) { - throw new Error(`Path not found: ${pathName}`); - } + blueprints={blueprint.paths.map((pathInfo) => { return { - name: pathObj.get?.summary || "Unknown", - link: `/insight/${blueprint.id}?path=${pathName}`, + name: pathInfo.name, + link: `/insight/${blueprint.id}?path=${pathInfo.path}`, }; })} /> @@ -51,7 +41,6 @@ export default async function Page() { function BlueprintSection(props: { title: string; - blueprintId: string; blueprints: { name: string; link: string }[]; }) { return ( diff --git a/apps/playground-web/src/app/insight/utils.ts b/apps/playground-web/src/app/insight/utils.ts index a9503422a65..6260c0fe90b 100644 --- a/apps/playground-web/src/app/insight/utils.ts +++ b/apps/playground-web/src/app/insight/utils.ts @@ -1,47 +1,38 @@ -import "server-only"; +// Note: This file is also used in the update-insight-blueprints script +// Do not use Next.js/Vercel specific APIs or envs here import type { OpenAPIV3 } from "openapi-types"; -import { isProd } from "../../lib/env"; -type BlueprintListItem = { +export type BlueprintParameter = OpenAPIV3.ParameterObject; +export type BlueprintPathMetadata = OpenAPIV3.PathItemObject; + +export type BlueprintListItem = { id: string; name: string; - description: string; slug: string; }; -const thirdwebDomain = !isProd ? "thirdweb-dev" : "thirdweb"; - -async function fetchBlueprintList() { - const res = await fetch( - `https://insight.${thirdwebDomain}.com/v1/blueprints`, - ); - - if (!res.ok) { - const text = await res.text(); - throw new Error(`Failed to fetch blueprints: ${text}`); - } - - const json = (await res.json()) as { data: BlueprintListItem[] }; - - return json.data; -} - -export type BlueprintParameter = OpenAPIV3.ParameterObject; -export type BlueprintPathMetadata = OpenAPIV3.PathItemObject; - -type BlueprintSpec = { +export type BlueprintSpec = { id: string; name: string; description: string; openapiJson: OpenAPIV3.Document; }; +export type MinimalBlueprintSpec = { + id: string; + name: string; + paths: { + name: string; + path: string; + }[]; +}; + export async function fetchBlueprintSpec(params: { blueprintId: string; }) { const res = await fetch( - `https://insight.${thirdwebDomain}.com/v1/blueprints/${params.blueprintId}`, + `https://insight.thirdweb.com/v1/blueprints/${params.blueprintId}`, ); if (!res.ok) { @@ -53,24 +44,3 @@ export async function fetchBlueprintSpec(params: { return json.data; } - -export async function fetchAllBlueprints() { - try { - // fetch list - const blueprintSpecs = await fetchBlueprintList(); - - // fetch all blueprints - const blueprints = await Promise.all( - blueprintSpecs.map((spec) => - fetchBlueprintSpec({ - blueprintId: spec.id, - }), - ), - ); - - return blueprints; - } catch (error) { - console.error(error); - return []; - } -} diff --git a/apps/playground-web/src/app/navLinks.ts b/apps/playground-web/src/app/navLinks.ts index 942135872e6..000dd9a7aa5 100644 --- a/apps/playground-web/src/app/navLinks.ts +++ b/apps/playground-web/src/app/navLinks.ts @@ -1,5 +1,5 @@ import type { SidebarLink } from "../components/ui/sidebar"; -import { fetchAllBlueprints } from "./insight/utils"; +import { insightBlueprints } from "./insight/insightBlueprints"; export const staticSidebarLinks: SidebarLink[] = [ { @@ -150,22 +150,15 @@ const engineSidebarLinks: SidebarLink = { ], }; -export async function getSidebarLinks() { - const insightBlueprints = await fetchAllBlueprints(); - +export function getSidebarLinks() { const insightLinks: SidebarLink[] = insightBlueprints.map((blueprint) => { - const paths = Object.keys(blueprint.openapiJson.paths); return { name: blueprint.name, expanded: false, - links: paths.map((pathName) => { - const pathObj = blueprint.openapiJson.paths[pathName]; - if (!pathObj) { - throw new Error(`Path not found: ${pathName}`); - } + links: blueprint.paths.map((pathInfo) => { return { - name: pathObj.get?.summary || pathName, - href: `/insight/${blueprint.id}?path=${pathName}`, + name: pathInfo.name, + href: `/insight/${blueprint.id}?path=${pathInfo.path}`, }; }), };