1+ import { faCheck , faSpinnerThird , Icon } from "@rivet-gg/icons" ;
2+ import { useQuery } from "@tanstack/react-query" ;
3+ import { AnimatePresence , motion } from "framer-motion" ;
14import { type UseFormReturn , useFormContext } from "react-hook-form" ;
25import z from "zod" ;
36import {
7+ CodeFrame ,
8+ CodeGroup ,
9+ CodePreview ,
10+ cn ,
411 createSchemaForm ,
512 FormControl ,
613 FormDescription ,
@@ -9,6 +16,7 @@ import {
916 FormLabel ,
1017 FormMessage ,
1118 Input ,
19+ ScrollArea ,
1220 Select ,
1321 SelectContent ,
1422 SelectItem ,
@@ -56,14 +64,58 @@ export const Plan = ({ className }: { className?: string }) => {
5664 </ SelectContent >
5765 </ Select >
5866 </ FormControl >
59- < FormDescription className = "col-span-1" > </ FormDescription >
67+ < FormDescription className = "col-span-1" >
68+ Your Vercel plan determines the configuration required
69+ to properly run your Rivet Engine.
70+ </ FormDescription >
6071 < FormMessage className = "col-span-1" />
6172 </ FormItem >
6273 ) }
6374 />
6475 ) ;
6576} ;
6677
78+ const PLAN_TO_MAX_DURATION : Record < string , number > = {
79+ hobby : 60 ,
80+ pro : 300 ,
81+ enterprise : 900 ,
82+ } ;
83+
84+ const code = ( { plan } : { plan : string } ) =>
85+ `{
86+ "$schema": "https://openapi.vercel.sh/vercel.json",
87+ "fluid": false, // [!code highlight]
88+ "functions": {
89+ "**": {
90+ "maxDuration": ${ PLAN_TO_MAX_DURATION [ plan ] || 60 } , // [!code highlight]
91+ },
92+ },
93+ }` ;
94+
95+ export const Json = ( ) => {
96+ const { watch } = useFormContext < FormValues > ( ) ;
97+
98+ const plan = watch ( "plan" ) ;
99+ return (
100+ < div className = "space-y-2 mt-2" >
101+ < CodeFrame language = "json" title = "vercel.json" >
102+ < CodePreview
103+ className = "w-full min-w-0"
104+ language = "json"
105+ code = { code ( { plan } ) }
106+ />
107+ </ CodeFrame >
108+ < FormDescription className = "col-span-1" >
109+ < b > Max Duration</ b > - The maximum execution time of your
110+ serverless functions.
111+ < br />
112+ < b > Disable Fluid Compute</ b > - Rivet has its own intelligent
113+ load balancing mechanism.
114+ </ FormDescription >
115+ </ div >
116+ ) ;
117+ } ;
118+
67119export const Endpoint = ( { className } : { className ?: string } ) => {
68120 const { control } = useFormContext < FormValues > ( ) ;
69121 return (
@@ -78,7 +130,6 @@ export const Endpoint = ({ className }: { className?: string }) => {
78130 < FormControl className = "row-start-2" >
79131 < Input
80132 placeholder = "https://your-application.vercel.app"
81- maxLength = { 25 }
82133 { ...field }
83134 />
84135 </ FormControl >
@@ -88,3 +139,68 @@ export const Endpoint = ({ className }: { className?: string }) => {
88139 />
89140 ) ;
90141} ;
142+
143+ export function ConnectionCheck ( ) {
144+ const { watch, formState } = useFormContext < FormValues > ( ) ;
145+ const endpoint = watch ( "endpoint" ) ;
146+ const enabled = ! ! endpoint && z . string ( ) . url ( ) . safeParse ( endpoint ) . success ;
147+
148+ const { data } = useQuery ( {
149+ queryKey : [ "vercel-endpoint-check" , endpoint ] ,
150+ queryFn : async ( ) => {
151+ try {
152+ const url = new URL ( "/health" , endpoint ) ;
153+ const response = await fetch ( url ) ;
154+ if ( ! response . ok ) {
155+ throw new Error ( "Failed to connect" ) ;
156+ }
157+ return response . json ( ) ;
158+ } catch {
159+ const url = new URL ( "/api/rivet/health" , endpoint ) ;
160+ const response = await fetch ( url ) ;
161+ if ( ! response . ok ) {
162+ throw new Error ( "Failed to connect" ) ;
163+ }
164+ return response . json ( ) ;
165+ }
166+ } ,
167+ enabled,
168+ refetchInterval : 1000 ,
169+ } ) ;
170+
171+ const success = ! ! data ;
172+
173+ return (
174+ < AnimatePresence >
175+ { enabled ? (
176+ < motion . div
177+ layoutId = "msg"
178+ className = { cn (
179+ "text-center text-muted-foreground text-sm overflow-hidden flex items-center justify-center" ,
180+ success && "text-primary-foreground" ,
181+ ) }
182+ initial = { { height : 0 , opacity : 0.5 } }
183+ animate = { { height : "4rem" , opacity : 1 } }
184+ >
185+ { success ? (
186+ < >
187+ < Icon
188+ icon = { faCheck }
189+ className = "mr-1.5 text-primary"
190+ /> { " " }
191+ Runner successfully connected
192+ </ >
193+ ) : (
194+ < >
195+ < Icon
196+ icon = { faSpinnerThird }
197+ className = "mr-1.5 animate-spin"
198+ /> { " " }
199+ Waiting for runner to connect...
200+ </ >
201+ ) }
202+ </ motion . div >
203+ ) : null }
204+ </ AnimatePresence >
205+ ) ;
206+ }
0 commit comments