diff --git a/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx b/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx
deleted file mode 100644
index 4db35034f0..0000000000
--- a/packages/ui/app/src/playground/PlaygroundAuthorizationForm.tsx
+++ /dev/null
@@ -1,594 +0,0 @@
-import type { EndpointContext } from "@fern-api/fdr-sdk/api-definition";
-import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
-import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
-import {
- FernButton,
- FernCard,
- FernCollapse,
- FernDropdown,
- FernInput,
- FernSegmentedControl,
- FernTooltip,
- FernTooltipProvider,
-} from "@fern-ui/components";
-import { useBooleanState } from "@fern-ui/react-commons";
-import { HelpCircle, Key, User } from "iconoir-react";
-import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
-import { useSearchParams } from "next/navigation";
-import { FC, ReactElement, SetStateAction, useCallback, useEffect, useState } from "react";
-import urlJoin from "url-join";
-import { useMemoOne } from "use-memo-one";
-import {
- PLAYGROUND_AUTH_STATE_ATOM,
- PLAYGROUND_AUTH_STATE_BASIC_AUTH_ATOM,
- PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM,
- PLAYGROUND_AUTH_STATE_HEADER_ATOM,
- PLAYGROUND_AUTH_STATE_OAUTH_ATOM,
- usePlaygroundEndpointFormState,
-} from "../atoms";
-import { useApiRoute } from "../hooks/useApiRoute";
-import { useStandardProxyEnvironment } from "../hooks/useStandardProxyEnvironment";
-import { Callout } from "../mdx/components/callout";
-import { useApiKeyInjectionConfig } from "../services/useApiKeyInjectionConfig";
-import { PasswordInputGroup } from "./PasswordInputGroup";
-import { PlaygroundEndpointForm } from "./endpoint/PlaygroundEndpointForm";
-import { useOAuthEndpointContext } from "./hooks/useOauthEndpointContext";
-import { PlaygroundAuthState } from "./types";
-import { oAuthClientCredentialReferencedEndpointLoginFlow } from "./utils/oauth";
-import { usePlaygroundBaseUrl } from "./utils/select-environment";
-
-interface PlaygroundAuthorizationFormProps {
- auth: APIV1Read.ApiAuth;
- closeContainer: () => void;
- disabled: boolean;
-}
-
-function BearerAuthForm({ bearerAuth, disabled }: { bearerAuth: APIV1Read.BearerAuth; disabled?: boolean }) {
- const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM);
- const handleChange = useCallback((newValue: string) => setValue({ token: newValue }), [setValue]);
-
- return (
-
-
-
-
-
- );
-}
-
-function BasicAuthForm({ basicAuth, disabled }: { basicAuth: APIV1Read.BasicAuth; disabled?: boolean }) {
- const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_BASIC_AUTH_ATOM);
- const handleChangeUsername = useCallback(
- (newValue: string) => setValue((prev) => ({ ...prev, username: newValue })),
- [setValue],
- );
- const handleChangePassword = useCallback(
- (newValue: string) => setValue((prev) => ({ ...prev, password: newValue })),
- [setValue],
- );
- return (
- <>
-
-
-
- }
- rightElement={{"string"}}
- disabled={disabled}
- />
-
-
-
-
-
-
-
-
- >
- );
-}
-
-function HeaderAuthForm({ header, disabled }: { header: APIV1Read.HeaderAuth; disabled?: boolean }) {
- const [value, setValue] = useAtom(
- useMemoOne(
- () =>
- atom(
- (get) => get(PLAYGROUND_AUTH_STATE_HEADER_ATOM).headers[header.headerWireValue],
- (_get, set, change: SetStateAction) => {
- set(PLAYGROUND_AUTH_STATE_HEADER_ATOM, ({ headers }) => ({
- headers: {
- ...headers,
- [header.headerWireValue]:
- typeof change === "function"
- ? change(headers[header.headerWireValue] ?? "")
- : change,
- },
- }));
- },
- ),
- [header.headerWireValue],
- ),
- );
-
- return (
-
-
-
-
- );
-}
-
-function FoundOAuthReferencedEndpointForm({
- context,
- oAuthClientCredentialsReferencedEndpoint,
- closeContainer,
- disabled,
-}: {
- /**
- * this must be the OAuth endpoint.
- */
- context: EndpointContext;
- oAuthClientCredentialsReferencedEndpoint: APIV1Read.OAuthClientCredentialsReferencedEndpoint;
- closeContainer: () => void;
- disabled?: boolean;
-}) {
- const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_OAUTH_ATOM);
- const proxyEnvironment = useStandardProxyEnvironment();
- const [formState, setFormState] = usePlaygroundEndpointFormState(context);
- const [baseUrl] = usePlaygroundBaseUrl(context.endpoint);
-
- const [displayFailedLogin, setDisplayFailedLogin] = useState(false);
-
- /**
- * TODO: turn this into a loadable (suspense)
- */
- const oAuthClientCredentialLogin = async () => {
- setValue((prev) => ({ ...prev, isLoggingIn: true }));
- await oAuthClientCredentialReferencedEndpointLoginFlow({
- formState,
- endpoint: context.endpoint,
- proxyEnvironment,
- oAuthClientCredentialsReferencedEndpoint,
- baseUrl,
- setValue,
- closeContainer,
- setDisplayFailedLogin,
- });
- setValue((prev) => ({ ...prev, isLoggingIn: false }));
- };
-
- const authenticationOptions: FernDropdown.Option[] = [
- { type: "value", value: "credentials", label: "Credentials", icon: },
- { type: "value", value: "token", label: "Bearer Token", icon: },
- ];
-
- return value.isLoggingIn ? (
- Loading...
- ) : (
- <>
-
- {
- if (value != null && value.length > 0) {
- setValue((prev) => ({ ...prev, selectedInputMethod: value as "credentials" | "token" }));
- }
- }}
- value={value.selectedInputMethod}
- disabled={disabled}
- />
-
-
- {value.selectedInputMethod === "credentials" ? (
- <>
-
-
-
-
- {displayFailedLogin && (
- Failed to login with the provided credentials
- )}
- {value.isLoggedIn && (
-
-
-
-
-
-
-
-
- )}
- {value.isLoggedIn && value.accessToken !== value.loggedInStartingToken && (
-
- The bearer token is no longer valid. Please refresh it by clicking the button below
-
- )}
- >
- ) : (
- <>
-
-
-
-
- setValue((prev) => ({ ...prev, userSuppliedAccessToken: newValue }))
- }
- value={value.userSuppliedAccessToken}
- autoComplete="off"
- data-1p-ignore="true"
- disabled={disabled}
- />
-
- >
- )}
-
- {value.selectedInputMethod === "credentials" && (
-
- )}
-
- >
- );
-}
-
-function OAuthReferencedEndpointForm({
- referencedEndpoint,
- oAuthClientCredentialsReferencedEndpoint,
- closeContainer,
- disabled,
-}: {
- referencedEndpoint: APIV1Read.OAuthClientCredentials.ReferencedEndpoint;
- oAuthClientCredentialsReferencedEndpoint: APIV1Read.OAuthClientCredentialsReferencedEndpoint;
- closeContainer: () => void;
- disabled?: boolean;
-}) {
- const { context, isLoading } = useOAuthEndpointContext(referencedEndpoint);
-
- if (context == null) {
- if (!isLoading) {
- // eslint-disable-next-line no-console
- console.error("Could not find OAuth endpoint for referenced endpoint", referencedEndpoint);
- }
- return ;
- }
-
- return (
-
- );
-}
-
-function OAuthForm({
- oAuth,
- closeContainer,
- disabled,
-}: {
- oAuth: APIV1Read.ApiAuth.OAuth;
- closeContainer: () => void;
- disabled?: boolean;
-}) {
- return visitDiscriminatedUnion(oAuth.value, "type")._visit({
- clientCredentials: (clientCredentials) => {
- return visitDiscriminatedUnion(clientCredentials.value, "type")._visit({
- referencedEndpoint: (referencedEndpoint) => {
- return (
-
- );
- },
- _other: () => null,
- });
- },
- _other: () => null,
- });
-}
-
-export const PlaygroundAuthorizationForm: FC = ({
- auth,
- closeContainer,
- disabled,
-}) => {
- return (
-
- {visitDiscriminatedUnion(auth, "type")._visit({
- bearerAuth: (bearerAuth) => ,
- basicAuth: (basicAuth) => ,
- header: (header) => ,
- oAuth: (oAuth) => ,
- _other: () => null,
- })}
-
- );
-};
-
-interface PlaygroundAuthorizationFormCardProps {
- auth: APIV1Read.ApiAuth;
- disabled: boolean;
-}
-
-export function PlaygroundAuthorizationFormCard({
- auth,
- disabled,
-}: PlaygroundAuthorizationFormCardProps): ReactElement | null {
- const authState = useAtomValue(PLAYGROUND_AUTH_STATE_ATOM);
- const setBearerAuth = useSetAtom(PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM);
- const oAuth = useAtomValue(PLAYGROUND_AUTH_STATE_OAUTH_ATOM);
- const isOpen = useBooleanState(false);
- const apiKeyInjection = useApiKeyInjectionConfig();
- const apiKey = apiKeyInjection.enabled && apiKeyInjection.authenticated ? apiKeyInjection.access_token : null;
- const searchParams = useSearchParams();
- const error = searchParams.get("error");
- const errorDescription = searchParams.get("error_description");
-
- const handleResetBearerAuth = useCallback(() => {
- setBearerAuth({ token: apiKey ?? "" });
- }, [apiKey, setBearerAuth]);
-
- const logoutApiRoute = useApiRoute("/api/fern-docs/auth/logout");
-
- const redirectOrOpenAuthForm = () => {
- if (apiKeyInjection.enabled && !apiKeyInjection.authenticated) {
- const url = new URL(apiKeyInjection.authorizationUrl);
- const state = new URL(window.location.href);
- if (state.searchParams.has("error")) {
- state.searchParams.delete("error");
- }
- if (state.searchParams.has("error_description")) {
- state.searchParams.delete("error_description");
- }
- url.searchParams.set(apiKeyInjection.returnToQueryParam, state.toString());
- window.location.replace(url);
- } else {
- isOpen.toggleValue();
- }
- };
-
- const authButtonCopy = apiKeyInjection.enabled
- ? "Login to send a real request"
- : `Authenticate with your ${
- auth.type === "oAuth"
- ? oAuth.selectedInputMethod === "credentials"
- ? "credentials"
- : "bearer token"
- : "API key"
- } to send a real request`;
-
- // TODO change this to on-login
- useEffect(() => {
- if (apiKey != null) {
- setBearerAuth({ token: apiKey });
- }
- }, [apiKey, setBearerAuth]);
-
- return (
-
- {apiKeyInjection.enabled && apiKey == null && (
- <>
-
- {error && {errorDescription ?? error}}
-
- Login to send a real request
-
- }
- onClick={redirectOrOpenAuthForm}
- />
- }
- text="Provide token manually"
- onClick={() => isOpen.toggleValue()}
- />
-
-
- >
- )}
- {apiKeyInjection.enabled && apiKey != null && (
- <>
-
- }
- active={true}
- />
-
- {
-
- {apiKey !== authState?.bearerAuth?.token && apiKey && (
- }
- onClick={handleResetBearerAuth}
- size="normal"
- variant="outlined"
- />
- )}
- {apiKeyInjection && (
- {
- if (!apiKeyInjection.authenticated) {
- return;
- }
- const url = new URL(urlJoin(window.location.origin, logoutApiRoute));
- const returnTo = new URL(window.location.href);
- url.searchParams.set(
- apiKeyInjection.returnToQueryParam,
- returnTo.toString(),
- );
- fetch(url)
- .then(() => {
- window.location.reload();
- })
- .catch((error) => {
- // eslint-disable-next-line no-console
- console.error(error);
- });
- }}
- size="normal"
- variant="outlined"
- />
- )}
-
- }
-
- >
- )}
- {!apiKeyInjection.enabled && (isAuthed(auth, authState) || apiKey != null) && (
-
}
- rightIcon={
-
- Authenticated
-
- }
- onClick={isOpen.toggleValue}
- active={isOpen.value}
- />
- )}
- {!apiKeyInjection.enabled && !(isAuthed(auth, authState) || apiKey != null) && (
-
}
- rightIcon={
-
- Not Authenticated
-
- }
- onClick={redirectOrOpenAuthForm}
- active={isOpen.value}
- />
- )}
-
-
-
-
-
-
-
- {auth.type !== "oAuth" && (
-
- )}
- {apiKey != null && (
-
- )}
-
-
-
-
-
- );
-}
-
-function isEmpty(str: string | undefined): boolean {
- return str == null || str.trim().length === 0;
-}
-
-function isAuthed(auth: APIV1Read.ApiAuth, authState: PlaygroundAuthState): boolean {
- return visitDiscriminatedUnion(auth)._visit({
- bearerAuth: () => !isEmpty(authState.bearerAuth?.token.trim()),
- basicAuth: () =>
- !isEmpty(authState.basicAuth?.username.trim()) && !isEmpty(authState.basicAuth?.password.trim()),
- header: (header) => !isEmpty(authState.header?.headers[header.headerWireValue]?.trim()),
- oAuth: () => {
- const authToken =
- authState.oauth?.selectedInputMethod === "credentials"
- ? authState.oauth?.accessToken
- : authState.oauth?.userSuppliedAccessToken;
- return authToken ? !isEmpty(authToken.trim()) : false;
- },
- _other: () => false,
- });
-}
diff --git a/packages/ui/app/src/playground/PlaygroundContent.tsx b/packages/ui/app/src/playground/PlaygroundContent.tsx
index 8d88692264..a430b8e928 100644
--- a/packages/ui/app/src/playground/PlaygroundContent.tsx
+++ b/packages/ui/app/src/playground/PlaygroundContent.tsx
@@ -2,11 +2,9 @@ import type * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { ArrowLeft } from "iconoir-react";
import { ReactElement } from "react";
import { usePlaygroundNode } from "../atoms";
-import { PlaygroundWebSocket } from "./PlaygroundWebSocket";
-import { PlaygroundEndpoint } from "./endpoint/PlaygroundEndpoint";
-import { PlaygroundEndpointSkeleton } from "./endpoint/PlaygroundEndpointSkeleton";
-import { useEndpointContext } from "./hooks/useEndpointContext";
-import { useWebSocketContext } from "./hooks/useWebSocketContext";
+import { PlaygroundEndpoint, PlaygroundEndpointSkeleton } from "./endpoint";
+import { useEndpointContext, useWebSocketContext } from "./hooks";
+import { PlaygroundWebSocket } from "./websocket";
const PlaygroundContentForEndpoint = ({ node }: { node: FernNavigation.EndpointNode }) => {
const { context, isLoading } = useEndpointContext(node);
diff --git a/packages/ui/app/src/playground/PlaygroundDrawer.tsx b/packages/ui/app/src/playground/PlaygroundDrawer.tsx
index 7738c0a593..d1d75a7ddc 100644
--- a/packages/ui/app/src/playground/PlaygroundDrawer.tsx
+++ b/packages/ui/app/src/playground/PlaygroundDrawer.tsx
@@ -8,20 +8,23 @@ import { useAtomValue, useSetAtom } from "jotai";
import { useAtomCallback } from "jotai/utils";
import { ReactElement, memo, useEffect } from "react";
import { useCallbackOne } from "use-memo-one";
-import { HEADER_HEIGHT_ATOM, useAtomEffect } from "../atoms";
import {
+ HEADER_HEIGHT_ATOM,
+ IS_MOBILE_SCREEN_ATOM,
MAX_PLAYGROUND_HEIGHT_ATOM,
+ MOBILE_SIDEBAR_ENABLED_ATOM,
PLAYGROUND_NODE_ID,
+ VIEWPORT_HEIGHT_ATOM,
+ useAtomEffect,
useIsPlaygroundOpen,
usePlaygroundFormStateAtom,
usePlaygroundNode,
useTogglePlayground,
-} from "../atoms/playground";
-import { IS_MOBILE_SCREEN_ATOM, MOBILE_SIDEBAR_ENABLED_ATOM, VIEWPORT_HEIGHT_ATOM } from "../atoms/viewport";
+} from "../atoms";
import { FernErrorBoundary } from "../components/FernErrorBoundary";
import { PlaygroundContent } from "./PlaygroundContent";
import { HorizontalSplitPane } from "./VerticalSplitPane";
-import { PlaygroundEndpointSelectorContent } from "./endpoint/PlaygroundEndpointSelectorContent";
+import { PlaygroundEndpointSelectorContent } from "./endpoint";
import { useResizeY } from "./useSplitPlane";
import { PLAYGROUND_API_GROUPS_ATOM } from "./utils/flatten-apis";
diff --git a/packages/ui/app/src/playground/auth/PlaygroundAuthorizationForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundAuthorizationForm.tsx
new file mode 100644
index 0000000000..78f89bde21
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundAuthorizationForm.tsx
@@ -0,0 +1,33 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
+import { FC, ReactElement } from "react";
+import { PlaygroundBasicAuthForm } from "./PlaygroundBasicAuthForm";
+import { PlaygroundBearerAuthForm } from "./PlaygroundBearerAuthForm";
+import { PlaygroundHeaderAuthForm } from "./PlaygroundHeaderAuthForm";
+import { PlaygroundOAuthForm } from "./PlaygroundOAuthForm";
+
+interface PlaygroundAuthorizationFormProps {
+ auth: APIV1Read.ApiAuth;
+ closeContainer: () => void;
+ disabled: boolean;
+}
+
+export const PlaygroundAuthorizationForm: FC = ({
+ auth,
+ closeContainer,
+ disabled,
+}) => {
+ return (
+
+ {visitDiscriminatedUnion(auth, "type")._visit({
+ bearerAuth: (bearerAuth) => ,
+ basicAuth: (basicAuth) => ,
+ header: (header) => ,
+ oAuth: (oAuth) => (
+
+ ),
+ _other: () => false,
+ })}
+
+ );
+};
diff --git a/packages/ui/app/src/playground/auth/PlaygroundAuthorizationFormCard.tsx b/packages/ui/app/src/playground/auth/PlaygroundAuthorizationFormCard.tsx
new file mode 100644
index 0000000000..6093a21b4e
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundAuthorizationFormCard.tsx
@@ -0,0 +1,72 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { FernButton, FernCollapse } from "@fern-ui/components";
+import { useBooleanState } from "@fern-ui/react-commons";
+import { useSetAtom } from "jotai/react";
+import { ReactElement } from "react";
+import { PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM } from "../../atoms";
+import { useApiKeyInjectionConfig } from "../../services/useApiKeyInjectionConfig";
+import { PlaygroundAuthorizationForm } from "./PlaygroundAuthorizationForm";
+import { PlaygroundCardTriggerApiKeyInjected } from "./PlaygroundCardTriggerApiKeyInjected";
+import { PlaygroundCardTriggerManual } from "./PlaygroundCardTriggerManual";
+
+interface PlaygroundAuthorizationFormCardProps {
+ auth: APIV1Read.ApiAuth;
+ disabled: boolean;
+}
+export function PlaygroundAuthorizationFormCard({
+ auth,
+ disabled,
+}: PlaygroundAuthorizationFormCardProps): ReactElement | null {
+ const setBearerAuth = useSetAtom(PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM);
+ const isOpen = useBooleanState(false);
+ const apiKeyInjection = useApiKeyInjectionConfig();
+ const apiKey = apiKeyInjection.enabled && apiKeyInjection.authenticated ? apiKeyInjection.access_token : null;
+
+ const handleResetBearerAuth = () => {
+ setBearerAuth({ token: apiKey ?? "" });
+ };
+
+ return (
+
+ {apiKeyInjection.enabled ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+ {auth.type !== "oAuth" && (
+
+ )}
+ {apiKey != null && (
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundBasicAuthForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundBasicAuthForm.tsx
new file mode 100644
index 0000000000..c1fdbde28b
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundBasicAuthForm.tsx
@@ -0,0 +1,57 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { FernInput } from "@fern-ui/components";
+import { User } from "iconoir-react";
+import { useAtom } from "jotai/react";
+import { ReactElement, useCallback } from "react";
+import { PLAYGROUND_AUTH_STATE_BASIC_AUTH_ATOM } from "../../atoms";
+import { PasswordInputGroup } from "../PasswordInputGroup";
+
+export function PlaygroundBasicAuthForm({
+ basicAuth,
+ disabled,
+}: {
+ basicAuth: APIV1Read.BasicAuth;
+ disabled?: boolean;
+}): ReactElement {
+ const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_BASIC_AUTH_ATOM);
+ const handleChangeUsername = useCallback(
+ (newValue: string) => setValue((prev) => ({ ...prev, username: newValue })),
+ [setValue],
+ );
+ const handleChangePassword = useCallback(
+ (newValue: string) => setValue((prev) => ({ ...prev, password: newValue })),
+ [setValue],
+ );
+ return (
+ <>
+
+
+
+ }
+ rightElement={{"string"}}
+ disabled={disabled}
+ />
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundBearerAuthForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundBearerAuthForm.tsx
new file mode 100644
index 0000000000..f48976a74c
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundBearerAuthForm.tsx
@@ -0,0 +1,34 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { useAtom } from "jotai/react";
+import { ReactElement, useCallback } from "react";
+import { PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM } from "../../atoms";
+import { PasswordInputGroup } from "../PasswordInputGroup";
+
+export function PlaygroundBearerAuthForm({
+ bearerAuth,
+ disabled,
+}: {
+ bearerAuth: APIV1Read.BearerAuth;
+ disabled?: boolean;
+}): ReactElement {
+ const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM);
+ const handleChange = useCallback((newValue: string) => setValue({ token: newValue }), [setValue]);
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundCardTriggerApiKeyInjected.tsx b/packages/ui/app/src/playground/auth/PlaygroundCardTriggerApiKeyInjected.tsx
new file mode 100644
index 0000000000..fbb4c332b0
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundCardTriggerApiKeyInjected.tsx
@@ -0,0 +1,145 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { FernButton, FernCard } from "@fern-ui/components";
+import { APIKeyInjectionConfigEnabled } from "@fern-ui/fern-docs-auth";
+import { Key, User } from "iconoir-react";
+import { useAtomValue, useSetAtom } from "jotai";
+import { useSearchParams } from "next/navigation";
+import { ReactElement, useEffect } from "react";
+import urlJoin from "url-join";
+import { PLAYGROUND_AUTH_STATE_ATOM, PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM } from "../../atoms";
+import { useApiRoute } from "../../hooks/useApiRoute";
+import { Callout } from "../../mdx/components/callout";
+import { PlaygroundAuthorizationForm } from "./PlaygroundAuthorizationForm";
+
+interface PlaygroundCardTriggerApiKeyInjectedProps {
+ auth: APIV1Read.ApiAuth;
+ config: APIKeyInjectionConfigEnabled;
+ disabled: boolean;
+ toggleOpen: () => void;
+ onClose: () => void;
+}
+
+export function PlaygroundCardTriggerApiKeyInjected({
+ auth,
+ config,
+ disabled,
+ toggleOpen,
+ onClose,
+}: PlaygroundCardTriggerApiKeyInjectedProps): ReactElement | false {
+ const searchParams = useSearchParams();
+ const error = searchParams.get("error");
+ const errorDescription = searchParams.get("error_description");
+ const authState = useAtomValue(PLAYGROUND_AUTH_STATE_ATOM);
+ const logoutApiRoute = useApiRoute("/api/fern-docs/auth/logout");
+
+ const apiKey = config.authenticated ? config.access_token : null;
+ const setBearerAuth = useSetAtom(PLAYGROUND_AUTH_STATE_BEARER_TOKEN_ATOM);
+
+ // TODO change this to on-login
+ useEffect(() => {
+ if (apiKey != null) {
+ setBearerAuth({ token: apiKey });
+ }
+ }, [apiKey, setBearerAuth]);
+
+ const handleResetBearerAuth = () => {
+ setBearerAuth({ token: apiKey ?? "" });
+ };
+
+ const redirectOrOpenAuthForm = () => {
+ if (!config.authenticated) {
+ const url = new URL(config.authorizationUrl);
+ const state = new URL(window.location.href);
+ if (state.searchParams.has("error")) {
+ state.searchParams.delete("error");
+ }
+ if (state.searchParams.has("error_description")) {
+ state.searchParams.delete("error_description");
+ }
+ url.searchParams.set(config.returnToQueryParam, state.toString());
+ window.location.replace(url);
+ } else {
+ toggleOpen();
+ }
+ };
+
+ if (apiKey != null) {
+ return (
+
+ }
+ active={true}
+ />
+
+ {
+
+ {apiKey !== authState?.bearerAuth?.token && apiKey && (
+ }
+ onClick={handleResetBearerAuth}
+ size="normal"
+ variant="outlined"
+ />
+ )}
+ {
+ if (!config.authenticated) {
+ return;
+ }
+ const url = new URL(urlJoin(window.location.origin, logoutApiRoute));
+ const returnTo = new URL(window.location.href);
+ url.searchParams.set(config.returnToQueryParam, returnTo.toString());
+ fetch(url)
+ .then(() => {
+ window.location.reload();
+ })
+ .catch((error) => {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ });
+ }}
+ size="normal"
+ variant="outlined"
+ />
+
+ }
+
+ );
+ }
+
+ return (
+
+ {error && {errorDescription ?? error}}
+
+ Login to send a real request
+
+ }
+ onClick={redirectOrOpenAuthForm}
+ />
+ }
+ text="Provide token manually"
+ onClick={toggleOpen}
+ />
+
+
+ );
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundCardTriggerManual.tsx b/packages/ui/app/src/playground/auth/PlaygroundCardTriggerManual.tsx
new file mode 100644
index 0000000000..e2ad97d6c4
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundCardTriggerManual.tsx
@@ -0,0 +1,87 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
+import { FernButton } from "@fern-ui/components";
+import { Key } from "iconoir-react";
+import { useAtomValue } from "jotai";
+import { ReactElement } from "react";
+import { PLAYGROUND_AUTH_STATE_ATOM } from "../../atoms";
+import { PlaygroundAuthState } from "../types";
+
+interface PlaygroundCardTriggerManualProps {
+ auth: APIV1Read.ApiAuth;
+ disabled: boolean;
+ toggleOpen: () => void;
+ isOpen: boolean;
+}
+
+export function PlaygroundCardTriggerManual({
+ auth,
+ disabled,
+ toggleOpen,
+ isOpen,
+}: PlaygroundCardTriggerManualProps): ReactElement | false {
+ const authState = useAtomValue(PLAYGROUND_AUTH_STATE_ATOM);
+
+ const authButtonCopy = "Login to send a real request";
+
+ if (isAuthed(auth, authState)) {
+ return (
+ }
+ rightIcon={
+
+ Authenticated
+
+ }
+ onClick={toggleOpen}
+ active={isOpen}
+ disabled={disabled}
+ />
+ );
+ } else {
+ return (
+ }
+ rightIcon={
+
+ Not Authenticated
+
+ }
+ onClick={toggleOpen}
+ active={isOpen}
+ disabled={disabled}
+ />
+ );
+ }
+}
+
+function isEmpty(str: string | undefined): boolean {
+ return str == null || str.trim().length === 0;
+}
+
+function isAuthed(auth: APIV1Read.ApiAuth, authState: PlaygroundAuthState): boolean {
+ return visitDiscriminatedUnion(auth)._visit({
+ bearerAuth: () => !isEmpty(authState.bearerAuth?.token.trim()),
+ basicAuth: () =>
+ !isEmpty(authState.basicAuth?.username.trim()) && !isEmpty(authState.basicAuth?.password.trim()),
+ header: (header) => !isEmpty(authState.header?.headers[header.headerWireValue]?.trim()),
+ oAuth: () => {
+ const authToken =
+ authState.oauth?.selectedInputMethod === "credentials"
+ ? authState.oauth?.accessToken
+ : authState.oauth?.userSuppliedAccessToken;
+ return authToken ? !isEmpty(authToken.trim()) : false;
+ },
+ _other: () => false,
+ });
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundHeaderAuthForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundHeaderAuthForm.tsx
new file mode 100644
index 0000000000..bb5f447b3e
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundHeaderAuthForm.tsx
@@ -0,0 +1,53 @@
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { atom } from "jotai";
+import { useAtom } from "jotai/react";
+import type { ReactElement, SetStateAction } from "react";
+import { useMemoOne } from "use-memo-one";
+import { PLAYGROUND_AUTH_STATE_HEADER_ATOM } from "../../atoms";
+import { PasswordInputGroup } from "../PasswordInputGroup";
+
+export function PlaygroundHeaderAuthForm({
+ header,
+ disabled,
+}: {
+ header: APIV1Read.HeaderAuth;
+ disabled?: boolean;
+}): ReactElement {
+ const [value, setValue] = useAtom(
+ useMemoOne(
+ () =>
+ atom(
+ (get) => get(PLAYGROUND_AUTH_STATE_HEADER_ATOM).headers[header.headerWireValue],
+ (_get, set, change: SetStateAction) => {
+ set(PLAYGROUND_AUTH_STATE_HEADER_ATOM, ({ headers }) => ({
+ headers: {
+ ...headers,
+ [header.headerWireValue]:
+ typeof change === "function"
+ ? change(headers[header.headerWireValue] ?? "")
+ : change,
+ },
+ }));
+ },
+ ),
+ [header.headerWireValue],
+ ),
+ );
+
+ return (
+
+
+
+
+ );
+}
diff --git a/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx
new file mode 100644
index 0000000000..eb822aebb4
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx
@@ -0,0 +1,207 @@
+import { EndpointContext } from "@fern-api/fdr-sdk/api-definition";
+import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
+import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
+import { FernButton, FernDropdown, FernSegmentedControl, FernTooltip, FernTooltipProvider } from "@fern-ui/components";
+import { HelpCircle, Key, User } from "iconoir-react";
+import { useAtom } from "jotai";
+import { ReactElement, useState } from "react";
+import { PLAYGROUND_AUTH_STATE_OAUTH_ATOM, usePlaygroundEndpointFormState } from "../../atoms";
+import { useStandardProxyEnvironment } from "../../hooks/useStandardProxyEnvironment";
+import { Callout } from "../../mdx/components/callout";
+import { PasswordInputGroup } from "../PasswordInputGroup";
+import { PlaygroundEndpointForm } from "../endpoint";
+import { useOAuthEndpointContext } from "../hooks";
+import { oAuthClientCredentialReferencedEndpointLoginFlow } from "../utils/oauth";
+import { usePlaygroundBaseUrl } from "../utils/select-environment";
+import { PlaygroundBearerAuthForm } from "./PlaygroundBearerAuthForm";
+
+function FoundOAuthReferencedEndpointForm({
+ context,
+ oAuthClientCredentialsReferencedEndpoint,
+ closeContainer,
+ disabled,
+}: {
+ /**
+ * this must be the OAuth endpoint.
+ */
+ context: EndpointContext;
+ oAuthClientCredentialsReferencedEndpoint: APIV1Read.OAuthClientCredentialsReferencedEndpoint;
+ closeContainer: () => void;
+ disabled?: boolean;
+}): ReactElement {
+ const [value, setValue] = useAtom(PLAYGROUND_AUTH_STATE_OAUTH_ATOM);
+ const proxyEnvironment = useStandardProxyEnvironment();
+ const [formState, setFormState] = usePlaygroundEndpointFormState(context);
+ const [baseUrl] = usePlaygroundBaseUrl(context.endpoint);
+
+ const [displayFailedLogin, setDisplayFailedLogin] = useState(false);
+
+ /**
+ * TODO: turn this into a loadable (suspense)
+ */
+ const oAuthClientCredentialLogin = async () => {
+ setValue((prev) => ({ ...prev, isLoggingIn: true }));
+ await oAuthClientCredentialReferencedEndpointLoginFlow({
+ formState,
+ endpoint: context.endpoint,
+ proxyEnvironment,
+ oAuthClientCredentialsReferencedEndpoint,
+ baseUrl,
+ setValue,
+ closeContainer,
+ setDisplayFailedLogin,
+ });
+ setValue((prev) => ({ ...prev, isLoggingIn: false }));
+ };
+
+ const authenticationOptions: FernDropdown.Option[] = [
+ { type: "value", value: "credentials", label: "Credentials", icon: },
+ { type: "value", value: "token", label: "Bearer Token", icon: },
+ ];
+
+ return value.isLoggingIn ? (
+ Loading...
+ ) : (
+ <>
+
+ {
+ if (value != null && value.length > 0) {
+ setValue((prev) => ({ ...prev, selectedInputMethod: value as "credentials" | "token" }));
+ }
+ }}
+ value={value.selectedInputMethod}
+ disabled={disabled}
+ />
+
+
+ {value.selectedInputMethod === "credentials" ? (
+ <>
+
+
+
+
+ {displayFailedLogin && (
+ Failed to login with the provided credentials
+ )}
+ {value.isLoggedIn && (
+
+
+
+
+
+
+
+
+ )}
+ {value.isLoggedIn && value.accessToken !== value.loggedInStartingToken && (
+
+ The bearer token is no longer valid. Please refresh it by clicking the button below
+
+ )}
+ >
+ ) : (
+ <>
+
+
+
+
+ setValue((prev) => ({ ...prev, userSuppliedAccessToken: newValue }))
+ }
+ value={value.userSuppliedAccessToken}
+ autoComplete="off"
+ data-1p-ignore="true"
+ disabled={disabled}
+ />
+
+ >
+ )}
+
+ {value.selectedInputMethod === "credentials" && (
+
+ )}
+
+ >
+ );
+}
+
+function OAuthReferencedEndpointForm({
+ referencedEndpoint,
+ oAuthClientCredentialsReferencedEndpoint,
+ closeContainer,
+ disabled,
+}: {
+ referencedEndpoint: APIV1Read.OAuthClientCredentials.ReferencedEndpoint;
+ oAuthClientCredentialsReferencedEndpoint: APIV1Read.OAuthClientCredentialsReferencedEndpoint;
+ closeContainer: () => void;
+ disabled?: boolean;
+}) {
+ const { context, isLoading } = useOAuthEndpointContext(referencedEndpoint);
+
+ if (context == null) {
+ if (!isLoading) {
+ // eslint-disable-next-line no-console
+ console.error("Could not find OAuth endpoint for referenced endpoint", referencedEndpoint);
+ }
+ return ;
+ }
+
+ return (
+
+ );
+}
+
+export function PlaygroundOAuthForm({
+ oAuth,
+ closeContainer,
+ disabled,
+}: {
+ oAuth: APIV1Read.ApiAuth.OAuth;
+ closeContainer: () => void;
+ disabled?: boolean;
+}): ReactElement | false {
+ return visitDiscriminatedUnion(oAuth.value, "type")._visit({
+ clientCredentials: (clientCredentials) => {
+ return visitDiscriminatedUnion(clientCredentials.value, "type")._visit({
+ referencedEndpoint: (referencedEndpoint) => (
+
+ ),
+ _other: () => false,
+ });
+ },
+ _other: () => false,
+ });
+}
diff --git a/packages/ui/app/src/playground/auth/index.ts b/packages/ui/app/src/playground/auth/index.ts
new file mode 100644
index 0000000000..03a2e3c123
--- /dev/null
+++ b/packages/ui/app/src/playground/auth/index.ts
@@ -0,0 +1 @@
+export { PlaygroundAuthorizationFormCard } from "./PlaygroundAuthorizationFormCard";
diff --git a/packages/ui/app/src/playground/endpoint/PlaygroundEndpointContent.tsx b/packages/ui/app/src/playground/endpoint/PlaygroundEndpointContent.tsx
index ea1abda6f1..e9ba05112b 100644
--- a/packages/ui/app/src/playground/endpoint/PlaygroundEndpointContent.tsx
+++ b/packages/ui/app/src/playground/endpoint/PlaygroundEndpointContent.tsx
@@ -1,7 +1,7 @@
import type { EndpointContext } from "@fern-api/fdr-sdk/api-definition";
import { Loadable } from "@fern-ui/loadable";
import { Dispatch, ReactElement, SetStateAction, useDeferredValue } from "react";
-import { PlaygroundAuthorizationFormCard } from "../PlaygroundAuthorizationForm";
+import { PlaygroundAuthorizationFormCard } from "../auth";
import { PlaygroundEndpointRequestFormState } from "../types";
import { PlaygroundResponse } from "../types/playgroundResponse";
import { PlaygroundEndpointContentLayout } from "./PlaygroundEndpointContentLayout";
diff --git a/packages/ui/app/src/playground/endpoint/index.ts b/packages/ui/app/src/playground/endpoint/index.ts
new file mode 100644
index 0000000000..2df8e62fa0
--- /dev/null
+++ b/packages/ui/app/src/playground/endpoint/index.ts
@@ -0,0 +1,4 @@
+export { PlaygroundEndpoint } from "./PlaygroundEndpoint";
+export { PlaygroundEndpointForm } from "./PlaygroundEndpointForm";
+export { PlaygroundEndpointSelectorContent } from "./PlaygroundEndpointSelectorContent";
+export { PlaygroundEndpointSkeleton } from "./PlaygroundEndpointSkeleton";
diff --git a/packages/ui/app/src/playground/hooks/index.ts b/packages/ui/app/src/playground/hooks/index.ts
new file mode 100644
index 0000000000..38f532a428
--- /dev/null
+++ b/packages/ui/app/src/playground/hooks/index.ts
@@ -0,0 +1,5 @@
+export { useEndpointContext } from "./useEndpointContext";
+export { useOAuthEndpointContext } from "./useOAuthEndpointContext";
+export { usePreloadApiLeaf } from "./usePreloadApiLeaf";
+export { useWebSocketContext } from "./useWebSocketContext";
+export { useWebsocketMessages } from "./useWebsocketMessages";
diff --git a/packages/ui/app/src/playground/hooks/useOauthEndpointContext.ts b/packages/ui/app/src/playground/hooks/useOAuthEndpointContext.ts
similarity index 100%
rename from packages/ui/app/src/playground/hooks/useOauthEndpointContext.ts
rename to packages/ui/app/src/playground/hooks/useOAuthEndpointContext.ts
diff --git a/packages/ui/app/src/playground/PlaygroundWebSocket.tsx b/packages/ui/app/src/playground/websocket/PlaygroundWebSocket.tsx
similarity index 95%
rename from packages/ui/app/src/playground/PlaygroundWebSocket.tsx
rename to packages/ui/app/src/playground/websocket/PlaygroundWebSocket.tsx
index 23c2db3f58..8b50119606 100644
--- a/packages/ui/app/src/playground/PlaygroundWebSocket.tsx
+++ b/packages/ui/app/src/playground/websocket/PlaygroundWebSocket.tsx
@@ -4,13 +4,13 @@ import { FernTooltipProvider } from "@fern-ui/components";
import { usePrevious } from "@fern-ui/react-commons";
import { Wifi, WifiOff } from "iconoir-react";
import { FC, ReactElement, useCallback, useEffect, useRef, useState } from "react";
-import { PLAYGROUND_AUTH_STATE_ATOM, store, usePlaygroundWebsocketFormState } from "../atoms";
-import { usePlaygroundSettings } from "../hooks/usePlaygroundSettings";
+import { PLAYGROUND_AUTH_STATE_ATOM, store, usePlaygroundWebsocketFormState } from "../../atoms";
+import { usePlaygroundSettings } from "../../hooks/usePlaygroundSettings";
+import { PlaygroundEndpointPath } from "../endpoint/PlaygroundEndpointPath";
+import { useWebsocketMessages } from "../hooks/useWebsocketMessages";
+import { buildAuthHeaders } from "../utils";
+import { usePlaygroundBaseUrl } from "../utils/select-environment";
import { PlaygroundWebSocketContent } from "./PlaygroundWebSocketContent";
-import { PlaygroundEndpointPath } from "./endpoint/PlaygroundEndpointPath";
-import { useWebsocketMessages } from "./hooks/useWebsocketMessages";
-import { buildAuthHeaders } from "./utils";
-import { usePlaygroundBaseUrl } from "./utils/select-environment";
// TODO: decide if this should be an env variable, and if we should move REST proxy to the same (or separate) cloudflare worker
const WEBSOCKET_PROXY_URI = "wss://websocket.proxy.ferndocs.com/ws";
diff --git a/packages/ui/app/src/playground/PlaygroundWebSocketContent.tsx b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketContent.tsx
similarity index 97%
rename from packages/ui/app/src/playground/PlaygroundWebSocketContent.tsx
rename to packages/ui/app/src/playground/websocket/PlaygroundWebSocketContent.tsx
index ea0de8014e..9f20821d1a 100644
--- a/packages/ui/app/src/playground/PlaygroundWebSocketContent.tsx
+++ b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketContent.tsx
@@ -1,8 +1,8 @@
import type * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import type { WebSocketContext } from "@fern-api/fdr-sdk/api-definition";
import { Dispatch, FC, SetStateAction, useEffect, useRef, useState } from "react";
+import { PlaygroundWebSocketRequestFormState } from "../types";
import { PlaygroundWebSocketSessionForm } from "./PlaygroundWebSocketSessionForm";
-import { PlaygroundWebSocketRequestFormState } from "./types";
interface PlaygroundWebSocketContentProps {
context: WebSocketContext;
diff --git a/packages/ui/app/src/playground/PlaygroundWebSocketHandshakeForm.tsx b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketHandshakeForm.tsx
similarity index 94%
rename from packages/ui/app/src/playground/PlaygroundWebSocketHandshakeForm.tsx
rename to packages/ui/app/src/playground/websocket/PlaygroundWebSocketHandshakeForm.tsx
index 66579a49df..b6bc2c6a32 100644
--- a/packages/ui/app/src/playground/PlaygroundWebSocketHandshakeForm.tsx
+++ b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketHandshakeForm.tsx
@@ -1,10 +1,10 @@
import type { WebSocketContext } from "@fern-api/fdr-sdk/api-definition";
import { FernCard } from "@fern-ui/components";
import { Dispatch, FC, SetStateAction, useCallback } from "react";
-import { Callout } from "../mdx/components/callout";
-import { PlaygroundAuthorizationFormCard } from "./PlaygroundAuthorizationForm";
-import { PlaygroundObjectPropertiesForm } from "./form/PlaygroundObjectPropertyForm";
-import { PlaygroundWebSocketRequestFormState } from "./types";
+import { Callout } from "../../mdx/components/callout";
+import { PlaygroundAuthorizationFormCard } from "../auth";
+import { PlaygroundObjectPropertiesForm } from "../form/PlaygroundObjectPropertyForm";
+import { PlaygroundWebSocketRequestFormState } from "../types";
interface PlaygroundWebSocketHandshakeFormProps {
context: WebSocketContext;
diff --git a/packages/ui/app/src/playground/PlaygroundWebSocketSessionForm.tsx b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketSessionForm.tsx
similarity index 94%
rename from packages/ui/app/src/playground/PlaygroundWebSocketSessionForm.tsx
rename to packages/ui/app/src/playground/websocket/PlaygroundWebSocketSessionForm.tsx
index df079fb943..c5231dbb81 100644
--- a/packages/ui/app/src/playground/PlaygroundWebSocketSessionForm.tsx
+++ b/packages/ui/app/src/playground/websocket/PlaygroundWebSocketSessionForm.tsx
@@ -4,12 +4,12 @@ import titleCase from "@fern-api/ui-core-utils/titleCase";
import { FernButton, FernCard, FernScrollArea } from "@fern-ui/components";
import cn from "clsx";
import { Dispatch, FC, SetStateAction, useCallback } from "react";
-import { WebSocketMessagesVirtualized } from "../api-reference/web-socket/WebSocketMessagesVirtualized";
+import { WebSocketMessagesVirtualized } from "../../api-reference/web-socket/WebSocketMessagesVirtualized";
+import { HorizontalSplitPane } from "../VerticalSplitPane";
+import { PlaygroundTypeReferenceForm } from "../form/PlaygroundTypeReferenceForm";
+import { useWebsocketMessages } from "../hooks/useWebsocketMessages";
+import { PlaygroundWebSocketRequestFormState } from "../types";
import { PlaygroundWebSocketHandshakeForm } from "./PlaygroundWebSocketHandshakeForm";
-import { HorizontalSplitPane } from "./VerticalSplitPane";
-import { PlaygroundTypeReferenceForm } from "./form/PlaygroundTypeReferenceForm";
-import { useWebsocketMessages } from "./hooks/useWebsocketMessages";
-import { PlaygroundWebSocketRequestFormState } from "./types";
interface PlaygroundWebSocketSessionFormProps {
context: WebSocketContext;
diff --git a/packages/ui/app/src/playground/websocket/index.ts b/packages/ui/app/src/playground/websocket/index.ts
new file mode 100644
index 0000000000..e8a7d3f9b2
--- /dev/null
+++ b/packages/ui/app/src/playground/websocket/index.ts
@@ -0,0 +1 @@
+export { PlaygroundWebSocket } from "./PlaygroundWebSocket";
diff --git a/packages/ui/fern-docs-auth/src/injection.ts b/packages/ui/fern-docs-auth/src/injection.ts
index ed3fab8db5..675422f65d 100644
--- a/packages/ui/fern-docs-auth/src/injection.ts
+++ b/packages/ui/fern-docs-auth/src/injection.ts
@@ -21,13 +21,18 @@ export const APIKeyInjectionConfigAuthorizedSchema = z.object({
returnToQueryParam: z.string(),
});
-export const APIKeyInjectionConfigSchema = z.union([
- APIKeyInjectionConfigDisabledSchema,
+export const APIKeyInjectionConfigEnabledSchema = z.union([
APIKeyInjectionConfigUnauthorizedSchema,
APIKeyInjectionConfigAuthorizedSchema,
]);
+export const APIKeyInjectionConfigSchema = z.union([
+ APIKeyInjectionConfigDisabledSchema,
+ APIKeyInjectionConfigEnabledSchema,
+]);
+
export type APIKeyInjectionConfigDisabled = z.infer;
+export type APIKeyInjectionConfigEnabled = z.infer;
export type APIKeyInjectionConfigUnauthorized = z.infer;
export type APIKeyInjectionConfigAuthorized = z.infer;
export type APIKeyInjectionConfig = z.infer;