TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import type { Metadata } from "next";2import Link from "next/link";3import { BookOpen } from "lucide-react";4import { COUNTRIES, PLAN_LIMITS, normalizePlan } from "@fetcha/core";5import { Button } from "@/components/ui/button";6import { PageHeader } from "@/components/ui/page-header";7import { Playground } from "@/components/playground/playground";8import { FORMATS, HTTP_METHODS, NETWORKS, type BuilderState } from "@/components/playground/types";9import { internalApi } from "@/lib/api";10import { getReplayRequest, listPlaygroundKeys } from "@/lib/queries/playground";11import { getWorkspace } from "@/lib/session";12import { API_PUBLIC_URL } from "@/lib/utils";1314export const dynamic = "force-dynamic";15export const metadata: Metadata = { title: "Playground" };1617type SearchParams = Promise<{ url?: string | string[]; request?: string | string[] }>;1819function first(v: string | string[] | undefined): string | undefined {20 return Array.isArray(v) ? v[0] : v;21}2223export default async function PlaygroundPage({ searchParams }: { searchParams: SearchParams }) {24 const [sp, ws] = await Promise.all([searchParams, getWorkspace()]);25 const limits = PLAN_LIMITS[normalizePlan(ws.organization.plan)];2627 const replayId = first(sp.request)?.trim();28 const prefillUrl = first(sp.url)?.trim();2930 const [keys, ready, replay] = await Promise.all([31 listPlaygroundKeys(ws.project.id, ws.organization.id),32 internalApi.ready().catch(() => ({ status: "unknown", checks: {}, available_networks: [] as string[] })),33 replayId ? getReplayRequest(ws.project.id, replayId).catch(() => null) : Promise.resolve(null),34 ]);3536 let initial: Partial<BuilderState> | null = null;37 if (replay) {38 initial = {39 url: replay.url,40 method: (HTTP_METHODS as readonly string[]).includes(replay.method) ? (replay.method as BuilderState["method"]) : "GET",41 network: (NETWORKS as readonly string[]).includes(replay.requestedNetwork) ? (replay.requestedNetwork as BuilderState["network"]) : "auto",42 country: replay.country ?? "",43 region: replay.region ?? "",44 city: replay.city ?? "",45 format: (FORMATS as readonly string[]).includes(replay.format) ? (replay.format as BuilderState["format"]) : "html",46 };47 } else if (prefillUrl) {48 initial = { url: prefillUrl.slice(0, 8192) };49 }5051 const projectDefaults: Partial<BuilderState> = {52 country: ws.project.defaultCountry ?? "",53 network: (NETWORKS as readonly string[]).includes(ws.project.defaultNetwork) ? (ws.project.defaultNetwork as BuilderState["network"]) : "auto",54 };5556 const countries = Object.entries(COUNTRIES)57 .map(([code, name]) => ({ code, name }))58 .sort((a, b) => a.name.localeCompare(b.name));5960 return (61 <>62 <PageHeader63 eyebrow={ws.project.name}64 title="Playground"65 description="Send a request through Fetcha and inspect the response, the network route it took and where the time went. Turn on browser rendering for JavaScript-heavy pages. Playground requests appear in the request log."66 actions={67 <Button variant="outline" size="sm" asChild>68 <Link href="/docs">69 <BookOpen className="size-3.5" /> API reference70 </Link>71 </Button>72 }73 />74 <Playground75 keys={keys}76 plan={{ label: limits.label, concurrency: limits.concurrency, max_timeout_ms: limits.max_timeout_ms, max_retries: limits.max_retries, networks: limits.networks, browser_concurrency: limits.browser_concurrency }}77 availableNetworks={ready.available_networks ?? []}78 countries={countries}79 baseUrl={API_PUBLIC_URL}80 projectDefaults={projectDefaults}81 initial={initial}82 replayId={replay?.id ?? null}83 />84 {replayId && !replay ? <p className="mt-4 text-xs text-fg-subtle">Request <span className="font-mono">{replayId}</span> was not found in this project, so nothing was prefilled.</p> : null}85 </>86 );87}88