From dcc853211623616aabb57ce46912194e1bf7b2ce Mon Sep 17 00:00:00 2001 From: voxmatt Date: Sun, 1 Oct 2017 11:03:05 -0700 Subject: [PATCH] removing UMD style module structure from relay-runtime and moving new relay types to v1 folder --- types/react-relay/index.d.ts | 459 +--- types/react-relay/react-relay-tests.tsx | 393 +-- types/react-relay/tsconfig.json | 4 +- types/react-relay/v1/index.d.ts | 474 ++++ .../v1/lib/react-relay-classic.d.ts | 0 .../v1/lib/react-relay-compat.d.ts | 0 .../v1/lib/react-relay-modern.d.ts | 0 types/react-relay/v1/react-relay-tests.tsx | 437 ++++ types/react-relay/v1/tsconfig.json | 31 + types/react-relay/{ => v1}/tslint.json | 0 types/relay-runtime/index.d.ts | 2182 ++++++++--------- 11 files changed, 2129 insertions(+), 1851 deletions(-) create mode 100644 types/react-relay/v1/index.d.ts create mode 100644 types/react-relay/v1/lib/react-relay-classic.d.ts create mode 100644 types/react-relay/v1/lib/react-relay-compat.d.ts create mode 100644 types/react-relay/v1/lib/react-relay-modern.d.ts create mode 100644 types/react-relay/v1/react-relay-tests.tsx create mode 100644 types/react-relay/v1/tsconfig.json rename types/react-relay/{ => v1}/tslint.json (100%) diff --git a/types/react-relay/index.d.ts b/types/react-relay/index.d.ts index 83b2890020..4f9f49e5e7 100644 --- a/types/react-relay/index.d.ts +++ b/types/react-relay/index.d.ts @@ -1,385 +1,86 @@ -// Type definitions for react-relay 1.3 +// Type definitions for react-relay 0.9.2 // Project: https://github.com/facebook/relay -// Definitions by: Johannes Schickling , Matt Martin +// Definitions by: Johannes Schickling // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 2.3 -/// - -declare namespace __Relay { - //////////////////////////// - // RELAY MODERN TYPES - /////////////////////////// - namespace Modern { - // ~~~~~~~~~~~~~~~~~~~~~ - // Maybe Fix - // ~~~~~~~~~~~~~~~~~~~~~ - type ConcreteFragment = any; - type ConcreteBatch = any; - type ConcreteFragmentDefinition = object; - type ConcreteOperationDefinition = object; - type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayProp - // ~~~~~~~~~~~~~~~~~~~~~ - // note: refetch and pagination containers augment this - interface RelayProp { - environment: Runtime.Environment; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayQL - // ~~~~~~~~~~~~~~~~~~~~~ - function RelayQL( - strings: string[], - ...substitutions: any[] - ): Common.RelayConcreteNode; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayModernGraphQLTag - // ~~~~~~~~~~~~~~~~~~~~~ - interface GeneratedNodeMap { [key: string]: GraphQLTaggedNode; } - type GraphQLTaggedNode = - (() => ConcreteFragment | ConcreteBatch) - | { - modern(): ConcreteFragment | ConcreteBatch, - classic(relayQL: typeof RelayQL): - | ConcreteFragmentDefinition - | ConcreteOperationDefinition, - }; - /** - * Runtime function to correspond to the `graphql` tagged template function. - * All calls to this function should be transformed by the plugin. - */ - interface GraphqlInterface { - (strings: string[] | TemplateStringsArray): GraphQLTaggedNode; - experimental(strings: string[] | TemplateStringsArray): GraphQLTaggedNode; - } - const graphql: GraphqlInterface; - - // ~~~~~~~~~~~~~~~~~~~~~ - // ReactRelayQueryRenderer - // ~~~~~~~~~~~~~~~~~~~~~ - interface QueryRendererProps { - cacheConfig?: Common.CacheConfig; - environment: Runtime.Environment; - query: GraphQLTaggedNode; - render(readyState: ReadyState): React.ReactElement | undefined | null; - variables: Common.Variables; - rerunParamExperimental?: Common.RerunParam; - } - interface ReadyState { - error: Error | undefined | null; - props: { [propName: string]: any } | undefined | null; - retry?(): void; - } - interface QueryRendererState { - readyState: ReadyState; - } - class ReactRelayQueryRenderer extends React.Component { } - - // ~~~~~~~~~~~~~~~~~~~~~ - // createFragmentContainer - // ~~~~~~~~~~~~~~~~~~~~~ - function createFragmentContainer( - Component: ReactBaseComponent, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, - ): ReactBaseComponent; - - // ~~~~~~~~~~~~~~~~~~~~~ - // createPaginationContainer - // ~~~~~~~~~~~~~~~~~~~~~ - interface PageInfo { - endCursor: string | undefined | null; - hasNextPage: boolean; - hasPreviousPage: boolean; - startCursor: string | undefined | null; - } - interface ConnectionData { - edges?: any[]; - pageInfo?: PageInfo; - } - type RelayPaginationProp = RelayProp & { - hasMore(): boolean, - isLoading(): boolean, - loadMore( - pageSize: number, - callback: (error?: Error) => void, - options?: RefetchOptions, - ): Common.Disposable | undefined | null, - refetchConnection( - totalCount: number, - callback: (error?: Error) => void, - refetchVariables?: Common.Variables, - ): Common.Disposable | undefined | null, - }; - function FragmentVariablesGetter( - prevVars: Common.Variables, - totalCount: number, - ): Common.Variables; - interface ConnectionConfig { - direction?: 'backward' | 'forward'; - getConnectionFromProps?(props: object): ConnectionData | undefined | null; - getFragmentVariables?: typeof FragmentVariablesGetter; - getVariables( - props: { [propName: string]: any }, - paginationInfo: { count: number, cursor?: string }, - fragmentVariables: Common.Variables, - ): Common.Variables; - query: GraphQLTaggedNode; - } - function createPaginationContainer( - Component: ReactBaseComponent, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, - connectionConfig: ConnectionConfig, - ): ReactBaseComponent; - - // ~~~~~~~~~~~~~~~~~~~~~ - // createFragmentContainer - // ~~~~~~~~~~~~~~~~~~~~~ - interface RefetchOptions { - force?: boolean; - rerunParamExperimental?: Common.RerunParam; - } - type RelayRefetchProp = RelayProp & { - refetch( - refetchVariables: Common.Variables | ((fragmentVariables: Common.Variables) => Common.Variables), - renderVariables?: Common.Variables, - callback?: (error?: Error) => void, - options?: RefetchOptions, - ): Common.Disposable, - }; - function createRefetchContainer( - Component: ReactBaseComponent, - fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, - taggedNode: GraphQLTaggedNode, - ): ReactBaseComponent; - } - - //////////////////////////// - // RELAY CLASSIC TYPES - /////////////////////////// - // note: the namespace here is really for use inside of - // relay-compat; the module declaration below - // uses the old, pre-existing types - namespace Classic { - // ~~~~~~~~~~~~~~~~~~~~~ - // Maybe Fix - // ~~~~~~~~~~~~~~~~~~~~~ - type StoreReaderData = any; - type StoreReaderOptions = any; - type RelayStoreData = any; - interface RelayQuery { Fragment: any; Node: any; Root: any; } - - // ~~~~~~~~~~~~~~~~~~~~~ - // Environment - // ~~~~~~~~~~~~~~~~~~~~~ - interface FragmentResolver { - dispose(): void; - resolve( - fragment: RelayQuery["Fragment"], - dataIDs: Common.DataID | Common.DataID[], - ): StoreReaderData | StoreReaderData[] | undefined | null; - } - interface RelayEnvironmentInterface { - forceFetch( - querySet: Common.RelayQuerySet, - onReadyStateChange: Common.ReadyStateChangeCallback, - ): Common.Abortable; - getFragmentResolver( - fragment: RelayQuery["Fragment"], - onNext: () => void, - ): FragmentResolver; - getStoreData(): RelayStoreData; - primeCache( - querySet: Common.RelayQuerySet, - onReadyStateChange: Common.ReadyStateChangeCallback, - ): Common.Abortable; - read( - node: RelayQuery["Node"], - dataID: Common.DataID, - options?: StoreReaderOptions, - ): StoreReaderData | void; - readQuery( - root: RelayQuery["Root"], - options?: StoreReaderOptions, - ): StoreReaderData[] | void; - } - } - - //////////////////////////// - // RELAY COMPAT TYPES - /////////////////////////// - namespace Compat { - // ~~~~~~~~~~~~~~~~~~~~~ - // Maybe Fix - // ~~~~~~~~~~~~~~~~~~~~~ - type ConcreteFragment = any; - type ConcreteBatch = any; - type ConcreteFragmentDefinition = object; - type ConcreteOperationDefinition = object; - - // ~~~~~~~~~~~~~~~~~~~~~ - // Util - // ~~~~~~~~~~~~~~~~~~~~~ - function getFragment(q: string, v?: Common.Variables): string; - interface ComponentWithFragment extends React.ComponentClass { - getFragment: typeof getFragment; - } - interface StatelessWithFragment extends React.StatelessComponent { - getFragment: typeof getFragment; - } - type ReactFragmentComponent = ComponentWithFragment | StatelessWithFragment; - type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; - type RelayClassicEnvironment = Classic.RelayEnvironmentInterface; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayCompatTypes - // ~~~~~~~~~~~~~~~~~~~~~ - type CompatEnvironment = Runtime.Environment | RelayClassicEnvironment; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayCompatMutations - // ~~~~~~~~~~~~~~~~~~~~~ - function commitUpdate( - environment: CompatEnvironment, - config: Runtime.MutationConfig, - ): Common.Disposable; - function applyUpdate( - environment: CompatEnvironment, - config: Runtime.OptimisticMutationConfig, - ): Common.Disposable; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayCompatContainer - // ~~~~~~~~~~~~~~~~~~~~~ - interface GeneratedNodeMap { [key: string]: Modern.GraphQLTaggedNode; } - function createContainer( - Component: ReactBaseComponent, - fragmentSpec: Modern.GraphQLTaggedNode | GeneratedNodeMap, - ): ReactFragmentComponent; - - // ~~~~~~~~~~~~~~~~~~~~~ - // injectDefaultVariablesProvider - // ~~~~~~~~~~~~~~~~~~~~~ - type VariablesProvider = () => Common.Variables; - function injectDefaultVariablesProvider(variablesProvider: VariablesProvider): void; - } -} - -//////////////////////////// -// MODULES -/////////////////////////// -// tslint:disable strict-export-declare-modifiers -declare module 'react-relay' { - export import QueryRenderer = __Relay.Modern.ReactRelayQueryRenderer; - export import createFragmentContainer = __Relay.Modern.createFragmentContainer; - export import createPaginationContainer = __Relay.Modern.createPaginationContainer; - export import createRefetchContainer = __Relay.Modern.createRefetchContainer; - export import graphql = __Relay.Modern.graphql; - - export import commitLocalUpdate = __Relay.Runtime.commitLocalUpdate; - export import commitMutation = __Relay.Runtime.commitRelayModernMutation; - export import fetchQuery = __Relay.Runtime.fetchRelayModernQuery; - export import requestSubscription = __Relay.Runtime.requestRelaySubscription; - - // exported for convenience — not exports in the original module - export import RelayProp = __Relay.Modern.RelayProp; - export import RelayPaginationProp = __Relay.Modern.RelayPaginationProp; - export import RelayRefetchProp = __Relay.Modern.RelayRefetchProp; -} - -declare module 'react-relay/compat' { - export import applyOptimisticMutation = __Relay.Compat.applyUpdate; - export import commitMutation = __Relay.Compat.commitUpdate; - export import createFragmentContainer = __Relay.Compat.createContainer; - export import createPaginationContainer = __Relay.Compat.createContainer; - export import createRefetchContainer = __Relay.Compat.createContainer; - export import injectDefaultVariablesProvider = __Relay.Compat.injectDefaultVariablesProvider; - export import QueryRenderer = __Relay.Modern.ReactRelayQueryRenderer; - export import graphql = __Relay.Modern.graphql; - export import fetchQuery = __Relay.Runtime.fetchRelayModernQuery; -} -// tslint:enable strict-export-declare-modifiers - -declare module "react-relay/classic" { +declare module "react-relay" { import * as React from "react"; type ClientMutationID = string; /** Fragments are a hash of functions */ interface Fragments { - [query: string]: ((variables?: RelayVariables) => string); + [query: string]: ((variables?: RelayVariables) => string) } interface CreateContainerOpts { - initialVariables?: any; - fragments: Fragments; - prepareVariables?(prevVariables: RelayVariables): RelayVariables; + initialVariables?: any + fragments: Fragments + prepareVariables?(prevVariables: RelayVariables): RelayVariables } interface RelayVariables { - [name: string]: any; + [name: string]: any } /** add static getFragment method to the component constructor */ interface RelayContainerClass extends React.ComponentClass { - getFragment: ((q: string, v?: RelayVariables) => string); + getFragment: ((q: string, v?: RelayVariables) => string) } interface RelayQueryRequestResolve { - response: any; + response: any } type RelayMutationStatus = - 'UNCOMMITTED' | // Transaction hasn't yet been sent to the server. Transaction can be committed or rolled back. - 'COMMIT_QUEUED' | // Transaction was committed but another transaction with the same collision key is pending, so the transaction has been queued to send to the server. - 'COLLISION_COMMIT_FAILED' | // Transaction was queued for commit but another transaction with the same collision key failed. All transactions in the collision queue, - // including this one, have been failed. Transaction can be recommitted or rolled back. - 'COMMITTING' | // Transaction is waiting for the server to respond. - 'COMMIT_FAILED'; + 'UNCOMMITTED' | // Transaction hasn't yet been sent to the server. Transaction can be committed or rolled back. + 'COMMIT_QUEUED' | // Transaction was committed but another transaction with the same collision key is pending, so the transaction has been queued to send to the server. + 'COLLISION_COMMIT_FAILED' | //Transaction was queued for commit but another transaction with the same collision key failed. All transactions in the collision queue, including this one, have been failed. Transaction can be recommitted or rolled back. + 'COMMITTING' | // Transaction is waiting for the server to respond. + 'COMMIT_FAILED'; class RelayMutationTransaction { - applyOptimistic(): RelayMutationTransaction; - commit(): RelayMutationTransaction | null; - recommit(): void; - rollback(): void; - getError(): Error; - getStatus(): RelayMutationStatus; - getHash(): string; - getID(): ClientMutationID; + applyOptimistic(): RelayMutationTransaction; + commit(): RelayMutationTransaction | null; + recommit(): void; + rollback(): void; + getError(): Error; + getStatus(): RelayMutationStatus; + getHash(): string; + getID(): ClientMutationID; } interface RelayMutationRequest { - getQueryString(): string; - getVariables(): RelayVariables; - resolve(result: RelayQueryRequestResolve): any; - reject(errors: any): any; + getQueryString(): string + getVariables(): RelayVariables + resolve(result: RelayQueryRequestResolve): any + reject(errors: any): any } interface RelayQueryRequest { - resolve(result: RelayQueryRequestResolve): any; - reject(errors: any): any; - getQueryString(): string; - getVariables(): RelayVariables; - getID(): string; - getDebugName(): string; + resolve(result: RelayQueryRequestResolve): any + reject(errors: any): any + + getQueryString(): string + getVariables(): RelayVariables + getID(): string + getDebugName(): string } interface RelayNetworkLayer { - supports(...options: string[]): boolean; + supports(...options: string[]): boolean } class DefaultNetworkLayer implements RelayNetworkLayer { - constructor(host: string, options?: any); - supports(...options: string[]): boolean; + constructor(host: string, options?: any) + supports(...options: string[]): boolean } - function createContainer(component: React.ComponentClass | React.StatelessComponent, params?: CreateContainerOpts): RelayContainerClass; - function injectNetworkLayer(networkLayer: RelayNetworkLayer): any; - function isContainer(component: React.ComponentClass): boolean; - function QL(...args: any[]): string; + function createContainer(component: React.ComponentClass | React.StatelessComponent, params?: CreateContainerOpts): RelayContainerClass + function injectNetworkLayer(networkLayer: RelayNetworkLayer): any + function isContainer(component: React.ComponentClass): boolean + function QL(...args: any[]): string class Route { constructor(params?: RelayVariables) @@ -390,59 +91,39 @@ declare module "react-relay/classic" { * S is typically dynamic as it depends on the data the app is currently using, but it's possible to always * return some data in the payload using REQUIRED_CHILDREN which is where specifying S is the most useful. */ - class Mutation { - props: T; + class Mutation { + props: T - constructor(props: T); - static getFragment(q: string): string; + constructor(props: T) + static getFragment(q: string): string } interface Transaction { - getError(): Error; - Status(): number; + getError(): Error + Status(): number } interface StoreUpdateCallbacks { - onFailure?(transaction: Transaction): any; - onSuccess?(response: T): any; + onFailure?(transaction: Transaction): any + onSuccess?(response: T): any } interface Store { - commitUpdate(mutation: Mutation, callbacks?: StoreUpdateCallbacks): any; + commitUpdate(mutation: Mutation, callbacks?: StoreUpdateCallbacks): any } - const Store: Store; + var Store: Store - class RootContainer extends React.Component { } + class RootContainer extends React.Component {} - interface RootContainerProps extends React.Props { - Component: RelayContainerClass; - route: Route; - renderLoading?(): JSX.Element; - renderFetched?(data: any): JSX.Element; - renderFailure?(error: Error, retry: (...args: any[]) => any): JSX.Element; + interface RootContainerProps extends React.Props{ + Component: RelayContainerClass + route: Route + renderLoading?(): JSX.Element + renderFetched?(data: any): JSX.Element + renderFailure?(error: Error, retry: Function): JSX.Element } - class Renderer extends React.Component { } - - interface RendererProps { - Container: RelayContainerClass; // Relay container that defines fragments and the view to render. - forceFetch?: boolean; // Whether to send a server request regardless of data available on the client. - queryConfig: Route; // `QueryConfig` or `Relay.Route` that defines the query roots. - environment: Store; // An instance of `Relay.Environment` or any object that implements the `RelayEnvironment` interface. - render?: RenderCallback; // Called to render when data requirements are being fulfilled. - onReadyStateChange?: OnReadyStateChange; - } - - interface RenderStateConfig { - props?: { [propName: string]: any }; - done: boolean; - error?: Error; - retry?(): void; - stale: boolean; - } - type RenderCallback = (renderState: RenderStateConfig) => any; - type ReadyStateEvent = 'ABORT' | 'CACHE_RESTORED_REQUIRED' | @@ -455,23 +136,25 @@ declare module "react-relay/classic" { 'STORE_FOUND_ALL' | 'STORE_FOUND_REQUIRED'; - type OnReadyStateChange = (readyState: { - ready: boolean, - done: boolean, - stale: boolean, - error?: Error, - events: ReadyStateEvent[], - aborted: boolean - }) => void; + interface OnReadyStateChange { + (readyState: { + ready: boolean, + done: boolean, + stale: boolean, + error?: Error, + events: Array, + aborted: boolean + }): void + } interface RelayProp { readonly route: { name: string; }; // incomplete, also has params and queries readonly variables: any; - readonly pendingVariables?: any; + readonly pendingVariables?: any | null; setVariables(variables: any, onReadyStateChange?: OnReadyStateChange): void; forceFetch(variables: any, onReadyStateChange?: OnReadyStateChange): void; hasOptimisticUpdate(record: any): boolean; getPendingTransactions(record: any): RelayMutationTransaction[]; - commitUpdate(mutation: Mutation, callbacks?: StoreUpdateCallbacks): any; + commitUpdate: (mutation: Mutation, callbacks?: StoreUpdateCallbacks) => any; } } diff --git a/types/react-relay/react-relay-tests.tsx b/types/react-relay/react-relay-tests.tsx index 2b8e371ee2..90425156cc 100644 --- a/types/react-relay/react-relay-tests.tsx +++ b/types/react-relay/react-relay-tests.tsx @@ -1,375 +1,30 @@ -import * as React from "react"; -import { - Environment, - Network, - RecordSource, - Store, - ConnectionHandler, -} from 'relay-runtime'; - -//////////////////////////// -// RELAY MODERN TESTS -/////////////////////////// -import { - graphql, - commitMutation, - createFragmentContainer, - createPaginationContainer, - createRefetchContainer, - requestSubscription, - QueryRenderer, - RelayPaginationProp, - RelayRefetchProp -} from "react-relay"; - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern Environment -// ~~~~~~~~~~~~~~~~~~~~~ -function fetchQuery(operation: any, variables: any, cacheConfig: {}) { - return fetch('/graphql'); -} -const network = Network.create(fetchQuery); -const source = new RecordSource(); -const store = new Store(source); -const modernEnvironment = new Environment({ network, store }); - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern QueryRenderer -// ~~~~~~~~~~~~~~~~~~~~~ -const MyQueryRenderer = (props: { name: string}) => ( - { - if (error) { - return
{error.message}
; - } else if (props) { - return
{props.name} is great!
; - } - return
Loading
; - }} - /> -); - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern FragmentContainer -// ~~~~~~~~~~~~~~~~~~~~~ -const MyFragmentContainer = createFragmentContainer( - class TodoListView extends React.Component { - render() { - return
; - } - }, - { - item: graphql` - fragment TodoItem_item on Todo { - text - isComplete - } - `, - } -); - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern RefetchContainer -// ~~~~~~~~~~~~~~~~~~~~~ -interface StoryInterface { id: string; } -interface FeedStoriesProps { - relay: RelayRefetchProp; - feed: { - stories: { edges: Array<{ node: StoryInterface }> } - }; -} -class Story extends React.Component<{ story: StoryInterface }> {} -class FeedStories extends React.Component { - render() { - return ( -
- {this.props.feed.stories.edges.map( - edge => - )} -
- ); - } - - _loadMore() { - // Increments the number of stories being rendered by 10. - const refetchVariables = (fragmentVariables: {count: number }) => ({ - count: fragmentVariables.count + 10, - }); - this.props.relay.refetch(refetchVariables); - } - } - -const FeedRefetchContainer = createRefetchContainer( - FeedStories, - { - feed: graphql.experimental` - fragment FeedStories_feed on Feed - @argumentDefinitions( - count: {type: "Int", defaultValue: 10} - ) { - stories(first: $count) { - edges { - node { - id - ...Story_story - } - } - } - } - ` - }, - graphql.experimental` - query FeedStoriesRefetchQuery($count: Int) { - feed { - ...FeedStories_feed @arguments(count: $count) - } - } - `, - ); - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern PaginationContainer -// ~~~~~~~~~~~~~~~~~~~~~ -interface FeedProps { - user: { feed: { edges: Array<{ node: StoryInterface}>}}; - relay: RelayPaginationProp; -} -class Feed extends React.Component { - render() { - return (
- {this.props.user.feed.edges.map( - edge => - )} -
); - } - - _loadMore() { - if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { - return; - } - - this.props.relay.loadMore( - 10, // Fetch the next 10 feed items - e => { - console.log(e); - }, - ); - } -} - -const FeedPaginationContainer = createPaginationContainer( - Feed, - { - user: graphql` - fragment Feed_user on User { - feed( - first: $count - after: $cursor - orderby: $orderBy # other variables - ) @connection(key: "Feed_feed") { - edges { - node { - id - ...Story_story - } - } - } - } - `, - }, - { - direction: 'forward', - getConnectionFromProps(props: { user: {feed: any}}) { - return props.user && props.user.feed; - }, - getFragmentVariables(prevVars, totalCount) { - return { - ...prevVars, - count: totalCount, - }; - }, - getVariables(props, {count, cursor}, fragmentVariables) { - return { - count, - cursor, - // in most cases, for variables other than connection filters like - // `first`, `after`, etc. you may want to use the previous values. - orderBy: fragmentVariables.orderBy, - }; - }, - query: graphql` - query FeedPaginationQuery( - $count: Int! - $cursor: String - $orderby: String! - ) { - user { - # You could reference the fragment defined previously. - ...Feed_user - } - } - ` - } -); - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern Mutations -// ~~~~~~~~~~~~~~~~~~~~~ -const mutation = graphql` -mutation MarkReadNotificationMutation( - $input: MarkReadNotificationData! -) { - markReadNotification(data: $input) { - notification { - seenState - } - } -} -`; - -const optimisticResponse = { - markReadNotification: { - notification: { - seenState: 'SEEN', - }, - }, -}; - -const configs = [{ - type: 'NODE_DELETE' as 'NODE_DELETE', - deletedIDFieldName: 'destroyedShipId', -}, { - type: 'RANGE_ADD' as 'RANGE_ADD', - parentID: 'shipId', - connectionInfo: [{ - key: 'AddShip_ships', - rangeBehavior: 'append', - }], - edgeName: 'newShipEdge', -}, { - type: 'RANGE_DELETE' as 'RANGE_DELETE', - parentID: 'todoId', - connectionKeys: [{ - key: 'RemoveTags_tags', - rangeBehavior: 'append', - }], - pathToConnection: ['todo', 'tags'], - deletedIDFieldName: 'removedTagId' -}]; - -function markNotificationAsRead(source: string, storyID: string) { - const variables = { - input: { - source, - storyID, - }, - }; - - commitMutation( - modernEnvironment, - { - configs, - mutation, - optimisticResponse, - variables, - onCompleted: (response, errors) => { - console.log('Response received from server.'); - }, - onError: err => console.error(err), - }, - ); -} - -// ~~~~~~~~~~~~~~~~~~~~~ -// Modern Subscriptions -// ~~~~~~~~~~~~~~~~~~~~~ -const subscription = graphql` - subscription MarkReadNotificationSubscription( - $storyID: ID! - ) { - markReadNotification(storyID: $storyID) { - notification { - seenState - } - } - } -`; -const variables = { - storyID: '123', -}; -requestSubscription( - modernEnvironment, // see Environment docs - { - subscription, - variables, - // optional but recommended: - onCompleted: () => {}, - onError: error => console.error(error), - // example of a custom updater - updater: store => { - // Get the notification - const rootField = store.getRootField('markReadNotification'); - const notification = !!rootField && rootField.getLinkedRecord('notification'); - // Add it to a connection - const viewer = store.getRoot().getLinkedRecord('viewer'); - const notifications = - ConnectionHandler.getConnection(viewer, 'notifications'); - const edge = ConnectionHandler.createEdge( - store, - notifications, - notification, - '', - ); - ConnectionHandler.insertEdgeAfter(notifications, edge); - }, - } -); - -//////////////////////////// -// RELAY-CLASSIC TESTS -/////////////////////////// - -import * as Relay from "react-relay/classic"; +import * as React from "react" +import * as Relay from "react-relay" interface Props { - text: string; - userId: string; + text: string + userId: string } -export default class AddTweetMutation extends Relay.Mutation { - getMutation() { - return Relay.QL`mutation{addTweet}`; +interface Response { +} + +export default class AddTweetMutation extends Relay.Mutation { + + getMutation () { + return Relay.QL`mutation{addTweet}` } - getFatQuery() { + getFatQuery () { return Relay.QL` fragment on AddTweetPayload { tweetEdge user } - `; + ` } - getConfigs() { + getConfigs () { return [{ type: "RANGE_ADD", parentName: "user", @@ -379,19 +34,19 @@ export default class AddTweetMutation extends Relay.Mutation { rangeBehaviors: { "": "append", }, - }]; + }] } - getVariables() { - return this.props; + getVariables () { + return this.props } } interface ArtworkProps { artwork: { title: string - }; - relay: Relay.RelayProp; + }, + relay: Relay.RelayProp, } class Artwork extends React.Component { @@ -400,7 +55,7 @@ class Artwork extends React.Component { {this.props.artwork.title} - ); + ) } } @@ -412,7 +67,7 @@ const ArtworkContainer = Relay.createContainer(Artwork, { } ` } -}); +}) class StubbedArtwork extends React.Component { render() { @@ -428,10 +83,10 @@ class StubbedArtwork extends React.Component { setVariables: () => {}, forceFetch: () => {}, hasOptimisticUpdate: () => false, - getPendingTransactions: (): any => undefined, + getPendingTransactions: (): Relay.RelayMutationTransaction[] => undefined, commitUpdate: () => {}, } - }; - return ; + } + return } } diff --git a/types/react-relay/tsconfig.json b/types/react-relay/tsconfig.json index 9cabc08c4a..90680fbafc 100644 --- a/types/react-relay/tsconfig.json +++ b/types/react-relay/tsconfig.json @@ -7,7 +7,7 @@ ], "noImplicitAny": true, "noImplicitThis": true, - "strictNullChecks": true, + "strictNullChecks": false, "baseUrl": "../", "typeRoots": [ "../" @@ -21,4 +21,4 @@ "index.d.ts", "react-relay-tests.tsx" ] -} +} \ No newline at end of file diff --git a/types/react-relay/v1/index.d.ts b/types/react-relay/v1/index.d.ts new file mode 100644 index 0000000000..14c7bb756a --- /dev/null +++ b/types/react-relay/v1/index.d.ts @@ -0,0 +1,474 @@ +// Type definitions for react-relay 1.3 +// Project: https://github.com/facebook/relay +// Definitions by: Johannes Schickling , Matt Martin +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.4 + +import * as React from 'react'; +import { RelayCommonTypes, RelayRuntimeTypes } from 'relay-runtime'; + +//////////////////////////// +// RELAY MODERN TYPES +/////////////////////////// +export namespace RelayModernTypes { + // ~~~~~~~~~~~~~~~~~~~~~ + // Maybe Fix + // ~~~~~~~~~~~~~~~~~~~~~ + type ConcreteFragment = any; + type ConcreteBatch = any; + type ConcreteFragmentDefinition = object; + type ConcreteOperationDefinition = object; + type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayProp + // ~~~~~~~~~~~~~~~~~~~~~ + // note: refetch and pagination containers augment this + interface RelayProp { + environment: RelayRuntimeTypes.Environment; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayQL + // ~~~~~~~~~~~~~~~~~~~~~ + function RelayQL( + strings: string[], + ...substitutions: any[] + ): RelayCommonTypes.RelayConcreteNode; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayModernGraphQLTag + // ~~~~~~~~~~~~~~~~~~~~~ + interface GeneratedNodeMap { [key: string]: GraphQLTaggedNode; } + type GraphQLTaggedNode = + (() => ConcreteFragment | ConcreteBatch) + | { + modern(): ConcreteFragment | ConcreteBatch, + classic(relayQL: typeof RelayQL): + | ConcreteFragmentDefinition + | ConcreteOperationDefinition, + }; + /** + * Runtime function to correspond to the `graphql` tagged template function. + * All calls to this function should be transformed by the plugin. + */ + interface GraphqlInterface { + (strings: string[] | TemplateStringsArray): GraphQLTaggedNode; + experimental(strings: string[] | TemplateStringsArray): GraphQLTaggedNode; + } + const graphql: GraphqlInterface; + + // ~~~~~~~~~~~~~~~~~~~~~ + // ReactRelayQueryRenderer + // ~~~~~~~~~~~~~~~~~~~~~ + interface QueryRendererProps { + cacheConfig?: RelayCommonTypes.CacheConfig; + environment: RelayRuntimeTypes.Environment; + query: GraphQLTaggedNode; + render(readyState: ReadyState): React.ReactElement | undefined | null; + variables: RelayCommonTypes.Variables; + rerunParamExperimental?: RelayCommonTypes.RerunParam; + } + interface ReadyState { + error: Error | undefined | null; + props: { [propName: string]: any } | undefined | null; + retry?(): void; + } + interface QueryRendererState { + readyState: ReadyState; + } + class ReactRelayQueryRenderer extends React.Component { } + + // ~~~~~~~~~~~~~~~~~~~~~ + // createFragmentContainer + // ~~~~~~~~~~~~~~~~~~~~~ + function createFragmentContainer( + Component: ReactBaseComponent, + fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, + ): ReactBaseComponent; + + // ~~~~~~~~~~~~~~~~~~~~~ + // createPaginationContainer + // ~~~~~~~~~~~~~~~~~~~~~ + interface PageInfo { + endCursor: string | undefined | null; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string | undefined | null; + } + interface ConnectionData { + edges?: any[]; + pageInfo?: PageInfo; + } + type RelayPaginationProp = RelayProp & { + hasMore(): boolean, + isLoading(): boolean, + loadMore( + pageSize: number, + callback: (error?: Error) => void, + options?: RefetchOptions, + ): RelayCommonTypes.Disposable | undefined | null, + refetchConnection( + totalCount: number, + callback: (error?: Error) => void, + refetchVariables?: RelayCommonTypes.Variables, + ): RelayCommonTypes.Disposable | undefined | null, + }; + function FragmentVariablesGetter( + prevVars: RelayCommonTypes.Variables, + totalCount: number, + ): RelayCommonTypes.Variables; + interface ConnectionConfig { + direction?: 'backward' | 'forward'; + getConnectionFromProps?(props: object): ConnectionData | undefined | null; + getFragmentVariables?: typeof FragmentVariablesGetter; + getVariables( + props: { [propName: string]: any }, + paginationInfo: { count: number, cursor?: string }, + fragmentVariables: RelayCommonTypes.Variables, + ): RelayCommonTypes.Variables; + query: GraphQLTaggedNode; + } + function createPaginationContainer( + Component: ReactBaseComponent, + fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, + connectionConfig: ConnectionConfig, + ): ReactBaseComponent; + + // ~~~~~~~~~~~~~~~~~~~~~ + // createFragmentContainer + // ~~~~~~~~~~~~~~~~~~~~~ + interface RefetchOptions { + force?: boolean; + rerunParamExperimental?: RelayCommonTypes.RerunParam; + } + type RelayRefetchProp = RelayProp & { + refetch( + refetchVariables: RelayCommonTypes.Variables | ((fragmentVariables: RelayCommonTypes.Variables) => RelayCommonTypes.Variables), + renderVariables?: RelayCommonTypes.Variables, + callback?: (error?: Error) => void, + options?: RefetchOptions, + ): RelayCommonTypes.Disposable, + }; + function createRefetchContainer( + Component: ReactBaseComponent, + fragmentSpec: GraphQLTaggedNode | GeneratedNodeMap, + taggedNode: GraphQLTaggedNode, + ): ReactBaseComponent; +} + +//////////////////////////// +// RELAY CLASSIC TYPES +/////////////////////////// +// note: the namespace here is really for use inside of +// relay-compat; the module declaration below +// uses the old, pre-existing types +export namespace RelayClassicTypes { + // ~~~~~~~~~~~~~~~~~~~~~ + // Maybe Fix + // ~~~~~~~~~~~~~~~~~~~~~ + type StoreReaderData = any; + type StoreReaderOptions = any; + type RelayStoreData = any; + interface RelayQuery { Fragment: any; Node: any; Root: any; } + + // ~~~~~~~~~~~~~~~~~~~~~ + // Environment + // ~~~~~~~~~~~~~~~~~~~~~ + interface FragmentResolver { + dispose(): void; + resolve( + fragment: RelayQuery["Fragment"], + dataIDs: RelayCommonTypes.DataID | RelayCommonTypes.DataID[], + ): StoreReaderData | StoreReaderData[] | undefined | null; + } + interface RelayEnvironmentInterface { + forceFetch( + querySet: RelayCommonTypes.RelayQuerySet, + onReadyStateChange: RelayCommonTypes.ReadyStateChangeCallback, + ): RelayCommonTypes.Abortable; + getFragmentResolver( + fragment: RelayQuery["Fragment"], + onNext: () => void, + ): FragmentResolver; + getStoreData(): RelayStoreData; + primeCache( + querySet: RelayCommonTypes.RelayQuerySet, + onReadyStateChange: RelayCommonTypes.ReadyStateChangeCallback, + ): RelayCommonTypes.Abortable; + read( + node: RelayQuery["Node"], + dataID: RelayCommonTypes.DataID, + options?: StoreReaderOptions, + ): StoreReaderData | void; + readQuery( + root: RelayQuery["Root"], + options?: StoreReaderOptions, + ): StoreReaderData[] | void; + } +} + +//////////////////////////// +// RELAY COMPAT TYPES +/////////////////////////// +export namespace RelayCompatTypes { + // ~~~~~~~~~~~~~~~~~~~~~ + // Maybe Fix + // ~~~~~~~~~~~~~~~~~~~~~ + type ConcreteFragment = any; + type ConcreteBatch = any; + type ConcreteFragmentDefinition = object; + type ConcreteOperationDefinition = object; + + // ~~~~~~~~~~~~~~~~~~~~~ + // Util + // ~~~~~~~~~~~~~~~~~~~~~ + function getFragment(q: string, v?: RelayCommonTypes.Variables): string; + interface ComponentWithFragment extends React.ComponentClass { + getFragment: typeof getFragment; + } + interface StatelessWithFragment extends React.StatelessComponent { + getFragment: typeof getFragment; + } + type ReactFragmentComponent = ComponentWithFragment | StatelessWithFragment; + type ReactBaseComponent = React.ComponentClass | React.StatelessComponent; + type RelayClassicEnvironment = RelayClassicTypes.RelayEnvironmentInterface; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayCompatTypes + // ~~~~~~~~~~~~~~~~~~~~~ + type CompatEnvironment = RelayRuntimeTypes.Environment | RelayClassicEnvironment; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayCompatMutations + // ~~~~~~~~~~~~~~~~~~~~~ + function commitUpdate( + environment: CompatEnvironment, + config: RelayRuntimeTypes.MutationConfig, + ): RelayCommonTypes.Disposable; + function applyUpdate( + environment: CompatEnvironment, + config: RelayRuntimeTypes.OptimisticMutationConfig, + ): RelayCommonTypes.Disposable; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayCompatContainer + // ~~~~~~~~~~~~~~~~~~~~~ + interface GeneratedNodeMap { [key: string]: RelayModernTypes.GraphQLTaggedNode; } + function createContainer( + Component: ReactBaseComponent, + fragmentSpec: RelayModernTypes.GraphQLTaggedNode | GeneratedNodeMap, + ): ReactFragmentComponent; + + // ~~~~~~~~~~~~~~~~~~~~~ + // injectDefaultVariablesProvider + // ~~~~~~~~~~~~~~~~~~~~~ + type VariablesProvider = () => RelayCommonTypes.Variables; + function injectDefaultVariablesProvider(variablesProvider: VariablesProvider): void; +} + + +//////////////////////////// +// MODULES +/////////////////////////// +export import QueryRenderer = RelayModernTypes.ReactRelayQueryRenderer; +export import createFragmentContainer = RelayModernTypes.createFragmentContainer; +export import createPaginationContainer = RelayModernTypes.createPaginationContainer; +export import createRefetchContainer = RelayModernTypes.createRefetchContainer; +export import graphql = RelayModernTypes.graphql; + +export import commitLocalUpdate = RelayRuntimeTypes.commitLocalUpdate; +export import commitMutation = RelayRuntimeTypes.commitRelayModernMutation; +export import fetchQuery = RelayRuntimeTypes.fetchRelayModernQuery; +export import requestSubscription = RelayRuntimeTypes.requestRelaySubscription; + +// exported for convenience — not exports in the original module +export import RelayProp = RelayModernTypes.RelayProp; +export import RelayPaginationProp = RelayModernTypes.RelayPaginationProp; +export import RelayRefetchProp = RelayModernTypes.RelayRefetchProp; + +declare module 'react-relay/compat' { + export import applyOptimisticMutation = RelayCompatTypes.applyUpdate; + export import commitMutation = RelayCompatTypes.commitUpdate; + export import createFragmentContainer = RelayCompatTypes.createContainer; + export import createPaginationContainer = RelayCompatTypes.createContainer; + export import createRefetchContainer = RelayCompatTypes.createContainer; + export import injectDefaultVariablesProvider = RelayCompatTypes.injectDefaultVariablesProvider; + export import QueryRenderer = RelayModernTypes.ReactRelayQueryRenderer; + export import graphql = RelayModernTypes.graphql; + export import fetchQuery = RelayRuntimeTypes.fetchRelayModernQuery; +} +// tslint:enable strict-export-declare-modifiers + +declare module "react-relay/classic" { + import * as React from "react"; + + type ClientMutationID = string; + + /** Fragments are a hash of functions */ + interface Fragments { + [query: string]: ((variables?: RelayVariables) => string); + } + + interface CreateContainerOpts { + initialVariables?: any; + fragments: Fragments; + prepareVariables?(prevVariables: RelayVariables): RelayVariables; + } + + interface RelayVariables { + [name: string]: any; + } + + /** add static getFragment method to the component constructor */ + interface RelayContainerClass extends React.ComponentClass { + getFragment: ((q: string, v?: RelayVariables) => string); + } + + interface RelayQueryRequestResolve { + response: any; + } + + type RelayMutationStatus = + 'UNCOMMITTED' | // Transaction hasn't yet been sent to the server. Transaction can be committed or rolled back. + 'COMMIT_QUEUED' | // Transaction was committed but another transaction with the same collision key is pending, so the transaction has been queued to send to the server. + 'COLLISION_COMMIT_FAILED' | // Transaction was queued for commit but another transaction with the same collision key failed. All transactions in the collision queue, + // including this one, have been failed. Transaction can be recommitted or rolled back. + 'COMMITTING' | // Transaction is waiting for the server to respond. + 'COMMIT_FAILED'; + + class RelayMutationTransaction { + applyOptimistic(): RelayMutationTransaction; + commit(): RelayMutationTransaction | null; + recommit(): void; + rollback(): void; + getError(): Error; + getStatus(): RelayMutationStatus; + getHash(): string; + getID(): ClientMutationID; + } + + interface RelayMutationRequest { + getQueryString(): string; + getVariables(): RelayVariables; + resolve(result: RelayQueryRequestResolve): any; + reject(errors: any): any; + } + + interface RelayQueryRequest { + resolve(result: RelayQueryRequestResolve): any; + reject(errors: any): any; + getQueryString(): string; + getVariables(): RelayVariables; + getID(): string; + getDebugName(): string; + } + + interface RelayNetworkLayer { + supports(...options: string[]): boolean; + } + + class DefaultNetworkLayer implements RelayNetworkLayer { + constructor(host: string, options?: any); + supports(...options: string[]): boolean; + } + + function createContainer(component: React.ComponentClass | React.StatelessComponent, params?: CreateContainerOpts): RelayContainerClass; + function injectNetworkLayer(networkLayer: RelayNetworkLayer): any; + function isContainer(component: React.ComponentClass): boolean; + function QL(...args: any[]): string; + + class Route { + constructor(params?: RelayVariables) + } + + /** + * Relay Mutation class, where T are the props it takes and S is the returned payload from Relay.Store.update. + * S is typically dynamic as it depends on the data the app is currently using, but it's possible to always + * return some data in the payload using REQUIRED_CHILDREN which is where specifying S is the most useful. + */ + class Mutation { + props: T; + + constructor(props: T); + static getFragment(q: string): string; + } + + interface Transaction { + getError(): Error; + Status(): number; + } + + interface StoreUpdateCallbacks { + onFailure?(transaction: Transaction): any; + onSuccess?(response: T): any; + } + + interface Store { + commitUpdate(mutation: Mutation, callbacks?: StoreUpdateCallbacks): any; + } + + const Store: Store; + + class RootContainer extends React.Component { } + + interface RootContainerProps extends React.Props { + Component: RelayContainerClass; + route: Route; + renderLoading?(): JSX.Element; + renderFetched?(data: any): JSX.Element; + renderFailure?(error: Error, retry: (...args: any[]) => any): JSX.Element; + } + + class Renderer extends React.Component { } + + interface RendererProps { + Container: RelayContainerClass; // Relay container that defines fragments and the view to render. + forceFetch?: boolean; // Whether to send a server request regardless of data available on the client. + queryConfig: Route; // `QueryConfig` or `Relay.Route` that defines the query roots. + environment: Store; // An instance of `Relay.Environment` or any object that implements the `RelayEnvironment` interface. + render?: RenderCallback; // Called to render when data requirements are being fulfilled. + onReadyStateChange?: OnReadyStateChange; + } + + interface RenderStateConfig { + props?: { [propName: string]: any }; + done: boolean; + error?: Error; + retry?(): void; + stale: boolean; + } + type RenderCallback = (renderState: RenderStateConfig) => any; + + type ReadyStateEvent = + 'ABORT' | + 'CACHE_RESTORED_REQUIRED' | + 'CACHE_RESTORE_FAILED' | + 'CACHE_RESTORE_START' | + 'NETWORK_QUERY_ERROR' | + 'NETWORK_QUERY_RECEIVED_ALL' | + 'NETWORK_QUERY_RECEIVED_REQUIRED' | + 'NETWORK_QUERY_START' | + 'STORE_FOUND_ALL' | + 'STORE_FOUND_REQUIRED'; + + type OnReadyStateChange = (readyState: { + ready: boolean, + done: boolean, + stale: boolean, + error?: Error, + events: ReadyStateEvent[], + aborted: boolean + }) => void; + + interface RelayProp { + readonly route: { name: string; }; // incomplete, also has params and queries + readonly variables: any; + readonly pendingVariables?: any; + setVariables(variables: any, onReadyStateChange?: OnReadyStateChange): void; + forceFetch(variables: any, onReadyStateChange?: OnReadyStateChange): void; + hasOptimisticUpdate(record: any): boolean; + getPendingTransactions(record: any): RelayMutationTransaction[]; + commitUpdate(mutation: Mutation, callbacks?: StoreUpdateCallbacks): any; + } +} diff --git a/types/react-relay/v1/lib/react-relay-classic.d.ts b/types/react-relay/v1/lib/react-relay-classic.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/types/react-relay/v1/lib/react-relay-compat.d.ts b/types/react-relay/v1/lib/react-relay-compat.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/types/react-relay/v1/lib/react-relay-modern.d.ts b/types/react-relay/v1/lib/react-relay-modern.d.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/types/react-relay/v1/react-relay-tests.tsx b/types/react-relay/v1/react-relay-tests.tsx new file mode 100644 index 0000000000..2b8e371ee2 --- /dev/null +++ b/types/react-relay/v1/react-relay-tests.tsx @@ -0,0 +1,437 @@ +import * as React from "react"; +import { + Environment, + Network, + RecordSource, + Store, + ConnectionHandler, +} from 'relay-runtime'; + +//////////////////////////// +// RELAY MODERN TESTS +/////////////////////////// +import { + graphql, + commitMutation, + createFragmentContainer, + createPaginationContainer, + createRefetchContainer, + requestSubscription, + QueryRenderer, + RelayPaginationProp, + RelayRefetchProp +} from "react-relay"; + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern Environment +// ~~~~~~~~~~~~~~~~~~~~~ +function fetchQuery(operation: any, variables: any, cacheConfig: {}) { + return fetch('/graphql'); +} +const network = Network.create(fetchQuery); +const source = new RecordSource(); +const store = new Store(source); +const modernEnvironment = new Environment({ network, store }); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern QueryRenderer +// ~~~~~~~~~~~~~~~~~~~~~ +const MyQueryRenderer = (props: { name: string}) => ( + { + if (error) { + return
{error.message}
; + } else if (props) { + return
{props.name} is great!
; + } + return
Loading
; + }} + /> +); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern FragmentContainer +// ~~~~~~~~~~~~~~~~~~~~~ +const MyFragmentContainer = createFragmentContainer( + class TodoListView extends React.Component { + render() { + return
; + } + }, + { + item: graphql` + fragment TodoItem_item on Todo { + text + isComplete + } + `, + } +); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern RefetchContainer +// ~~~~~~~~~~~~~~~~~~~~~ +interface StoryInterface { id: string; } +interface FeedStoriesProps { + relay: RelayRefetchProp; + feed: { + stories: { edges: Array<{ node: StoryInterface }> } + }; +} +class Story extends React.Component<{ story: StoryInterface }> {} +class FeedStories extends React.Component { + render() { + return ( +
+ {this.props.feed.stories.edges.map( + edge => + )} +
+ ); + } + + _loadMore() { + // Increments the number of stories being rendered by 10. + const refetchVariables = (fragmentVariables: {count: number }) => ({ + count: fragmentVariables.count + 10, + }); + this.props.relay.refetch(refetchVariables); + } + } + +const FeedRefetchContainer = createRefetchContainer( + FeedStories, + { + feed: graphql.experimental` + fragment FeedStories_feed on Feed + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + ) { + stories(first: $count) { + edges { + node { + id + ...Story_story + } + } + } + } + ` + }, + graphql.experimental` + query FeedStoriesRefetchQuery($count: Int) { + feed { + ...FeedStories_feed @arguments(count: $count) + } + } + `, + ); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern PaginationContainer +// ~~~~~~~~~~~~~~~~~~~~~ +interface FeedProps { + user: { feed: { edges: Array<{ node: StoryInterface}>}}; + relay: RelayPaginationProp; +} +class Feed extends React.Component { + render() { + return (
+ {this.props.user.feed.edges.map( + edge => + )} +
); + } + + _loadMore() { + if (!this.props.relay.hasMore() || this.props.relay.isLoading()) { + return; + } + + this.props.relay.loadMore( + 10, // Fetch the next 10 feed items + e => { + console.log(e); + }, + ); + } +} + +const FeedPaginationContainer = createPaginationContainer( + Feed, + { + user: graphql` + fragment Feed_user on User { + feed( + first: $count + after: $cursor + orderby: $orderBy # other variables + ) @connection(key: "Feed_feed") { + edges { + node { + id + ...Story_story + } + } + } + } + `, + }, + { + direction: 'forward', + getConnectionFromProps(props: { user: {feed: any}}) { + return props.user && props.user.feed; + }, + getFragmentVariables(prevVars, totalCount) { + return { + ...prevVars, + count: totalCount, + }; + }, + getVariables(props, {count, cursor}, fragmentVariables) { + return { + count, + cursor, + // in most cases, for variables other than connection filters like + // `first`, `after`, etc. you may want to use the previous values. + orderBy: fragmentVariables.orderBy, + }; + }, + query: graphql` + query FeedPaginationQuery( + $count: Int! + $cursor: String + $orderby: String! + ) { + user { + # You could reference the fragment defined previously. + ...Feed_user + } + } + ` + } +); + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern Mutations +// ~~~~~~~~~~~~~~~~~~~~~ +const mutation = graphql` +mutation MarkReadNotificationMutation( + $input: MarkReadNotificationData! +) { + markReadNotification(data: $input) { + notification { + seenState + } + } +} +`; + +const optimisticResponse = { + markReadNotification: { + notification: { + seenState: 'SEEN', + }, + }, +}; + +const configs = [{ + type: 'NODE_DELETE' as 'NODE_DELETE', + deletedIDFieldName: 'destroyedShipId', +}, { + type: 'RANGE_ADD' as 'RANGE_ADD', + parentID: 'shipId', + connectionInfo: [{ + key: 'AddShip_ships', + rangeBehavior: 'append', + }], + edgeName: 'newShipEdge', +}, { + type: 'RANGE_DELETE' as 'RANGE_DELETE', + parentID: 'todoId', + connectionKeys: [{ + key: 'RemoveTags_tags', + rangeBehavior: 'append', + }], + pathToConnection: ['todo', 'tags'], + deletedIDFieldName: 'removedTagId' +}]; + +function markNotificationAsRead(source: string, storyID: string) { + const variables = { + input: { + source, + storyID, + }, + }; + + commitMutation( + modernEnvironment, + { + configs, + mutation, + optimisticResponse, + variables, + onCompleted: (response, errors) => { + console.log('Response received from server.'); + }, + onError: err => console.error(err), + }, + ); +} + +// ~~~~~~~~~~~~~~~~~~~~~ +// Modern Subscriptions +// ~~~~~~~~~~~~~~~~~~~~~ +const subscription = graphql` + subscription MarkReadNotificationSubscription( + $storyID: ID! + ) { + markReadNotification(storyID: $storyID) { + notification { + seenState + } + } + } +`; +const variables = { + storyID: '123', +}; +requestSubscription( + modernEnvironment, // see Environment docs + { + subscription, + variables, + // optional but recommended: + onCompleted: () => {}, + onError: error => console.error(error), + // example of a custom updater + updater: store => { + // Get the notification + const rootField = store.getRootField('markReadNotification'); + const notification = !!rootField && rootField.getLinkedRecord('notification'); + // Add it to a connection + const viewer = store.getRoot().getLinkedRecord('viewer'); + const notifications = + ConnectionHandler.getConnection(viewer, 'notifications'); + const edge = ConnectionHandler.createEdge( + store, + notifications, + notification, + '', + ); + ConnectionHandler.insertEdgeAfter(notifications, edge); + }, + } +); + +//////////////////////////// +// RELAY-CLASSIC TESTS +/////////////////////////// + +import * as Relay from "react-relay/classic"; + +interface Props { + text: string; + userId: string; +} + +export default class AddTweetMutation extends Relay.Mutation { + getMutation() { + return Relay.QL`mutation{addTweet}`; + } + + getFatQuery() { + return Relay.QL` + fragment on AddTweetPayload { + tweetEdge + user + } + `; + } + + getConfigs() { + return [{ + type: "RANGE_ADD", + parentName: "user", + parentID: this.props.userId, + connectionName: "tweets", + edgeName: "tweetEdge", + rangeBehaviors: { + "": "append", + }, + }]; + } + + getVariables() { + return this.props; + } +} + +interface ArtworkProps { + artwork: { + title: string + }; + relay: Relay.RelayProp; +} + +class Artwork extends React.Component { + render() { + return ( + + {this.props.artwork.title} + + ); + } +} + +const ArtworkContainer = Relay.createContainer(Artwork, { + fragments: { + artwork: () => Relay.QL` + fragment on Artwork { + title + } + ` + } +}); + +class StubbedArtwork extends React.Component { + render() { + const props = { + artwork: { title: "CHAMPAGNE FORMICA FLAG" }, + relay: { + route: { + name: "champagne" + }, + variables: { + artworkID: "champagne-formica-flag", + }, + setVariables: () => {}, + forceFetch: () => {}, + hasOptimisticUpdate: () => false, + getPendingTransactions: (): any => undefined, + commitUpdate: () => {}, + } + }; + return ; + } +} diff --git a/types/react-relay/v1/tsconfig.json b/types/react-relay/v1/tsconfig.json new file mode 100644 index 0000000000..e6b06bf446 --- /dev/null +++ b/types/react-relay/v1/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "paths": { + "react-relay": ["react-relay/v1"], + "react-relay/*": ["react-relay/v2/*"] + } + }, + "files": [ + "index.d.ts", + "react-relay-tests.tsx", + "lib/react-relay-classic.d.ts", + "lib/react-relay-modern.d.ts", + "lib/react-relay-compat.d.ts" + ] +} diff --git a/types/react-relay/tslint.json b/types/react-relay/v1/tslint.json similarity index 100% rename from types/react-relay/tslint.json rename to types/react-relay/v1/tslint.json diff --git a/types/relay-runtime/index.d.ts b/types/relay-runtime/index.d.ts index 2bd9938b0c..152df90640 100644 --- a/types/relay-runtime/index.d.ts +++ b/types/relay-runtime/index.d.ts @@ -4,1129 +4,1127 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.4 -// note: __Relay namespace is used so react-relay can access and inter-op -declare namespace __Relay { - namespace Common { - /** - * SOURCE: - * Relay 1.3.0 - * https://github.com/facebook/relay/blob/b85a1d69bb72be4ace67179f55c2a54a8d761c8b/packages/react-relay/classic/environment/RelayCombinedEnvironmentTypes.js - */ - // ~~~~~~~~~~~~~~~~~~~~~ - // Maybe Fix - // ~~~~~~~~~~~~~~~~~~~~~ - type RelayConcreteNode = any; - type RelayMutationTransaction = any; - type RelayMutationRequest = any; - type RelayQueryRequest = any; - type ConcreteFragment = any; - type ConcreteBatch = any; - type ConcreteFragmentDefinition = object; - type ConcreteOperationDefinition = object; +// note: namespace is used so react-relay can access and inter-op +export namespace RelayCommonTypes { + /** + * SOURCE: + * Relay 1.3.0 + * https://github.com/facebook/relay/blob/b85a1d69bb72be4ace67179f55c2a54a8d761c8b/packages/react-relay/classic/environment/RelayCombinedEnvironmentTypes.js + */ + // ~~~~~~~~~~~~~~~~~~~~~ + // Maybe Fix + // ~~~~~~~~~~~~~~~~~~~~~ + type RelayConcreteNode = any; + type RelayMutationTransaction = any; + type RelayMutationRequest = any; + type RelayQueryRequest = any; + type ConcreteFragment = any; + type ConcreteBatch = any; + type ConcreteFragmentDefinition = object; + type ConcreteOperationDefinition = object; - /** - * FIXME: RelayContainer used to be typed with ReactClass, but - * ReactClass is broken and allows for access to any property. For example - * ReactClass.getFragment('foo') is valid even though ReactClass has no - * such getFragment() type definition. When ReactClass is fixed this causes a - * lot of errors in Relay code since methods like getFragment() are used often - * but have no definition in Relay's types. Suppressing for now. - */ - type RelayContainer = any; + /** + * FIXME: RelayContainer used to be typed with ReactClass, but + * ReactClass is broken and allows for access to any property. For example + * ReactClass.getFragment('foo') is valid even though ReactClass has no + * such getFragment() type definition. When ReactClass is fixed this causes a + * lot of errors in Relay code since methods like getFragment() are used often + * but have no definition in Relay's types. Suppressing for now. + */ + type RelayContainer = any; - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayQL - // ~~~~~~~~~~~~~~~~~~~~~ - type RelayQL = ( - strings: string[], - ...substitutions: any[] - ) => RelayConcreteNode; + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayQL + // ~~~~~~~~~~~~~~~~~~~~~ + type RelayQL = ( + strings: string[], + ...substitutions: any[] + ) => RelayConcreteNode; - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayModernGraphQLTag - // ~~~~~~~~~~~~~~~~~~~~~ - interface GeneratedNodeMap { - [key: string]: GraphQLTaggedNode; - } - type GraphQLTaggedNode = - (() => ConcreteFragment | ConcreteBatch) | - { - modern(): ConcreteFragment | ConcreteBatch, - classic(relayQL: RelayQL): - | ConcreteFragmentDefinition - | ConcreteOperationDefinition, - }; - // ~~~~~~~~~~~~~~~~~~~~~ - // General Usage - // ~~~~~~~~~~~~~~~~~~~~~ - type DataID = string; - interface Variables { - [name: string]: any; - } - type Uploadable = File | Blob; - interface UploadableMap { - [key: string]: Uploadable; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayNetworkTypes - // Version: Relay 1.3.0 - // File: https://github.com/facebook/relay/blob/master/packages/relay-runtime/network/RelayNetworkTypes.js - // ~~~~~~~~~~~~~~~~~~~~~ - - interface LegacyObserver { - onCompleted?(): void; - onError?(error: Error): void; - onNext?(data: T): void; - } - interface PayloadError { - message: string; - locations?: Array<{ - line: number, - column: number, - }>; - } - /** - * A function that executes a GraphQL operation with request/response semantics. - * - * May return an Observable or Promise of a raw server response. - */ - function FetchFunction( - operation: ConcreteBatch, - variables: Variables, - cacheConfig: CacheConfig, - uploadables?: UploadableMap, - ): Runtime.ObservableFromValue; - - /** - * A function that executes a GraphQL subscription operation, returning one or - * more raw server responses over time. - * - * May return an Observable, otherwise must call the callbacks found in the - * fourth parameter. - */ - type SubscribeFunction = ( - operation: ConcreteBatch, - variables: Variables, - cacheConfig: CacheConfig, - observer: LegacyObserver, - ) => Runtime.RelayObservable | Disposable; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayStoreTypes - // Version: Relay 1.3.0 - // File: https://github.com/facebook/relay/blob/master/packages/relay-runtime/store/RelayStoreTypes.js - // ~~~~~~~~~~~~~~~~~~~~~ - /** - * A function that receives a proxy over the store and may trigger side-effects - * (indirectly) by calling `set*` methods on the store or its record proxies. - */ - type StoreUpdater = (store: RecordSourceProxy) => void; - - /** - * Similar to StoreUpdater, but accepts a proxy tied to a specific selector in - * order to easily access the root fields of a query/mutation as well as a - * second argument of the response object of the mutation. - */ - type SelectorStoreUpdater = ( - store: RecordSourceSelectorProxy, - // Actually RelayCombinedEnvironmentTypes#SelectorData, but mixed is - // inconvenient to access deeply in product code. - data: any, // FLOW FIXME - ) => void; - - /** - * Extends the RecordSourceProxy interface with methods for accessing the root - * fields of a Selector. - */ - interface RecordSourceSelectorProxy { - create(dataID: DataID, typeName: string): RecordProxy; - delete(dataID: DataID): void; - get(dataID: DataID): RecordProxy | void; - getRoot(): RecordProxy; - getRootField(fieldName: string): RecordProxy | void; - getPluralRootField(fieldName: string): RecordProxy[] | void; - } - - interface RecordProxy { - copyFieldsFrom(source: RecordProxy): void; - getDataID(): DataID; - getLinkedRecord(name: string, args?: Variables): RecordProxy | void; - getLinkedRecords(name: string, args?: Variables): Array | void; - getOrCreateLinkedRecord( - name: string, - typeName: string, - args?: Variables, - ): RecordProxy; - getType(): string; - getValue(name: string, args?: Variables): any; - setLinkedRecord( - record: RecordProxy, - name: string, - args?: Variables, - ): RecordProxy; - setLinkedRecords( - records: Array | undefined | null, - name: string, - args?: Variables, - ): RecordProxy; - setValue(value: any, name: string, args?: Variables): RecordProxy; - } - - interface RecordSourceProxy { - create(dataID: DataID, typeName: string): RecordProxy; - delete(dataID: DataID): void; - get(dataID: DataID): Array | void; - getRoot(): RecordProxy; - } - - interface HandleFieldPayload { - // The arguments that were fetched. - args: Variables; - // The __id of the record containing the source/handle field. - dataID: DataID; - // The (storage) key at which the original server data was written. - fieldKey: string; - // The name of the handle - handle: string; - // The (storage) key at which the handle's data should be written by the - // handler - handleKey: string; - } - interface HandlerInterface { - update(store: RecordSourceProxy, fieldPayload: HandleFieldPayload): void; - [functionName: string]: (...args: any[]) => any; - } - const Handler: HandlerInterface; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayCombinedEnvironmentTypes - // Version: Relay 1.3.0 - // File: https://github.com/facebook/relay/blob/b85a1d69bb72be4ace67179f55c2a54a8d761c8b/packages/react-relay/classic/environment/RelayCombinedEnvironmentTypes.js - // ~~~~~~~~~~~~~~~~~~~~~ - /** - * Settings for how a query response may be cached. - * - * - `force`: causes a query to be issued unconditionally, irrespective of the - * state of any configured response cache. - * - `poll`: causes a query to live update by polling at the specified interval - * in milliseconds. (This value will be passed to setTimeout.) - */ - interface CacheConfig { - force?: boolean; - poll?: number; - } - - /** - * Represents any resource that must be explicitly disposed of. The most common - * use-case is as a return value for subscriptions, where calling `dispose()` - * would cancel the subscription. - */ - interface Disposable { - dispose(): void; - } - - /** - * Arbitrary data e.g. received by a container as props. - */ - interface Props { [key: string]: any; } - - /* - * An individual cached graph object. - */ - interface Record { [key: string]: any; } - - /** - * A collection of records keyed by id. - */ - interface RecordMap { [dataID: string]: Record | null | undefined; } - - /** - * A selector defines the starting point for a traversal into the graph for the - * purposes of targeting a subgraph. - */ - interface CSelector { - dataID: DataID; - node: TNode; - variables: Variables; - } - - /** - * A representation of a selector and its results at a particular point in time. - */ - type CSnapshot = CSelector & { - data: SelectorData | null | undefined, - seenRecords: RecordMap, + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayModernGraphQLTag + // ~~~~~~~~~~~~~~~~~~~~~ + interface GeneratedNodeMap { + [key: string]: GraphQLTaggedNode; + } + type GraphQLTaggedNode = + (() => ConcreteFragment | ConcreteBatch) | + { + modern(): ConcreteFragment | ConcreteBatch, + classic(relayQL: RelayQL): + | ConcreteFragmentDefinition + | ConcreteOperationDefinition, }; + // ~~~~~~~~~~~~~~~~~~~~~ + // General Usage + // ~~~~~~~~~~~~~~~~~~~~~ + type DataID = string; + interface Variables { + [name: string]: any; + } + type Uploadable = File | Blob; + interface UploadableMap { + [key: string]: Uploadable; + } + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayNetworkTypes + // Version: Relay 1.3.0 + // File: https://github.com/facebook/relay/blob/master/packages/relay-runtime/network/RelayNetworkTypes.js + // ~~~~~~~~~~~~~~~~~~~~~ + + interface LegacyObserver { + onCompleted?(): void; + onError?(error: Error): void; + onNext?(data: T): void; + } + interface PayloadError { + message: string; + locations?: Array<{ + line: number, + column: number, + }>; + } + /** + * A function that executes a GraphQL operation with request/response semantics. + * + * May return an Observable or Promise of a raw server response. + */ + function FetchFunction( + operation: ConcreteBatch, + variables: Variables, + cacheConfig: CacheConfig, + uploadables?: UploadableMap, + ): Runtime.ObservableFromValue; + + /** + * A function that executes a GraphQL subscription operation, returning one or + * more raw server responses over time. + * + * May return an Observable, otherwise must call the callbacks found in the + * fourth parameter. + */ + type SubscribeFunction = ( + operation: ConcreteBatch, + variables: Variables, + cacheConfig: CacheConfig, + observer: LegacyObserver, + ) => Runtime.RelayObservable | Disposable; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayStoreTypes + // Version: Relay 1.3.0 + // File: https://github.com/facebook/relay/blob/master/packages/relay-runtime/store/RelayStoreTypes.js + // ~~~~~~~~~~~~~~~~~~~~~ + /** + * A function that receives a proxy over the store and may trigger side-effects + * (indirectly) by calling `set*` methods on the store or its record proxies. + */ + type StoreUpdater = (store: RecordSourceProxy) => void; + + /** + * Similar to StoreUpdater, but accepts a proxy tied to a specific selector in + * order to easily access the root fields of a query/mutation as well as a + * second argument of the response object of the mutation. + */ + type SelectorStoreUpdater = ( + store: RecordSourceSelectorProxy, + // Actually RelayCombinedEnvironmentTypes#SelectorData, but mixed is + // inconvenient to access deeply in product code. + data: any, // FLOW FIXME + ) => void; + + /** + * Extends the RecordSourceProxy interface with methods for accessing the root + * fields of a Selector. + */ + interface RecordSourceSelectorProxy { + create(dataID: DataID, typeName: string): RecordProxy; + delete(dataID: DataID): void; + get(dataID: DataID): RecordProxy | void; + getRoot(): RecordProxy; + getRootField(fieldName: string): RecordProxy | void; + getPluralRootField(fieldName: string): RecordProxy[] | void; + } + + interface RecordProxy { + copyFieldsFrom(source: RecordProxy): void; + getDataID(): DataID; + getLinkedRecord(name: string, args?: Variables): RecordProxy | void; + getLinkedRecords(name: string, args?: Variables): Array | void; + getOrCreateLinkedRecord( + name: string, + typeName: string, + args?: Variables, + ): RecordProxy; + getType(): string; + getValue(name: string, args?: Variables): any; + setLinkedRecord( + record: RecordProxy, + name: string, + args?: Variables, + ): RecordProxy; + setLinkedRecords( + records: Array | undefined | null, + name: string, + args?: Variables, + ): RecordProxy; + setValue(value: any, name: string, args?: Variables): RecordProxy; + } + + interface RecordSourceProxy { + create(dataID: DataID, typeName: string): RecordProxy; + delete(dataID: DataID): void; + get(dataID: DataID): Array | void; + getRoot(): RecordProxy; + } + + interface HandleFieldPayload { + // The arguments that were fetched. + args: Variables; + // The __id of the record containing the source/handle field. + dataID: DataID; + // The (storage) key at which the original server data was written. + fieldKey: string; + // The name of the handle + handle: string; + // The (storage) key at which the handle's data should be written by the + // handler + handleKey: string; + } + interface HandlerInterface { + update(store: RecordSourceProxy, fieldPayload: HandleFieldPayload): void; + [functionName: string]: (...args: any[]) => any; + } + const Handler: HandlerInterface; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayCombinedEnvironmentTypes + // Version: Relay 1.3.0 + // File: https://github.com/facebook/relay/blob/b85a1d69bb72be4ace67179f55c2a54a8d761c8b/packages/react-relay/classic/environment/RelayCombinedEnvironmentTypes.js + // ~~~~~~~~~~~~~~~~~~~~~ + /** + * Settings for how a query response may be cached. + * + * - `force`: causes a query to be issued unconditionally, irrespective of the + * state of any configured response cache. + * - `poll`: causes a query to live update by polling at the specified interval + * in milliseconds. (This value will be passed to setTimeout.) + */ + interface CacheConfig { + force?: boolean; + poll?: number; + } + + /** + * Represents any resource that must be explicitly disposed of. The most common + * use-case is as a return value for subscriptions, where calling `dispose()` + * would cancel the subscription. + */ + interface Disposable { + dispose(): void; + } + + /** + * Arbitrary data e.g. received by a container as props. + */ + interface Props { [key: string]: any; } + + /* + * An individual cached graph object. + */ + interface Record { [key: string]: any; } + + /** + * A collection of records keyed by id. + */ + interface RecordMap { [dataID: string]: Record | null | undefined; } + + /** + * A selector defines the starting point for a traversal into the graph for the + * purposes of targeting a subgraph. + */ + interface CSelector { + dataID: DataID; + node: TNode; + variables: Variables; + } + + /** + * A representation of a selector and its results at a particular point in time. + */ + type CSnapshot = CSelector & { + data: SelectorData | null | undefined, + seenRecords: RecordMap, + }; + + /** + * The results of a selector given a store/RecordSource. + */ + interface SelectorData { [key: string]: any; } + + /** + * The results of reading the results of a FragmentMap given some input + * `Props`. + */ + interface FragmentSpecResults { [key: string]: any; } + + /** + * A utility for resolving and subscribing to the results of a fragment spec + * (key -> fragment mapping) given some "props" that determine the root ID + * and variables to use when reading each fragment. When props are changed via + * `setProps()`, the resolver will update its results and subscriptions + * accordingly. Internally, the resolver: + * - Converts the fragment map & props map into a map of `Selector`s. + * - Removes any resolvers for any props that became null. + * - Creates resolvers for any props that became non-null. + * - Updates resolvers with the latest props. + */ + interface FragmentSpecResolver { /** - * The results of a selector given a store/RecordSource. + * Stop watching for changes to the results of the fragments. */ - interface SelectorData { [key: string]: any; } + dispose(): void; /** - * The results of reading the results of a FragmentMap given some input - * `Props`. + * Get the current results. */ - interface FragmentSpecResults { [key: string]: any; } + resolve(): FragmentSpecResults; /** - * A utility for resolving and subscribing to the results of a fragment spec - * (key -> fragment mapping) given some "props" that determine the root ID - * and variables to use when reading each fragment. When props are changed via - * `setProps()`, the resolver will update its results and subscriptions - * accordingly. Internally, the resolver: - * - Converts the fragment map & props map into a map of `Selector`s. - * - Removes any resolvers for any props that became null. - * - Creates resolvers for any props that became non-null. - * - Updates resolvers with the latest props. + * Update the resolver with new inputs. Call `resolve()` to get the updated + * results. */ - interface FragmentSpecResolver { - /** - * Stop watching for changes to the results of the fragments. - */ - dispose(): void; - - /** - * Get the current results. - */ - resolve(): FragmentSpecResults; - - /** - * Update the resolver with new inputs. Call `resolve()` to get the updated - * results. - */ - setProps(props: Props): void; - - /** - * Override the variables used to read the results of the fragments. Call - * `resolve()` to get the updated results. - */ - setVariables(variables: Variables): void; - } - - interface CFragmentMap { [key: string]: TFragment; } + setProps(props: Props): void; /** - * An operation selector describes a specific instance of a GraphQL operation - * with variables applied. + * Override the variables used to read the results of the fragments. Call + * `resolve()` to get the updated results. + */ + setVariables(variables: Variables): void; + } + + interface CFragmentMap { [key: string]: TFragment; } + + /** + * An operation selector describes a specific instance of a GraphQL operation + * with variables applied. + * + * - `root`: a selector intended for processing server results or retaining + * response data in the store. + * - `fragment`: a selector intended for use in reading or subscribing to + * the results of the the operation. + */ + interface COperationSelector { + fragment: CSelector; + node: TOperation; + root: CSelector; + variables: Variables; + } + + /** + * The public API of Relay core. Represents an encapsulated environment with its + * own in-memory cache. + */ + interface CEnvironment< + TEnvironment, + TFragment, + TGraphQLTaggedNode, + TNode, + TOperation, + TPayload, + > { + /** + * Read the results of a selector from in-memory records in the store. + */ + lookup(selector: CSelector): CSnapshot; + + /** + * Subscribe to changes to the results of a selector. The callback is called + * when data has been committed to the store that would cause the results of + * the snapshot's selector to change. + */ + subscribe( + snapshot: CSnapshot, + callback: (snapshot: CSnapshot) => void, + ): Disposable; + + /** + * Ensure that all the records necessary to fulfill the given selector are + * retained in-memory. The records will not be eligible for garbage collection + * until the returned reference is disposed. * - * - `root`: a selector intended for processing server results or retaining - * response data in the store. - * - `fragment`: a selector intended for use in reading or subscribing to - * the results of the the operation. + * Note: This is a no-op in the classic core. */ - interface COperationSelector { - fragment: CSelector; - node: TOperation; - root: CSelector; - variables: Variables; - } + retain(selector: CSelector): Disposable; /** - * The public API of Relay core. Represents an encapsulated environment with its - * own in-memory cache. + * Send a query to the server with request/response semantics: the query will + * either complete successfully (calling `onNext` and `onCompleted`) or fail + * (calling `onError`). + * + * Note: Most applications should use `streamQuery` in order to + * optionally receive updated information over time, should that feature be + * supported by the network/server. A good rule of thumb is to use this method + * if you would otherwise immediately dispose the `streamQuery()` + * after receving the first `onNext` result. */ - interface CEnvironment< + sendQuery(config: { + cacheConfig?: CacheConfig, + onCompleted?(): void, + onError?(error: Error): void, + onNext?(payload: TPayload): void, + operation: COperationSelector, + }): Disposable; + + /** + * Send a query to the server with request/subscription semantics: one or more + * responses may be returned (via `onNext`) over time followed by either + * the request completing (`onCompleted`) or an error (`onError`). + * + * Networks/servers that support subscriptions may choose to hold the + * subscription open indefinitely such that `onCompleted` is not called. + */ + streamQuery(config: { + cacheConfig?: CacheConfig, + onCompleted?(): void, + onError?(error: Error): void, + onNext?(payload: TPayload): void, + operation: COperationSelector, + }): Disposable; + + unstable_internal: CUnstableEnvironmentCore< TEnvironment, TFragment, TGraphQLTaggedNode, TNode, - TOperation, - TPayload, - > { - /** - * Read the results of a selector from in-memory records in the store. - */ - lookup(selector: CSelector): CSnapshot; - - /** - * Subscribe to changes to the results of a selector. The callback is called - * when data has been committed to the store that would cause the results of - * the snapshot's selector to change. - */ - subscribe( - snapshot: CSnapshot, - callback: (snapshot: CSnapshot) => void, - ): Disposable; - - /** - * Ensure that all the records necessary to fulfill the given selector are - * retained in-memory. The records will not be eligible for garbage collection - * until the returned reference is disposed. - * - * Note: This is a no-op in the classic core. - */ - retain(selector: CSelector): Disposable; - - /** - * Send a query to the server with request/response semantics: the query will - * either complete successfully (calling `onNext` and `onCompleted`) or fail - * (calling `onError`). - * - * Note: Most applications should use `streamQuery` in order to - * optionally receive updated information over time, should that feature be - * supported by the network/server. A good rule of thumb is to use this method - * if you would otherwise immediately dispose the `streamQuery()` - * after receving the first `onNext` result. - */ - sendQuery(config: { - cacheConfig?: CacheConfig, - onCompleted?(): void, - onError?(error: Error): void, - onNext?(payload: TPayload): void, - operation: COperationSelector, - }): Disposable; - - /** - * Send a query to the server with request/subscription semantics: one or more - * responses may be returned (via `onNext`) over time followed by either - * the request completing (`onCompleted`) or an error (`onError`). - * - * Networks/servers that support subscriptions may choose to hold the - * subscription open indefinitely such that `onCompleted` is not called. - */ - streamQuery(config: { - cacheConfig?: CacheConfig, - onCompleted?(): void, - onError?(error: Error): void, - onNext?(payload: TPayload): void, - operation: COperationSelector, - }): Disposable; - - unstable_internal: CUnstableEnvironmentCore< - TEnvironment, - TFragment, - TGraphQLTaggedNode, - TNode, - TOperation - >; - } - - interface CUnstableEnvironmentCore< - TEnvironment, - TFragment, - TGraphQLTaggedNode, - TNode, - TOperation, - > { - /** - * Create an instance of a FragmentSpecResolver. - * - * TODO: The FragmentSpecResolver *can* be implemented via the other methods - * defined here, so this could be moved out of core. It's convenient to have - * separate implementations until the experimental core is in OSS. - */ - createFragmentSpecResolver( - context: CRelayContext, - containerName: string, - fragments: CFragmentMap, - props: Props, - callback: () => void, - ): FragmentSpecResolver; - - /** - * Creates an instance of an OperationSelector given an operation definition - * (see `getOperation`) and the variables to apply. The input variables are - * filtered to exclude variables that do not matche defined arguments on the - * operation, and default values are populated for null values. - */ - createOperationSelector( - operation: TOperation, - variables: Variables, - ): COperationSelector; - - /** - * Given a graphql`...` tagged template, extract a fragment definition usable - * by this version of Relay core. Throws if the value is not a fragment. - */ - getFragment(node: TGraphQLTaggedNode): TFragment; - - /** - * Given a graphql`...` tagged template, extract an operation definition - * usable by this version of Relay core. Throws if the value is not an - * operation. - */ - getOperation(node: TGraphQLTaggedNode): TOperation; - - /** - * Determine if two selectors are equal (represent the same selection). Note - * that this function returns `false` when the two queries/fragments are - * different objects, even if they select the same fields. - */ - areEqualSelectors(a: CSelector, b: CSelector): boolean; - - /** - * Given the result `item` from a parent that fetched `fragment`, creates a - * selector that can be used to read the results of that fragment for that item. - * - * Example: - * - * Given two fragments as follows: - * - * ``` - * fragment Parent on User { - * id - * ...Child - * } - * fragment Child on User { - * name - * } - * ``` - * - * And given some object `parent` that is the results of `Parent` for id "4", - * the results of `Child` can be accessed by first getting a selector and then - * using that selector to `lookup()` the results against the environment: - * - * ``` - * const childSelector = getSelector(queryVariables, Child, parent); - * const childData = environment.lookup(childSelector).data; - * ``` - */ - getSelector( - operationVariables: Variables, - fragment: TFragment, - prop: any, - ): CSelector | void; - - /** - * Given the result `items` from a parent that fetched `fragment`, creates a - * selector that can be used to read the results of that fragment on those - * items. This is similar to `getSelector` but for "plural" fragments that - * expect an array of results and therefore return an array of selectors. - */ - getSelectorList( - operationVariables: Variables, - fragment: TFragment, - props: any[], - ): Array> | void; - - /** - * Given a mapping of keys -> results and a mapping of keys -> fragments, - * extracts the selectors for those fragments from the results. - * - * The canonical use-case for this function are Relay Containers, which - * use this function to convert (props, fragments) into selectors so that they - * can read the results to pass to the inner component. - */ - getSelectorsFromObject( - operationVariables: Variables, - fragments: CFragmentMap, - props: Props, - ): { [key: string]: CSelector | Array> | null | undefined }; - - /** - * Given a mapping of keys -> results and a mapping of keys -> fragments, - * extracts a mapping of keys -> id(s) of the results. - * - * Similar to `getSelectorsFromObject()`, this function can be useful in - * determining the "identity" of the props passed to a component. - */ - getDataIDsFromObject( - fragments: CFragmentMap, - props: Props, - ): { [key: string]: DataID | DataID[] | null | undefined }; - - /** - * Given a mapping of keys -> results and a mapping of keys -> fragments, - * extracts the merged variables that would be in scope for those - * fragments/results. - * - * This can be useful in determing what varaibles were used to fetch the data - * for a Relay container, for example. - */ - getVariablesFromObject( - operationVariables: Variables, - fragments: CFragmentMap, - props: Props, - ): Variables; - } - - /** - * The type of the `relay` property set on React context by the React/Relay - * integration layer (e.g. QueryRenderer, FragmentContainer, etc). - */ - interface CRelayContext { - environment: TEnvironment; - variables: Variables; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayTypes - /** - * Version: Relay 1.3.0 - * File: - * https://github.com/facebook/relay/blob/fa9f48ea209ee2402d433b59a84d1cbc046574e2/packages/react-relay/classic/tools/RelayTypes.js - */ - // ~~~~~~~~~~~~~~~~~~~~~ - interface RerunParam { - param: string; - import: string; - max_runs: number; - } - interface FIELDS_CHANGE { - type: 'FIELDS_CHANGE'; - fieldIDs: { [fieldName: string]: DataID | DataID[]; }; - } - interface RANGE_ADD { - type: 'RANGE_ADD'; - parentName?: string; - parentID?: string; - connectionInfo?: Array<{ - key: string, - filters?: Variables, - rangeBehavior: string, - }>; - connectionName?: string; - edgeName: string; - rangeBehaviors?: RangeBehaviors; - } - interface NODE_DELETE { - type: 'NODE_DELETE'; - parentName?: string; - parentID?: string; - connectionName?: string; - deletedIDFieldName: string; - } - interface RANGE_DELETE { - type: 'RANGE_DELETE'; - parentName?: string; - parentID?: string; - connectionKeys?: Array<{ - key: string, - filters?: Variables, - }>; - connectionName?: string; - deletedIDFieldName: string | string[]; - pathToConnection: string[]; - } - interface REQUIRED_CHILDREN { - type: 'REQUIRED_CHILDREN'; - children: RelayConcreteNode[]; - } - type RelayMutationConfig = - FIELDS_CHANGE | - RANGE_ADD | - NODE_DELETE | - RANGE_DELETE | - REQUIRED_CHILDREN; - - interface RelayMutationTransactionCommitCallbacks { - onFailure?: RelayMutationTransactionCommitFailureCallback; - onSuccess?: RelayMutationTransactionCommitSuccessCallback; - } - type RelayMutationTransactionCommitFailureCallback = ( - transaction: RelayMutationTransaction, - preventAutoRollback: () => void, - ) => void; - type RelayMutationTransactionCommitSuccessCallback = (response: { - [key: string]: any, - }) => void; - interface NetworkLayer { - sendMutation(request: RelayMutationRequest): Promise | void; - sendQueries(requests: RelayQueryRequest[]): Promise | void; - supports(...options: string[]): boolean; - } - interface QueryResult { - error?: Error; - ref_params?: { [name: string]: any }; - response: QueryPayload; - } - interface ReadyState { - aborted: boolean; - done: boolean; - error: Error | null; - events: ReadyStateEvent[]; - ready: boolean; - stale: boolean; - } - type RelayContainerErrorEventType = - | 'CACHE_RESTORE_FAILED' - | 'NETWORK_QUERY_ERROR'; - type RelayContainerLoadingEventType = - | 'ABORT' - | 'CACHE_RESTORED_REQUIRED' - | 'CACHE_RESTORE_START' - | 'NETWORK_QUERY_RECEIVED_ALL' - | 'NETWORK_QUERY_RECEIVED_REQUIRED' - | 'NETWORK_QUERY_START' - | 'STORE_FOUND_ALL' - | 'STORE_FOUND_REQUIRED'; - type ReadyStateChangeCallback = (readyState: ReadyState) => void; - interface ReadyStateEvent { - type: RelayContainerLoadingEventType | RelayContainerErrorEventType; - error?: Error; - } - interface Abortable { - abort(): void; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayInternalTypes - /** - * Version: Relay 1.3.0 - * File: - * https://github.com/facebook/relay/blob/master/packages/react-relay/classic/tools/RelayInternalTypes.js - */ - // ~~~~~~~~~~~~~~~~~~~~~ - interface QueryPayload { [key: string]: any; } - interface RelayQuerySet { [queryName: string]: any; } - type RangeBehaviorsFunction = (connectionArgs: { - [argName: string]: any, - }) => 'APPEND' | 'IGNORE' | 'PREPEND' | 'REFETCH' | 'REMOVE'; - interface RangeBehaviorsObject { - [key: string]: 'APPEND' | 'IGNORE' | 'PREPEND' | 'REFETCH' | 'REMOVE'; - } - type RangeBehaviors = RangeBehaviorsFunction | RangeBehaviorsObject; + TOperation + >; } - namespace Runtime { - // ~~~~~~~~~~~~~~~~~~~~~ - // Maybe Fix - // ~~~~~~~~~~~~~~~~~~~~~ - type RelayDebugger = any; - type OptimisticUpdate = any; - type OperationSelector = Common.COperationSelector; - type Selector = Common.CSelector; - type PayloadData = any; - type Snapshot = Common.CSnapshot; - type RelayResponsePayload = any; - type MutableRecordSource = RecordSource; - + interface CUnstableEnvironmentCore< + TEnvironment, + TFragment, + TGraphQLTaggedNode, + TNode, + TOperation, + > { /** - * A function that returns an Observable representing the response of executing - * a GraphQL operation. - */ - type ExecuteFunction = ( - operation: object, - variables: Common.Variables, - cacheConfig: Common.CacheConfig, - uploadables?: Common.UploadableMap, - ) => Promise; - interface RelayNetwork { - execute: ExecuteFunction; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayDefaultHandlerProvider - // ~~~~~~~~~~~~~~~~~~~~~ - function HandlerProvider(name: string): typeof Common.Handler | void; - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayModernEnvironment - // ~~~~~~~~~~~~~~~~~~~~~ - interface EnvironmentConfig { - configName?: string; - handlerProvider?: typeof HandlerProvider; - network: Network; - store: Store; - } - class Environment { - constructor(config: EnvironmentConfig); - getStore(): Store; - getDebugger(): RelayDebugger; - applyUpdate(optimisticUpdate: OptimisticUpdate): Common.Disposable; - revertUpdate(update: OptimisticUpdate): void; - replaceUpdate(update: OptimisticUpdate, newUpdate: OptimisticUpdate): void; - applyMutation(config: { - operation: OperationSelector, - optimisticUpdater?: Common.SelectorStoreUpdater, - optimisticResponse?: object, - }): Common.Disposable; - check(readSelector: Selector): boolean; - commitPayload( - operationSelector: OperationSelector, - payload: PayloadData, - ): void; - commitUpdate(updater: Common.StoreUpdater): void; - lookup(readSelector: Selector): Snapshot; - subscribe( - snapshot: Snapshot, - callback: (snapshot: Snapshot) => void, - ): Common.Disposable; - retain(selector: Selector): Common.Disposable; - execute(config: { - operation: OperationSelector, - cacheConfig?: Common.CacheConfig, - updater?: Common.SelectorStoreUpdater, - }): RelayObservable; - executeMutation(config: { - operation: OperationSelector, - optimisticUpdater?: Common.SelectorStoreUpdater, - optimisticResponse?: object, - updater?: Common.SelectorStoreUpdater, - uploadables?: Common.UploadableMap, - }): RelayObservable; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayInMemoryRecordSource - // ~~~~~~~~~~~~~~~~~~~~~ - interface Record { [key: string]: any; } - interface RecordMap { [dataID: string]: Record | null | undefined; } - - // ~~~~~~~~~~~~~~~~~~~~~ - // Network - // ~~~~~~~~~~~~~~~~~~~~~ - class Network { - /** - * Creates an implementation of the `Network` interface defined in - * `RelayNetworkTypes` given `fetch` and `subscribe` functions. - */ - static create(fetchFn: typeof Common.FetchFunction, subscribeFn?: Common.SubscribeFunction): RelayNetwork; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // Network - // ~~~~~~~~~~~~~~~~~~~~~ - class RecordSource { - constructor(records?: RecordMap); - clear(): void; - delete(dataID: Common.DataID): void; - get(dataID: Common.DataID): Record | void; - getRecordIDs(): Common.DataID[]; - getStatus(dataID: Common.DataID): 'EXISTENT' | 'NONEXISTENT' | 'UNKNOWN'; - has(dataID: Common.DataID): boolean; - load( - dataID: Common.DataID, - callback: (error: Error | null, record: Record | null) => void, - ): void; - remove(dataID: Common.DataID): void; - set(dataID: Common.DataID, record: Record): void; - size(): number; - toJSON(): RecordMap; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // ModernStore - // ~~~~~~~~~~~~~~~~~~~~~ - class Store { - constructor(source: RecordSource); - getSource(): MutableRecordSource; - check(selector: Selector): boolean; - retain(selector: Selector): Common.Disposable; - lookup(selector: Selector): Snapshot; - notify(): void; - publish(source: RecordSource): void; - subscribe( - snapshot: Snapshot, - callback: (snapshot: Snapshot) => void, - ): Common.Disposable; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayRecordSourceInspector - // ~~~~~~~~~~~~~~~~~~~~~ - /** - * An internal class to provide a console-friendly string representation of a - * Record. - */ - class RecordSummary { - id: Common.DataID; - type: string | null | undefined; - static createFromRecord(id: Common.DataID, record: any): RecordSummary; - constructor(id: Common.DataID, type: string | null | undefined); - toString(): string; - } - /** - * Internal class for inspecting a single Record. - */ - class RecordInspector { - constructor(sourceInspector: RelayRecordSourceInspector, record: Record); - /** - * Get the cache id of the given record. For types that implement the `Node` - * interface (or that have an `id`) this will be `id`, for other types it will be - * a synthesized identifier based on the field path from the nearest ancestor - * record that does have an `id`. - */ - getDataID(): Common.DataID; - - /** - * Returns a list of the fields that have been fetched on the current record. - */ - getFields(): string[]; - - /** - * Returns the type of the record. - */ - getType(): string; - - /** - * Returns a copy of the internal representation of the record. - */ - inspect(): any; - - /** - * Returns the value of a scalar field. May throw if the given field is - * present but not actually scalar. - */ - getValue(name: string, args?: Common.Variables): any; - - /** - * Returns an inspector for the given scalar "linked" field (a field whose - * value is another Record instead of a scalar). May throw if the field is - * present but not a scalar linked record. - */ - getLinkedRecord(name: string, args?: Common.Variables): RecordInspector | void; - - /** - * Returns an array of inspectors for the given plural "linked" field (a field - * whose value is an array of Records instead of a scalar). May throw if the - * field is present but not a plural linked record. - */ - getLinkedRecords(name: string, args?: Common.Variables): RecordInspector[] | void; - } - - class RelayRecordSourceInspector { - constructor(source: RecordSource); - static getForEnvironment(environment: Environment): RelayRecordSourceInspector; - /** - * Returns an inspector for the record with the given id, or null/undefined if - * that record is deleted/unfetched. - */ - get(dataID: Common.DataID): RecordInspector | void; - /** - * Returns a list of ": " for each record in the store that has an - * `id`. - */ - getNodes(): RecordSummary[]; - /** - * Returns a list of ": " for all records in the store including - * those that do not have an `id`. - */ - getRecords(): RecordSummary[]; - - /** - * Returns an inspector for the synthesized "root" object, allowing access to - * e.g. the `viewer` object or the results of other fields on the "Query" - * type. - */ - getRoot(): RecordInspector; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // RelayObservable - // ~~~~~~~~~~~~~~~~~~~~~ - interface Subscription { - unsubscribe(): void; - readonly closed: boolean; - } - interface Observer { - start?(subscription: Subscription): any; - next?(nextThing: T): any; - error?(error: Error): any; - complete?(): any; - unsubscribe?(subscription: Subscription): any; - } - type Source = () => any; - interface Subscribable { - subscribe(observer: Observer): Subscription; - } - type ObservableFromValue = RelayObservable | Promise | T; - class RelayObservable implements Subscribable { - _source: Source; - - constructor(source: Source); - - /** - * When an unhandled error is detected, it is reported to the host environment - * (the ESObservable spec refers to this method as "HostReportErrors()"). - * - * The default implementation in development builds re-throws errors in a - * separate frame, and from production builds does nothing (swallowing - * uncaught errors). - * - * Called during application initialization, this method allows - * application-specific handling of uncaught errors. Allowing, for example, - * integration with error logging or developer tools. - */ - static onUnhandledError(callback: (error: Error) => any): void; - - /** - * Accepts various kinds of data sources, and always returns a RelayObservable - * useful for accepting the result of a user-provided FetchFunction. - */ - static from(obj: ObservableFromValue): RelayObservable; - - /** - * Creates a RelayObservable, given a function which expects a legacy - * Relay Observer as the last argument and which returns a Disposable. - * - * To support migration to Observable, the function may ignore the - * legacy Relay observer and directly return an Observable instead. - */ - static fromLegacy( - callback: (legacyObserver: Common.LegacyObserver) => Common.Disposable | RelayObservable, - ): RelayObservable; - - /** - * Returns a new Observable which returns the same values as this one, but - * modified so that the provided Observer is called to perform a side-effects - * for all events emitted by the source. - * - * Any errors that are thrown in the side-effect Observer are unhandled, and - * do not affect the source Observable or its Observer. - * - * This is useful for when debugging your Observables or performing other - * side-effects such as logging or performance monitoring. - */ - do(observer: Observer): RelayObservable; - - /** - * Returns a new Observable which returns the same values as this one, but - * modified so that the finally callback is performed after completion, - * whether normal or due to error or unsubscription. - * - * This is useful for cleanup such as resource finalization. - */ - finally(fn: () => any): RelayObservable; - - /** - * Returns a new Observable which is identical to this one, unless this - * Observable completes before yielding any values, in which case the new - * Observable will yield the values from the alternate Observable. - * - * If this Observable does yield values, the alternate is never subscribed to. - * - * This is useful for scenarios where values may come from multiple sources - * which should be tried in order, i.e. from a cache before a network. - */ - ifEmpty(alternate: RelayObservable): RelayObservable; - - /** - * Observable's primary API: returns an unsubscribable Subscription to the - * source of this Observable. - */ - subscribe(observer: Observer): Subscription; - - /** - * Supports subscription of a legacy Relay Observer, returning a Disposable. - */ - subscribeLegacy(legacyObserver: Common.LegacyObserver): Common.Disposable; - - /** - * Returns a new Observerable where each value has been transformed by - * the mapping function. - */ - map(fn: (thing: T) => U): RelayObservable; - - /** - * Returns a new Observable where each value is replaced with a new Observable - * by the mapping function, the results of which returned as a single - * concattenated Observable. - */ - concatMap(fn: (thing: T) => ObservableFromValue): RelayObservable; - - /** - * Returns a new Observable which first mirrors this Observable, then when it - * completes, waits for `pollInterval` milliseconds before re-subscribing to - * this Observable again, looping in this manner until unsubscribed. - * - * The returned Observable never completes. - */ - poll(pollInterval: number): RelayObservable; - - /** - * Returns a Promise which resolves when this Observable yields a first value - * or when it completes with no value. - */ - toPromise(): Promise; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // commitLocalUpdate - // ~~~~~~~~~~~~~~~~~~~~~ - // exposed through RelayModern, not Runtime directly - type commitLocalUpdate = ( - environment: Environment, - updater: Common.StoreUpdater, - ) => void; - - // ~~~~~~~~~~~~~~~~~~~~~ - // commitRelayModernMutation - // ~~~~~~~~~~~~~~~~~~~~~ - // exposed through RelayModern, not Runtime directly - interface MutationConfig { - configs?: Common.RelayMutationConfig[]; - mutation: Common.GraphQLTaggedNode; - variables: Common.Variables; - uploadables?: Common.UploadableMap; - onCompleted?(response: T, errors: Common.PayloadError[] | null | undefined): void; - onError?(error?: Error): void; - optimisticUpdater?: Common.SelectorStoreUpdater; - optimisticResponse?: object; - updater?: Common.SelectorStoreUpdater; - } - function commitRelayModernMutation( - environment: Environment, - config: MutationConfig, - ): Common.Disposable; - - // ~~~~~~~~~~~~~~~~~~~~~ - // applyRelayModernOptimisticMutation - // ~~~~~~~~~~~~~~~~~~~~~ - // exposed through RelayModern, not Runtime directly - interface OptimisticMutationConfig { - configs?: Common.RelayMutationConfig[]; - mutation: Common.GraphQLTaggedNode; - variables: Common.Variables; - optimisticUpdater?: Common.SelectorStoreUpdater; - optimisticResponse?: object; - } - - // ~~~~~~~~~~~~~~~~~~~~~ - // fetchRelayModernQuery - // ~~~~~~~~~~~~~~~~~~~~~ - // exposed through RelayModern, not Runtime directly - /** - * A helper function to fetch the results of a query. Note that results for - * fragment spreads are masked: fields must be explicitly listed in the query in - * order to be accessible in the result object. + * Create an instance of a FragmentSpecResolver. * - * NOTE: This module is primarily intended for integrating with classic APIs. - * Most product code should use a Renderer or Container. - * - * TODO(t16875667): The return type should be `Promise`, but - * that's not really helpful as `SelectorData` is essentially just `mixed`. We - * can probably leverage generated flow types here to return the real expected - * shape. + * TODO: The FragmentSpecResolver *can* be implemented via the other methods + * defined here, so this could be moved out of core. It's convenient to have + * separate implementations until the experimental core is in OSS. */ - function fetchRelayModernQuery( - environment: any, // FIXME - $FlowFixMe in facebook source code - taggedNode: Common.GraphQLTaggedNode, - variables: Common.Variables, - cacheConfig?: Common.CacheConfig, - ): Promise; // FIXME - $FlowFixMe in facebook source code + createFragmentSpecResolver( + context: CRelayContext, + containerName: string, + fragments: CFragmentMap, + props: Props, + callback: () => void, + ): FragmentSpecResolver; - // ~~~~~~~~~~~~~~~~~~~~~ - // requestRelaySubscription - // ~~~~~~~~~~~~~~~~~~~~~ - // exposed through RelayModern, not Runtime directly - interface GraphQLSubscriptionConfig { - configs?: Common.RelayMutationConfig[]; - subscription: Common.GraphQLTaggedNode; - variables: Common.Variables; - onCompleted?(): void; - onError?(error: Error): void; - onNext?(response: object | null | undefined): void; - updater?(store: Common.RecordSourceSelectorProxy): void; - } - function requestRelaySubscription( - environment: Environment, - config: GraphQLSubscriptionConfig, - ): Common.Disposable; + /** + * Creates an instance of an OperationSelector given an operation definition + * (see `getOperation`) and the variables to apply. The input variables are + * filtered to exclude variables that do not matche defined arguments on the + * operation, and default values are populated for null values. + */ + createOperationSelector( + operation: TOperation, + variables: Variables, + ): COperationSelector; + + /** + * Given a graphql`...` tagged template, extract a fragment definition usable + * by this version of Relay core. Throws if the value is not a fragment. + */ + getFragment(node: TGraphQLTaggedNode): TFragment; + + /** + * Given a graphql`...` tagged template, extract an operation definition + * usable by this version of Relay core. Throws if the value is not an + * operation. + */ + getOperation(node: TGraphQLTaggedNode): TOperation; + + /** + * Determine if two selectors are equal (represent the same selection). Note + * that this function returns `false` when the two queries/fragments are + * different objects, even if they select the same fields. + */ + areEqualSelectors(a: CSelector, b: CSelector): boolean; + + /** + * Given the result `item` from a parent that fetched `fragment`, creates a + * selector that can be used to read the results of that fragment for that item. + * + * Example: + * + * Given two fragments as follows: + * + * ``` + * fragment Parent on User { + * id + * ...Child + * } + * fragment Child on User { + * name + * } + * ``` + * + * And given some object `parent` that is the results of `Parent` for id "4", + * the results of `Child` can be accessed by first getting a selector and then + * using that selector to `lookup()` the results against the environment: + * + * ``` + * const childSelector = getSelector(queryVariables, Child, parent); + * const childData = environment.lookup(childSelector).data; + * ``` + */ + getSelector( + operationVariables: Variables, + fragment: TFragment, + prop: any, + ): CSelector | void; + + /** + * Given the result `items` from a parent that fetched `fragment`, creates a + * selector that can be used to read the results of that fragment on those + * items. This is similar to `getSelector` but for "plural" fragments that + * expect an array of results and therefore return an array of selectors. + */ + getSelectorList( + operationVariables: Variables, + fragment: TFragment, + props: any[], + ): Array> | void; + + /** + * Given a mapping of keys -> results and a mapping of keys -> fragments, + * extracts the selectors for those fragments from the results. + * + * The canonical use-case for this function are Relay Containers, which + * use this function to convert (props, fragments) into selectors so that they + * can read the results to pass to the inner component. + */ + getSelectorsFromObject( + operationVariables: Variables, + fragments: CFragmentMap, + props: Props, + ): { [key: string]: CSelector | Array> | null | undefined }; + + /** + * Given a mapping of keys -> results and a mapping of keys -> fragments, + * extracts a mapping of keys -> id(s) of the results. + * + * Similar to `getSelectorsFromObject()`, this function can be useful in + * determining the "identity" of the props passed to a component. + */ + getDataIDsFromObject( + fragments: CFragmentMap, + props: Props, + ): { [key: string]: DataID | DataID[] | null | undefined }; + + /** + * Given a mapping of keys -> results and a mapping of keys -> fragments, + * extracts the merged variables that would be in scope for those + * fragments/results. + * + * This can be useful in determing what varaibles were used to fetch the data + * for a Relay container, for example. + */ + getVariablesFromObject( + operationVariables: Variables, + fragments: CFragmentMap, + props: Props, + ): Variables; } + + /** + * The type of the `relay` property set on React context by the React/Relay + * integration layer (e.g. QueryRenderer, FragmentContainer, etc). + */ + interface CRelayContext { + environment: TEnvironment; + variables: Variables; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayTypes + /** + * Version: Relay 1.3.0 + * File: + * https://github.com/facebook/relay/blob/fa9f48ea209ee2402d433b59a84d1cbc046574e2/packages/react-relay/classic/tools/RelayTypes.js + */ + // ~~~~~~~~~~~~~~~~~~~~~ + interface RerunParam { + param: string; + import: string; + max_runs: number; + } + interface FIELDS_CHANGE { + type: 'FIELDS_CHANGE'; + fieldIDs: { [fieldName: string]: DataID | DataID[]; }; + } + interface RANGE_ADD { + type: 'RANGE_ADD'; + parentName?: string; + parentID?: string; + connectionInfo?: Array<{ + key: string, + filters?: Variables, + rangeBehavior: string, + }>; + connectionName?: string; + edgeName: string; + rangeBehaviors?: RangeBehaviors; + } + interface NODE_DELETE { + type: 'NODE_DELETE'; + parentName?: string; + parentID?: string; + connectionName?: string; + deletedIDFieldName: string; + } + interface RANGE_DELETE { + type: 'RANGE_DELETE'; + parentName?: string; + parentID?: string; + connectionKeys?: Array<{ + key: string, + filters?: Variables, + }>; + connectionName?: string; + deletedIDFieldName: string | string[]; + pathToConnection: string[]; + } + interface REQUIRED_CHILDREN { + type: 'REQUIRED_CHILDREN'; + children: RelayConcreteNode[]; + } + type RelayMutationConfig = + FIELDS_CHANGE | + RANGE_ADD | + NODE_DELETE | + RANGE_DELETE | + REQUIRED_CHILDREN; + + interface RelayMutationTransactionCommitCallbacks { + onFailure?: RelayMutationTransactionCommitFailureCallback; + onSuccess?: RelayMutationTransactionCommitSuccessCallback; + } + type RelayMutationTransactionCommitFailureCallback = ( + transaction: RelayMutationTransaction, + preventAutoRollback: () => void, + ) => void; + type RelayMutationTransactionCommitSuccessCallback = (response: { + [key: string]: any, + }) => void; + interface NetworkLayer { + sendMutation(request: RelayMutationRequest): Promise | void; + sendQueries(requests: RelayQueryRequest[]): Promise | void; + supports(...options: string[]): boolean; + } + interface QueryResult { + error?: Error; + ref_params?: { [name: string]: any }; + response: QueryPayload; + } + interface ReadyState { + aborted: boolean; + done: boolean; + error: Error | null; + events: ReadyStateEvent[]; + ready: boolean; + stale: boolean; + } + type RelayContainerErrorEventType = + | 'CACHE_RESTORE_FAILED' + | 'NETWORK_QUERY_ERROR'; + type RelayContainerLoadingEventType = + | 'ABORT' + | 'CACHE_RESTORED_REQUIRED' + | 'CACHE_RESTORE_START' + | 'NETWORK_QUERY_RECEIVED_ALL' + | 'NETWORK_QUERY_RECEIVED_REQUIRED' + | 'NETWORK_QUERY_START' + | 'STORE_FOUND_ALL' + | 'STORE_FOUND_REQUIRED'; + type ReadyStateChangeCallback = (readyState: ReadyState) => void; + interface ReadyStateEvent { + type: RelayContainerLoadingEventType | RelayContainerErrorEventType; + error?: Error; + } + interface Abortable { + abort(): void; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayInternalTypes + /** + * Version: Relay 1.3.0 + * File: + * https://github.com/facebook/relay/blob/master/packages/react-relay/classic/tools/RelayInternalTypes.js + */ + // ~~~~~~~~~~~~~~~~~~~~~ + interface QueryPayload { [key: string]: any; } + interface RelayQuerySet { [queryName: string]: any; } + type RangeBehaviorsFunction = (connectionArgs: { + [argName: string]: any, + }) => 'APPEND' | 'IGNORE' | 'PREPEND' | 'REFETCH' | 'REMOVE'; + interface RangeBehaviorsObject { + [key: string]: 'APPEND' | 'IGNORE' | 'PREPEND' | 'REFETCH' | 'REMOVE'; + } + type RangeBehaviors = RangeBehaviorsFunction | RangeBehaviorsObject; } -// tslint:disable no-single-declare-module strict-export-declare-modifiers -declare module 'relay-runtime' { - export import Environment = __Relay.Runtime.Environment; - export import Network = __Relay.Runtime.Network; - export import RecordSource = __Relay.Runtime.RecordSource; - export import Store = __Relay.Runtime.Store; - export import Observable = __Relay.Runtime.RelayObservable; - // note RecordSourceInspector is only available in dev environment - export import RecordSourceInspector = __Relay.Runtime.RelayRecordSourceInspector; - export import ConnectionHandler = __Relay.Common.Handler; - export import ViewerHandler = __Relay.Common.Handler; +export namespace RelayRuntimeTypes { + // ~~~~~~~~~~~~~~~~~~~~~ + // Maybe Fix + // ~~~~~~~~~~~~~~~~~~~~~ + type RelayDebugger = any; + type OptimisticUpdate = any; + type OperationSelector = RelayCommonTypes.COperationSelector; + type Selector = RelayCommonTypes.CSelector; + type PayloadData = any; + type Snapshot = RelayCommonTypes.CSnapshot; + type RelayResponsePayload = any; + type MutableRecordSource = RecordSource; + + /** + * A function that returns an Observable representing the response of executing + * a GraphQL operation. + */ + type ExecuteFunction = ( + operation: object, + variables: RelayCommonTypes.Variables, + cacheConfig: RelayCommonTypes.CacheConfig, + uploadables?: RelayCommonTypes.UploadableMap, + ) => Promise; + interface RelayNetwork { + execute: ExecuteFunction; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayDefaultHandlerProvider + // ~~~~~~~~~~~~~~~~~~~~~ + function HandlerProvider(name: string): typeof RelayCommonTypes.Handler | void; + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayModernEnvironment + // ~~~~~~~~~~~~~~~~~~~~~ + interface EnvironmentConfig { + configName?: string; + handlerProvider?: typeof HandlerProvider; + network: Network; + store: Store; + } + class Environment { + constructor(config: EnvironmentConfig); + getStore(): Store; + getDebugger(): RelayDebugger; + applyUpdate(optimisticUpdate: OptimisticUpdate): RelayCommonTypes.Disposable; + revertUpdate(update: OptimisticUpdate): void; + replaceUpdate(update: OptimisticUpdate, newUpdate: OptimisticUpdate): void; + applyMutation(config: { + operation: OperationSelector, + optimisticUpdater?: RelayCommonTypes.SelectorStoreUpdater, + optimisticResponse?: object, + }): RelayCommonTypes.Disposable; + check(readSelector: Selector): boolean; + commitPayload( + operationSelector: OperationSelector, + payload: PayloadData, + ): void; + commitUpdate(updater: RelayCommonTypes.StoreUpdater): void; + lookup(readSelector: Selector): Snapshot; + subscribe( + snapshot: Snapshot, + callback: (snapshot: Snapshot) => void, + ): RelayCommonTypes.Disposable; + retain(selector: Selector): RelayCommonTypes.Disposable; + execute(config: { + operation: OperationSelector, + cacheConfig?: RelayCommonTypes.CacheConfig, + updater?: RelayCommonTypes.SelectorStoreUpdater, + }): RelayObservable; + executeMutation(config: { + operation: OperationSelector, + optimisticUpdater?: RelayCommonTypes.SelectorStoreUpdater, + optimisticResponse?: object, + updater?: RelayCommonTypes.SelectorStoreUpdater, + uploadables?: RelayCommonTypes.UploadableMap, + }): RelayObservable; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayInMemoryRecordSource + // ~~~~~~~~~~~~~~~~~~~~~ + interface Record { [key: string]: any; } + interface RecordMap { [dataID: string]: Record | null | undefined; } + + // ~~~~~~~~~~~~~~~~~~~~~ + // Network + // ~~~~~~~~~~~~~~~~~~~~~ + class Network { + /** + * Creates an implementation of the `Network` interface defined in + * `RelayNetworkTypes` given `fetch` and `subscribe` functions. + */ + static create(fetchFn: typeof RelayCommonTypes.FetchFunction, subscribeFn?: RelayCommonTypes.SubscribeFunction): RelayNetwork; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // Network + // ~~~~~~~~~~~~~~~~~~~~~ + class RecordSource { + constructor(records?: RecordMap); + clear(): void; + delete(dataID: RelayCommonTypes.DataID): void; + get(dataID: RelayCommonTypes.DataID): Record | void; + getRecordIDs(): RelayCommonTypes.DataID[]; + getStatus(dataID: RelayCommonTypes.DataID): 'EXISTENT' | 'NONEXISTENT' | 'UNKNOWN'; + has(dataID: RelayCommonTypes.DataID): boolean; + load( + dataID: RelayCommonTypes.DataID, + callback: (error: Error | null, record: Record | null) => void, + ): void; + remove(dataID: RelayCommonTypes.DataID): void; + set(dataID: RelayCommonTypes.DataID, record: Record): void; + size(): number; + toJSON(): RecordMap; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // ModernStore + // ~~~~~~~~~~~~~~~~~~~~~ + class Store { + constructor(source: RecordSource); + getSource(): MutableRecordSource; + check(selector: Selector): boolean; + retain(selector: Selector): RelayCommonTypes.Disposable; + lookup(selector: Selector): Snapshot; + notify(): void; + publish(source: RecordSource): void; + subscribe( + snapshot: Snapshot, + callback: (snapshot: Snapshot) => void, + ): RelayCommonTypes.Disposable; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayRecordSourceInspector + // ~~~~~~~~~~~~~~~~~~~~~ + /** + * An internal class to provide a console-friendly string representation of a + * Record. + */ + class RecordSummary { + id: RelayCommonTypes.DataID; + type: string | null | undefined; + static createFromRecord(id: RelayCommonTypes.DataID, record: any): RecordSummary; + constructor(id: RelayCommonTypes.DataID, type: string | null | undefined); + toString(): string; + } + /** + * Internal class for inspecting a single Record. + */ + class RecordInspector { + constructor(sourceInspector: RelayRecordSourceInspector, record: Record); + /** + * Get the cache id of the given record. For types that implement the `Node` + * interface (or that have an `id`) this will be `id`, for other types it will be + * a synthesized identifier based on the field path from the nearest ancestor + * record that does have an `id`. + */ + getDataID(): RelayCommonTypes.DataID; + + /** + * Returns a list of the fields that have been fetched on the current record. + */ + getFields(): string[]; + + /** + * Returns the type of the record. + */ + getType(): string; + + /** + * Returns a copy of the internal representation of the record. + */ + inspect(): any; + + /** + * Returns the value of a scalar field. May throw if the given field is + * present but not actually scalar. + */ + getValue(name: string, args?: RelayCommonTypes.Variables): any; + + /** + * Returns an inspector for the given scalar "linked" field (a field whose + * value is another Record instead of a scalar). May throw if the field is + * present but not a scalar linked record. + */ + getLinkedRecord(name: string, args?: RelayCommonTypes.Variables): RecordInspector | void; + + /** + * Returns an array of inspectors for the given plural "linked" field (a field + * whose value is an array of Records instead of a scalar). May throw if the + * field is present but not a plural linked record. + */ + getLinkedRecords(name: string, args?: RelayCommonTypes.Variables): RecordInspector[] | void; + } + + class RelayRecordSourceInspector { + constructor(source: RecordSource); + static getForEnvironment(environment: Environment): RelayRecordSourceInspector; + /** + * Returns an inspector for the record with the given id, or null/undefined if + * that record is deleted/unfetched. + */ + get(dataID: RelayCommonTypes.DataID): RecordInspector | void; + /** + * Returns a list of ": " for each record in the store that has an + * `id`. + */ + getNodes(): RecordSummary[]; + /** + * Returns a list of ": " for all records in the store including + * those that do not have an `id`. + */ + getRecords(): RecordSummary[]; + + /** + * Returns an inspector for the synthesized "root" object, allowing access to + * e.g. the `viewer` object or the results of other fields on the "Query" + * type. + */ + getRoot(): RecordInspector; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // RelayObservable + // ~~~~~~~~~~~~~~~~~~~~~ + interface Subscription { + unsubscribe(): void; + readonly closed: boolean; + } + interface Observer { + start?(subscription: Subscription): any; + next?(nextThing: T): any; + error?(error: Error): any; + complete?(): any; + unsubscribe?(subscription: Subscription): any; + } + type Source = () => any; + interface Subscribable { + subscribe(observer: Observer): Subscription; + } + type ObservableFromValue = RelayObservable | Promise | T; + class RelayObservable implements Subscribable { + _source: Source; + + constructor(source: Source); + + /** + * When an unhandled error is detected, it is reported to the host environment + * (the ESObservable spec refers to this method as "HostReportErrors()"). + * + * The default implementation in development builds re-throws errors in a + * separate frame, and from production builds does nothing (swallowing + * uncaught errors). + * + * Called during application initialization, this method allows + * application-specific handling of uncaught errors. Allowing, for example, + * integration with error logging or developer tools. + */ + static onUnhandledError(callback: (error: Error) => any): void; + + /** + * Accepts various kinds of data sources, and always returns a RelayObservable + * useful for accepting the result of a user-provided FetchFunction. + */ + static from(obj: ObservableFromValue): RelayObservable; + + /** + * Creates a RelayObservable, given a function which expects a legacy + * Relay Observer as the last argument and which returns a Disposable. + * + * To support migration to Observable, the function may ignore the + * legacy Relay observer and directly return an Observable instead. + */ + static fromLegacy( + callback: (legacyObserver: RelayCommonTypes.LegacyObserver) => RelayCommonTypes.Disposable | RelayObservable, + ): RelayObservable; + + /** + * Returns a new Observable which returns the same values as this one, but + * modified so that the provided Observer is called to perform a side-effects + * for all events emitted by the source. + * + * Any errors that are thrown in the side-effect Observer are unhandled, and + * do not affect the source Observable or its Observer. + * + * This is useful for when debugging your Observables or performing other + * side-effects such as logging or performance monitoring. + */ + do(observer: Observer): RelayObservable; + + /** + * Returns a new Observable which returns the same values as this one, but + * modified so that the finally callback is performed after completion, + * whether normal or due to error or unsubscription. + * + * This is useful for cleanup such as resource finalization. + */ + finally(fn: () => any): RelayObservable; + + /** + * Returns a new Observable which is identical to this one, unless this + * Observable completes before yielding any values, in which case the new + * Observable will yield the values from the alternate Observable. + * + * If this Observable does yield values, the alternate is never subscribed to. + * + * This is useful for scenarios where values may come from multiple sources + * which should be tried in order, i.e. from a cache before a network. + */ + ifEmpty(alternate: RelayObservable): RelayObservable; + + /** + * Observable's primary API: returns an unsubscribable Subscription to the + * source of this Observable. + */ + subscribe(observer: Observer): Subscription; + + /** + * Supports subscription of a legacy Relay Observer, returning a Disposable. + */ + subscribeLegacy(legacyObserver: RelayCommonTypes.LegacyObserver): RelayCommonTypes.Disposable; + + /** + * Returns a new Observerable where each value has been transformed by + * the mapping function. + */ + map(fn: (thing: T) => U): RelayObservable; + + /** + * Returns a new Observable where each value is replaced with a new Observable + * by the mapping function, the results of which returned as a single + * concattenated Observable. + */ + concatMap(fn: (thing: T) => ObservableFromValue): RelayObservable; + + /** + * Returns a new Observable which first mirrors this Observable, then when it + * completes, waits for `pollInterval` milliseconds before re-subscribing to + * this Observable again, looping in this manner until unsubscribed. + * + * The returned Observable never completes. + */ + poll(pollInterval: number): RelayObservable; + + /** + * Returns a Promise which resolves when this Observable yields a first value + * or when it completes with no value. + */ + toPromise(): Promise; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // commitLocalUpdate + // ~~~~~~~~~~~~~~~~~~~~~ + // exposed through RelayModern, not Runtime directly + type commitLocalUpdate = ( + environment: Environment, + updater: RelayCommonTypes.StoreUpdater, + ) => void; + + // ~~~~~~~~~~~~~~~~~~~~~ + // commitRelayModernMutation + // ~~~~~~~~~~~~~~~~~~~~~ + // exposed through RelayModern, not Runtime directly + interface MutationConfig { + configs?: RelayCommonTypes.RelayMutationConfig[]; + mutation: RelayCommonTypes.GraphQLTaggedNode; + variables: RelayCommonTypes.Variables; + uploadables?: RelayCommonTypes.UploadableMap; + onCompleted?(response: T, errors: RelayCommonTypes.PayloadError[] | null | undefined): void; + onError?(error?: Error): void; + optimisticUpdater?: RelayCommonTypes.SelectorStoreUpdater; + optimisticResponse?: object; + updater?: RelayCommonTypes.SelectorStoreUpdater; + } + function commitRelayModernMutation( + environment: Environment, + config: MutationConfig, + ): RelayCommonTypes.Disposable; + + // ~~~~~~~~~~~~~~~~~~~~~ + // applyRelayModernOptimisticMutation + // ~~~~~~~~~~~~~~~~~~~~~ + // exposed through RelayModern, not Runtime directly + interface OptimisticMutationConfig { + configs?: RelayCommonTypes.RelayMutationConfig[]; + mutation: RelayCommonTypes.GraphQLTaggedNode; + variables: RelayCommonTypes.Variables; + optimisticUpdater?: RelayCommonTypes.SelectorStoreUpdater; + optimisticResponse?: object; + } + + // ~~~~~~~~~~~~~~~~~~~~~ + // fetchRelayModernQuery + // ~~~~~~~~~~~~~~~~~~~~~ + // exposed through RelayModern, not Runtime directly + /** + * A helper function to fetch the results of a query. Note that results for + * fragment spreads are masked: fields must be explicitly listed in the query in + * order to be accessible in the result object. + * + * NOTE: This module is primarily intended for integrating with classic APIs. + * Most product code should use a Renderer or Container. + * + * TODO(t16875667): The return type should be `Promise`, but + * that's not really helpful as `SelectorData` is essentially just `mixed`. We + * can probably leverage generated flow types here to return the real expected + * shape. + */ + function fetchRelayModernQuery( + environment: any, // FIXME - $FlowFixMe in facebook source code + taggedNode: RelayCommonTypes.GraphQLTaggedNode, + variables: RelayCommonTypes.Variables, + cacheConfig?: RelayCommonTypes.CacheConfig, + ): Promise; // FIXME - $FlowFixMe in facebook source code + + // ~~~~~~~~~~~~~~~~~~~~~ + // requestRelaySubscription + // ~~~~~~~~~~~~~~~~~~~~~ + // exposed through RelayModern, not Runtime directly + interface GraphQLSubscriptionConfig { + configs?: RelayCommonTypes.RelayMutationConfig[]; + subscription: RelayCommonTypes.GraphQLTaggedNode; + variables: RelayCommonTypes.Variables; + onCompleted?(): void; + onError?(error: Error): void; + onNext?(response: object | null | undefined): void; + updater?(store: RelayCommonTypes.RecordSourceSelectorProxy): void; + } + function requestRelaySubscription( + environment: Environment, + config: GraphQLSubscriptionConfig, + ): RelayCommonTypes.Disposable; } + +// ~~~~~~~~~~~~~~~~~~~~~ +// Package Exports +// ~~~~~~~~~~~~~~~~~~~~~ +export import Environment = RelayRuntimeTypes.Environment; +export import Network = RelayRuntimeTypes.Network; +export import RecordSource = RelayRuntimeTypes.RecordSource; +export import Store = RelayRuntimeTypes.Store; +export import Observable = RelayRuntimeTypes.RelayObservable; +// note RecordSourceInspector is only available in dev environment +export import RecordSourceInspector = RelayRuntimeTypes.RelayRecordSourceInspector; +export import ConnectionHandler = RelayCommonTypes.Handler; +export import ViewerHandler = RelayCommonTypes.Handler;