diff --git a/packages/explorer/next.config.mjs b/packages/explorer/next.config.mjs index cffb1b98ba..7811d5e71d 100644 --- a/packages/explorer/next.config.mjs +++ b/packages/explorer/next.config.mjs @@ -10,10 +10,15 @@ export default function config() { }, redirects: async () => { return [ + { + source: "/worlds/:path*", + destination: "/anvil/worlds/:path*", + permanent: false, + }, { source: "/:chainName/worlds/:worldAddress/explorer", destination: "/:chainName/worlds/:worldAddress/explore", - permanent: true, + permanent: false, }, ]; }, diff --git a/packages/explorer/src/app/error.tsx b/packages/explorer/src/app/(explorer)/error.tsx similarity index 88% rename from packages/explorer/src/app/error.tsx rename to packages/explorer/src/app/(explorer)/error.tsx index 844d3e5723..c599bd6ab8 100644 --- a/packages/explorer/src/app/error.tsx +++ b/packages/explorer/src/app/(explorer)/error.tsx @@ -2,8 +2,8 @@ import { ExternalLink, RefreshCwIcon } from "lucide-react"; import Link from "next/link"; -import { Button } from "../components/ui/Button"; -import { useWorldUrl } from "../hooks/useWorldUrl"; +import { Button } from "../../components/ui/Button"; +import { useWorldUrl } from "../../hooks/useWorldUrl"; type Props = { error: Error & { digest?: string }; @@ -14,7 +14,7 @@ export default function Error({ reset, error }: Props) { const getUrl = useWorldUrl(); return (
-

400

+

500

Something went wrong :(

{error.message && ( diff --git a/packages/explorer/src/app/globals.css b/packages/explorer/src/app/(explorer)/globals.css similarity index 100% rename from packages/explorer/src/app/globals.css rename to packages/explorer/src/app/(explorer)/globals.css diff --git a/packages/explorer/src/app/layout.tsx b/packages/explorer/src/app/(explorer)/layout.tsx similarity index 96% rename from packages/explorer/src/app/layout.tsx rename to packages/explorer/src/app/(explorer)/layout.tsx index 1dd8395953..533d78abcf 100644 --- a/packages/explorer/src/app/layout.tsx +++ b/packages/explorer/src/app/(explorer)/layout.tsx @@ -19,9 +19,6 @@ const jetbrains = JetBrains_Mono({ export const metadata: Metadata = { title: "World Explorer", description: "World Explorer is a tool for visually exploring and manipulating the state of worlds", - icons: { - icon: "/favicon.svg", - }, }; export default function RootLayout({ diff --git a/packages/explorer/src/app/not-found.tsx b/packages/explorer/src/app/(explorer)/not-found.tsx similarity index 90% rename from packages/explorer/src/app/not-found.tsx rename to packages/explorer/src/app/(explorer)/not-found.tsx index a4cddf5996..44059b412c 100644 --- a/packages/explorer/src/app/not-found.tsx +++ b/packages/explorer/src/app/(explorer)/not-found.tsx @@ -2,8 +2,8 @@ import { ExternalLink } from "lucide-react"; import Link from "next/link"; -import { Button } from "../components/ui/Button"; -import { useWorldUrl } from "../hooks/useWorldUrl"; +import { Button } from "../../components/ui/Button"; +import { useWorldUrl } from "../../hooks/useWorldUrl"; export default function NotFound() { const getUrl = useWorldUrl(); diff --git a/packages/explorer/src/app/page.tsx b/packages/explorer/src/app/(explorer)/page.tsx similarity index 67% rename from packages/explorer/src/app/page.tsx rename to packages/explorer/src/app/(explorer)/page.tsx index cfa56011be..99c90345a5 100644 --- a/packages/explorer/src/app/page.tsx +++ b/packages/explorer/src/app/(explorer)/page.tsx @@ -1,5 +1,5 @@ import { redirect } from "next/navigation"; -import { supportedChainsById, validateChainId } from "../common"; +import { chainIdToName, validateChainId } from "../../common"; export const dynamic = "force-dynamic"; @@ -7,6 +7,6 @@ export default function IndexPage() { const chainId = Number(process.env.CHAIN_ID); validateChainId(chainId); - const chainName = supportedChainsById[chainId]; + const chainName = chainIdToName[chainId] ?? "anvil"; return redirect(`/${chainName}/worlds`); } diff --git a/packages/explorer/src/app/api/world/route.ts b/packages/explorer/src/app/api/world/route.ts index 650dee9bad..4e4aa12ce9 100644 --- a/packages/explorer/src/app/api/world/route.ts +++ b/packages/explorer/src/app/api/world/route.ts @@ -3,12 +3,12 @@ import { getBlockNumber, getLogs } from "viem/actions"; import { helloStoreEvent } from "@latticexyz/store"; import { helloWorldEvent } from "@latticexyz/world"; import { getWorldAbi } from "@latticexyz/world/internal"; -import { SupportedChainIds, supportedChainsById, validateChainId } from "../../../common"; +import { supportedChainId, supportedChains, validateChainId } from "../../../common"; export const dynamic = "force-dynamic"; -async function getClient(chainId: SupportedChainIds) { - const chain = supportedChainsById[chainId]; +async function getClient(chainId: supportedChainId) { + const chain = Object.values(supportedChains).find((c) => c.id === chainId); const client = createWalletClient({ chain, transport: http(), @@ -17,7 +17,7 @@ async function getClient(chainId: SupportedChainIds) { return client; } -async function getParameters(chainId: SupportedChainIds, worldAddress: Address) { +async function getParameters(chainId: supportedChainId, worldAddress: Address) { const client = await getClient(chainId); const toBlock = await getBlockNumber(client); const logs = await getLogs(client, { diff --git a/packages/explorer/src/common.ts b/packages/explorer/src/common.ts index de77ede488..f139856721 100644 --- a/packages/explorer/src/common.ts +++ b/packages/explorer/src/common.ts @@ -1,21 +1,23 @@ import { anvil, garnet, redstone } from "viem/chains"; export const supportedChains = { anvil, garnet, redstone } as const; -export const supportedChainsById = Object.fromEntries( - Object.entries(supportedChains).map(([, chain]) => [chain.id, chain]), -); +export type supportedChains = typeof supportedChains; + +export type supportedChainName = keyof supportedChains; +export type supportedChainId = supportedChains[supportedChainName]["id"]; -export type SupportedChainIds = (typeof supportedChains)[keyof typeof supportedChains]["id"]; -export type SupportedChainNames = keyof typeof supportedChains; +export const chainIdToName = Object.fromEntries( + Object.entries(supportedChains).map(([chainName, chain]) => [chain.id, chainName]), +); -export function validateChainId(chainId: number): asserts chainId is SupportedChainIds { - if (!(chainId in supportedChainsById)) { - throw new Error(`Invalid chain id. Supported chains are: ${Object.keys(supportedChainsById).join(", ")}.`); +export function validateChainId(chainId: unknown): asserts chainId is supportedChainId { + if (!(typeof chainId === "number" && chainId in chainIdToName)) { + throw new Error(`Invalid chain ID. Supported chains are: ${Object.keys(chainIdToName).join(", ")}.`); } } -export function validateChainName(name: string | string[] | undefined): asserts name is SupportedChainNames { - if (Array.isArray(name) || typeof name !== "string" || !(name in supportedChains)) { - throw new Error(`Invalid chain name. Supported chains are: ${Object.keys(supportedChainsById).join(", ")}.`); +export function validateChainName(name: unknown): asserts name is supportedChainName { + if (!(typeof name === "string" && name in supportedChains)) { + throw new Error(`Invalid chain name. Supported chains are: ${Object.keys(supportedChains).join(", ")}.`); } }