SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
3.9 KB · 88 lines tsx
Raw Blame History
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