1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["19799"],{3381(e,i,n){n.r(i),n.d(i,{metadata:()=>s,default:()=>p,frontMatter:()=>c,contentTitle:()=>l,toc:()=>h,assets:()=>d});var s=JSON.parse('{"id":"guided-tour/updating-data/graphql-subscriptions","title":"GraphQL subscriptions","description":"Relay guide to GraphQL subscriptions","source":"@site/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md","sourceDirName":"guided-tour/updating-data","slug":"/guided-tour/updating-data/graphql-subscriptions/","permalink":"/docs/v16.0.0/guided-tour/updating-data/graphql-subscriptions/","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/relay/tree/main/website/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md","tags":[],"version":"v16.0.0","frontMatter":{"id":"graphql-subscriptions","title":"GraphQL subscriptions","slug":"/guided-tour/updating-data/graphql-subscriptions/","description":"Relay guide to GraphQL subscriptions","keywords":["subscription"]},"sidebar":"docs","previous":{"title":"Retaining Queries","permalink":"/docs/v16.0.0/guided-tour/accessing-data-without-react/retaining-queries/"},"next":{"title":"Imperatively modifying store data","permalink":"/docs/v16.0.0/guided-tour/updating-data/imperatively-modifying-store-data/"}}'),t=n(74848),r=n(28453),a=n(86898),o=n(17352);let c={id:"graphql-subscriptions",title:"GraphQL subscriptions",slug:"/guided-tour/updating-data/graphql-subscriptions/",description:"Relay guide to GraphQL subscriptions",keywords:["subscription"]},l,d={},h=[{value:"Using <code>useSubscription</code> to create a subscription",id:"using-usesubscription-to-create-a-subscription",level:2},{value:"Refreshing components in response to subscription events",id:"refreshing-components-in-response-to-subscription-events",level:2},{value:"Executing a callback when the subscription fires, errors or is closed by the server",id:"executing-a-callback-when-the-subscription-fires-errors-or-is-closed-by-the-server",level:2},{value:"Declarative mutation directives",id:"declarative-mutation-directives",level:2},{value:"Manipulating connections in response to subscription events",id:"manipulating-connections-in-response-to-subscription-events",level:3},{value:"Deleting items in response to mutations",id:"deleting-items-in-response-to-mutations",level:3},{value:"Imperatively modifying local data",id:"imperatively-modifying-local-data",level:2}
1,{value:"Configuring the Network Layer",id:"configuring-the-network-layer",level:2}];function u(e){let i={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(o.FbInternalOnly,{children:(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.a,{href:"https://www.internalfb.com/intern/wiki/GraphQL_Subscriptions/",children:"GraphQL subscriptions"})," are a mechanism to allow clients to query for data in response to a stream of server-side events."]})}),"\n",(0,t.jsx)(o.OssOnly,{children:(0,t.jsx)(i.p,{children:"GraphQL subscriptions are a mechanism to allow clients to query for data in response to a stream of server-side events."})}),"\n",(0,t.jsxs)(i.p,{children:["A GraphQL subscription looks very similar to a query, except that it uses the ",(0,t.jsx)(i.code,{children:"subscription"})," keyword:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-graphql",children:"subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {\n feedback_like_subscribe(data: $input) {\n feedback {\n like_count\n }\n }\n}\n"})}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["Establishing a subscription using this GraphQL snippet will cause the application to be notified whenever an event is emitted from the ",(0,t.jsx)(i.code,{children:"feedback_like_subscribe"})," stream."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"feedback_like_subscribe"})," is a ",(0,t.jsx)(i.em,{children:"subscription root field"})," (or just ",(0,t.jsx)(i.em,{children:"subscription field"}),"), which sets up the subscription on the backend."]}),"\n"]}),"\n",(0,t.jsx)(o.FbInternalOnly,{children:(0,t.jsxs)(i.admonition,{type:"info",children:[(0,t.jsx)(i.p,{children:'You can view subscription root fields in the GraphQL Schema Explorer by opening VSCode @ FB and executing the command "Relay: Open GraphQL Schema Explorer". Then, in the "Schema Explorer Tab", click on "Subscription".'}),(0,t.jsx)(i.p,{children:"You can click on the various mutation fields to see their parameters, descriptions and exposed fields."})]})}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsx)(i.li,{children:"Like mutations, a subscription is handled in two separate steps. First, a server-side event occurs. Then, the query is executed."}),"\n"]}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Note that the event stream can be completely arbitrary, and can have no relation to the fields selected. In other words, there is no guarantee that the values selected in a subscription will have changed from notification to notification."})}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"feedback_like_subscribe"})," returns a specific GraphQL type which exposes the data we can query in response to the server-side event. In this case, we're querying for the Feedback object and its updated ",(0,t.jsx)(i.code,{children:"like_count"}),". This allows us to show the like count in real time."]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"An example of a subscription payload received by the client could look like this:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-json",children:'{\n "feedback_like_subscribe": {\n "feedback": {\n "id": "feedback-id",\n "like_count": 321,\n }\n }\n}\n'})}),"\n",(0,t.jsxs)(i.p,{children:["In Relay, we can declare GraphQL subscriptions using the ",(0,t.jsx)(i.code,{children:"graphql"})," tag too:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-js",children:"const {graphql} = require('react-relay');\n\nconst feedbackLikeSubscription = graphql`\n subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {\n feedback_like_subscribe(data: $input) {\n feedback {\n like_count\n }\n }\n }\n`;\n"})}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["Note that subscriptions can also reference GraphQL ",(0,t.jsx)(i.a,{href:"../../rendering/variables/",children:"variables"})," in the same way queries or fragments do."]}),"\n"]}),"\n",(0,t.jsxs)(i.h2,{id:"using-usesubscription-to-create-a-subscription",children:["Using ",(0,t.jsx)(i.co
1de,{children:"useSubscription"})," to create a subscription"]}),"\n",(0,t.jsxs)(i.p,{children:["In order to create a subscription in Relay, we can use the ",(0,t.jsx)(i.code,{children:"useSubscription"})," and ",(0,t.jsx)(i.code,{children:"requestSubscription"})," APIs. Let's take a look at an example using the ",(0,t.jsx)(i.code,{children:"useSubscription"})," API:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-js",children:"import type {Environment} from 'react-relay';\nimport type {FeedbackLikeSubscribeData} from 'FeedbackLikeSubscription.graphql';\n\nconst {graphql, useSubscription} = require('react-relay');\nconst {useMemo} = require('React');\n\nfunction useFeedbackSubscription(\n input: FeedbackLikeSubscribeData,\n) {\n const config = useMemo(() => ({\n subscription: graphql`\n subscription FeedbackLikeSubscription(\n $input: FeedbackLikeSubscribeData!\n ) {\n feedback_like_subscribe(data: $input) {\n feedback {\n like_count\n }\n }\n }\n `,\n variables: {input},\n }), [input]);\n\n return useSubscription(config);\n}\n"})}),"\n",(0,t.jsx)(i.p,{children:"Let's distill what's happening here."}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"useSubscription"})," takes a ",(0,t.jsx)(i.code,{children:"GraphlQLSubscriptionConfig"})," object, which includes the following fields:","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"subscription"}),": the GraphQL literal containing the subscription, and"]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"variables"}),": the variables with which to establish the subscription."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["In addition, ",(0,t.jsx)(i.code,{children:"useSubscription"})," accepts a Flow type parameter. As with queries, the Flow type of the subscription is exported from the file that the Relay compiler generates.","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["If this type is provided, the ",(0,t.jsx)(i.code,{children:"GraphQLSubscriptionConfig"})," becomes statically typed as well. ",(0,t.jsx)(i.strong,{children:"It is a best practice to always provide this type."})]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["Now, when the ",(0,t.jsx)(i.code,{children:"useFeedbackSubscription"})," hook commits, Relay will establish a subscription.","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["Unlike with APIs like ",(0,t.jsx)(i.code,{children:"useLazyLoadQuery"}),", Relay will ",(0,t.jsx)(i.strong,{children:"not"})," attempt to establish this subscription during the render phase."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["Once it is established, whenever an event occurs, the backend will select the updated Feedback object and select the ",(0,t.jsx)(i.code,{children:"like_count"})," fields off of it.","\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["Since the ",(0,t.jsx)(i.code,{children:"Feedback"})," type contains an ",(0,t.jsx)(i.code,{children:"id"})," field, the Relay compiler will automatically add a selection for the ",(0,t.jsx)(i.code,{children:"id"})," field."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(i.li,{children:["When the subscription response is received, Relay will find a feedback object in the store with a matching ",(0,t.jsx)(i.code,{children:"id"})," and update it with the newly received ",(0,t.jsx)(i.code,{children:"like_count"})," value."]}),"\n",(0,t.jsx)(i.li,{children:"If these values have changed as a result, any components which selected these fields off of the feedback object will be re-rendered. Or, to put it colloquially, any component which depends on the updated data will re-render."}),"\n"]}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsxs)(i.p,{children:["The name of the type of the parameter ",(0,t.jsx)(i.code,{children:"FeedbackLikeSubscribeData"})," is derived from the name of the top-level mutation field, i.e. from ",(0,t.jsx)(i.code,{children:"feedback_like_subscribe"}),". This type is also exported from the generated ",(0,t.jsx)(i.co
1de,{children:"graphql.js"})," file."]})}),"\n",(0,t.jsx)(i.admonition,{type:"caution",children:(0,t.jsxs)(i.p,{children:["The ",(0,t.jsx)(i.code,{children:"GraphQLSubscriptionConfig"})," object passed to ",(0,t.jsx)(i.code,{children:"useSubscription"})," should be memoized! Otherwise, ",(0,t.jsx)(i.code,{children:"useSubscription"})," will dispose the subscription and re-establish it with every render!"]})}),"\n",(0,t.jsx)(i.h2,{id:"refreshing-components-in-response-to-subscription-events",children:"Refreshing components in response to subscription events"}),"\n",(0,t.jsxs)(i.p,{children:["In the previous example, we manually selected ",(0,t.jsx)(i.code,{children:"like_count"}),". Components that select this field will be re-rendered, should we receive an updated value."]}),"\n",(0,t.jsx)(i.p,{children:"However, it is generally better to spread fragments that correspond to the components that we want to refresh in response to the mutation. This is because the data selected by components can change."}),"\n",(0,t.jsx)(i.p,{children:"Requiring developers to know about all subscriptions that might fetch their components data (and keeping them up-to-date) is an example of the kind of global reasoning that Relay wants to avoid requiring."}),"\n",(0,t.jsx)(i.p,{children:"For example, we might rewrite the subscription as follows:"}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-graphql",children:"subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) {\n feedback_like_subscribe(data: $input) {\n feedback {\n ...FeedbackDisplay_feedback\n ...FeedbackDetail_feedback\n }\n }\n}\n"})}),"\n",(0,t.jsxs)(i.p,{children:["Now, whenever a event in the ",(0,t.jsx)(i.code,{children:"feedback_like_subscribe"})," event stream occurred, the data selected by the ",(0,t.jsx)(i.code,{children:"FeedbackDisplay"})," and ",(0,t.jsx)(i.code,{children:"FeedbackDetail"})," components will be refetched, and those components will remain in a consistent state."]}),"\n",(0,t.jsx)(i.admonition,{type:"note",children:(0,t.jsx)(i.p,{children:"Spreading fragments is generally preferable to refetching the data in response to subscription events, since the updated data can be fetched in a single round trip."})}),"\n",(0,t.jsx)(i.h2,{id:"executing-a-callback-when-the-subscription-fires-errors-or-is-closed-by-the-server",children:"Executing a callback when the subscription fires, errors or is closed by the server"}),"\n",(0,t.jsxs)(i.p,{children:["In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The ",(0,t.jsx)(i.code,{children:"GraphQLSubscriptionConfig"})," can include the following fields to handle such cases:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"onNext"}),", a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries)."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"onError"}),", a callback that is executed when the subscription errors. It is passed the error that occurred."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.code,{children:"onCompleted"}),", a callback that is executed when the server ends the subscription."]}),"\n"]}),"\n",(0,t.jsx)(i.h2,{id:"declarative-mutation-directives",children:"Declarative mutation directives"}),"\n",(0,t.jsxs)(i.p,{children:[(0,t.jsx)(i.a,{href:"../../list-data/updating-connections/#using-declarative-directives",children:"Declarative mutation directives"})," and ",(0,t.jsx)(i.a,{href:"../graphql-mutations/#deleting-items-in-response-to-mutations",children:(0,t.jsx)(i.code,{children:"@deleteRecord"})})," work in subscriptions, too."]}),"\n",(0,t.jsx)(i.h3,{id:"manipulating-connections-in-response-to-subscription-events",children:"Manipulating connections in response to subscription events"}),"\n",(0,t.jsxs)(i.p,{children:["Relay makes it easy to respond to subscription events by adding items to or removing items from connections (i.e. lists). For example, you might want to append a newly created user to a given connection. For more, see ",(0,t.jsx)(i.a,{href:"../../list-data/updating-connections/#using-declarative-directives",children:"Using declarative directives"}),"."]}),"\n",(0,t.jsx)(i.h3,{id:"deleting-items-in-response-to-mutations",children:"Deleting items in response to mutations"}),"\n",(0,t.jsxs)(i.p,{children:["In addition, you might want to delete an item from the store in response to a mutation. In order to do this, you would add the ",(0,t.jsx)(i.co
1de,{children:"@deleteRecord"})," directive to the deleted ID. For example:"]}),"\n",(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-graphql",children:"subscription DeletePostSubscription($input: DeletePostSubscribeData!) {\n delete_post_subscribe(data: $input) {\n deleted_post {\n id @deleteRecord\n }\n }\n}\n"})}),"\n",(0,t.jsx)(i.h2,{id:"imperatively-modifying-local-data",children:"Imperatively modifying local data"}),"\n",(0,t.jsxs)(i.p,{children:["At times, the updates you wish to perform are more complex than just updating the values of fields and cannot be handled by the declarative mutation directives. For such situations, the ",(0,t.jsx)(i.code,{children:"GraphQLSubscriptionConfig"})," accepts an ",(0,t.jsx)(i.code,{children:"updater"})," function which gives you full control over how to update the store."]}),"\n",(0,t.jsxs)(i.p,{children:["This is discussed in more detail in the section on ",(0,t.jsx)(i.a,{href:"../imperatively-modifying-store-data/",children:"Imperatively updating store data"}),"."]}),"\n",(0,t.jsx)(i.h2,{id:"configuring-the-network-layer",children:"Configuring the Network Layer"}),"\n",(0,t.jsxs)(o.OssOnly,{children:[(0,t.jsxs)(i.p,{children:["You will need to Configure your ",(0,t.jsx)(i.a,{href:"../../../guides/network-layer",children:"Network layer"})," to handle subscriptions."]}),(0,t.jsxs)(i.p,{children:["Usually GraphQL subscriptions are communicated over ",(0,t.jsx)(i.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API",children:"WebSockets"}),", here's an example using ",(0,t.jsx)(i.a,{href:"https://github.com/enisdenjo/graphql-ws",children:"graphql-ws"}),":"]}),(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-javascript",children:"import {\n ...\n Network,\n Observable\n} from 'relay-runtime';\nimport { createClient } from 'graphql-ws';\n\nconst wsClient = createClient({\n url:'ws://localhost:3000',\n});\n\nconst subscribe = (operation, variables) => {\n return Observable.create((sink) => {\n return wsClient.subscribe(\n {\n operationName: operation.name,\n query: operation.text,\n variables,\n },\n sink,\n );\n });\n}\n\nconst network = Network.create(fetchQuery, subscribe);\n"})}),(0,t.jsxs)(i.p,{children:["Alternatively, the legacy ",(0,t.jsx)(i.a,{href:"https://github.com/apollographql/subscriptions-transport-ws",children:"subscriptions-transport-ws"})," library can be used too:"]}),(0,t.jsx)(i.pre,{children:(0,t.jsx)(i.code,{className:"language-javascript",children:"import {\n ...\n Network,\n Observable\n} from 'relay-runtime';\nimport { SubscriptionClient } from 'subscriptions-transport-ws';\n\nconst subscriptionClient = new SubscriptionClient('ws://localhost:3000', {\n reconnect: true,\n});\n\nconst subscribe = (request, variables) => {\n const subscribeObservable = subscriptionClient.request({\n query: request.text,\n operationName: request.name,\n variables,\n });\n // Important: Convert subscriptions-transport-ws observable type to Relay's\n return Observable.from(subscribeObservable);\n};\n\nconst network = Network.create(fetchQuery, subscribe);\n"})})]}),"\n",(0,t.jsx)(o.FbInternalOnly,{children:(0,t.jsxs)(i.p,{children:["At Facebook, the Network Layer has already been configured to handle GraphQL Subscriptions. For more details on writing subscriptions at Facebook, check out this ",(0,t.jsx)(i.a,{href:"../../../guides/writing-subscriptions/",children:"guide"}),". For a guide on setting up subscriptions on the server side, check out this ",(0,t.jsx)(i.a,{href:"https://www.internalfb.com/intern/wiki/GraphQL_Subscriptions/creating-a-new-subscription/",children:"wiki"}),"."]})}),"\n",(0,t.jsx)(a.A,{})]})}function p(e={}){let{wrapper:i}={...(0,r.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(u,{...e})}):u(e)}},86898(e,i,n){n.d(i,{A:()=>b});var s=n(74848),t=n(95310),r=n(17352),a=n(96540);function o(){let e=window.encodeURI(JSON.stringify({title:"Feedback about "+window.location.pathname,description:"**!!! Required !!!**\n\nPlease modify the task description to let us know how the docs can be improved.\n\n**Please do not ask support questions via this form! Instead, ask in fburl.com/relay_
1support**",tag_ids:{add:[0xac96423e5b680,0x64079768ac750]}}));window.open("https://www.internalfb.com/tasks/?n="+e)}function c({children:e}){return(0,s.jsxs)("div",{className:"docsRating",id:"docsRating",children:[(0,s.jsx)("hr",{}),e]})}let l=()=>{let[e,i]=a.useState(!1),n=e=>{i(!0),window.ga&&window.ga("send",{hitType:"event",eventCategory:"button",eventAction:"feedback",eventValue:e})};return e?"Thank you for letting us know!":(0,s.jsxs)(s.Fragment,{children:["Is this page useful?",(0,s.jsx)("svg",{className:"i_thumbsup",alt:"Like",id:"docsRating-like",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>n(1),children:(0,s.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})}),(0,s.jsx)("svg",{className:"i_thumbsdown",alt:"Dislike",id:"docsRating-dislike",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>n(0),children:(0,s.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})})]})},d=()=>(0,s.jsxs)("p",{children:["Let us know how these docs can be improved by",(0,s.jsx)("a",{className:"button",role:"button",tabIndex:0,onClick:o,children:"Filing a task"})]}),h=()=>(0,s.jsxs)("p",{children:["Help us make the site even better by"," ",(0,s.jsx)(t.default,{to:"https://www.surveymonkey.com/r/FYC9TCJ",children:"answering a few quick questions"}),"."]}),u=()=>(0,s.jsxs)(c,{children:[(0,s.jsx)(d,{}),(0,s.jsx)(l,{}),(0,s.jsx)(h,{})]}),p=()=>(0,s.jsxs)(c,{children:[(0,s.jsx)(l,{}),(0,s.jsx)(h,{})]}),b=()=>(0,r.fbContent)({internal:(0,s.jsx)(u,{}),external:(0,s.jsx)(p,{})})},28453(e,i,n){n.d(i,{R:()=>a,x:()=>o});var s=n(96540);let t={},r=s.createContext(t);function a(e){let i=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(i):{...i,...e}},[i,e])}function o(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),s.createElement(r.Provider,{value:i},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.