diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..938c562 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,96 @@ +NOTE: this is just documenting my process for this PR in case anyone wants to help. It's not necessarily intended to be used as the CONTRIBUTING file for `reason-apollo-hooks` + +# Goals and Intent + +My hope is that we can achieve well maintained, nearly-complete bindings without a huge burden on any single person if we follow these two rules: + +1. Follow a consistent pattern for bindings +1. Avoid partial types or bindings if possible + +## Going the Full Distance + +Please type something as completely as possible when you come across it or leave it for someone else (if nothing else, put an abstract type so things will still flow through everywhere and people can cast it). If all of us contribute just a little piece, but do it completely, it should be very easy to get 99% complete bindings. Also, 50% of the work is in tracing through the code and loading up context. This way no one has to go back and duplicate that work. Each binding we add also makes the next one that much faster as we have more and more types we can reuse blindly. + +# Guidelines (style) + +## Directory Structure and File Naming + +``` +@apollo/client/react/hooks/useQuery.js +``` + +should become + +``` +reason-react-hooks/src/react/hooks/ApolloClient__React_Hooks_UseQuery.re +``` + +in reason. + +### Breaking it down: `/[1]/[2]__[3]_[4]` + +1. Reason files should be located in the same directory structure as the js counterpart (usually there is a `.d.ts` for every `.js` file so we can think of them interchangeably) +1. All module names should be prefixed with `ApolloClient__` "namespace" +1. File names reflect the directory structure +1. Files should be named the same as the js counterpart + +## Types + +- Every type goes in its own module with a `type t` (exception: see SubStypes) +- Every type module should contain a `type t`, a `Js_` module with a `type t`, and a `toJs` or `fromJs` conversion function. `t => Js_.t` or `Js_.t => t` +- Paste the type definition from the `.d.ts` file above the `type t` in the `Js_` module +- If data requires parsing/serializing create a new record even if the shape is the same. This ensures you don't forget to parse somewhere. +- Prefer single types when the more complicated type in a union can express both `shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean);` +- Do all of this even if it doesn't need it (See Reasoning Behind `Js_` modules) + +Example: + +``` +// #1 - create a Js_ module for your type +module Js_ = { + // #2 - paste the definition from the .js file here + // #3 - add the `.js` representation of type t = ... +} + +// #4 - add the Reason representation of type t +// #4a - you can use `type t = Js_.t` if they are _exactly_ the same +// #4b - if they are the exact same _shape_ but requires parsing or serializing, *define a new record of the same shape* so we can leverage the fact that records are nominally typed to prevent someone forgetting to convert somewhere + +// #5 - add `toJs` or `fromJs`. They often require parsing: `let fromJs: Js_.t('jsData, ~parse: 'jsData => 'data) => t('data)` +``` + +### Reasoning behind `Js_` modules + +When I'm defining a `Js_` `type t` that references other types, I know I should always be using the `Js_.t` versions of those types and it's very easy to visually confirm the correct types are being referenced. The same goes for defining a top-level `type t`, I should never see a `Js_.t` there. Now I can just follow the compiler errors. + +FWIW, I tried `JS` naming, but I would accidentally type `Js` all the time and not see it. Additionally, due to the importance of the above, I wanted it to stick out and the `_` helps a little there. :shrug: + +### SubTypes + +Sometimes multiple types were required to represent a single type in TypeScript. In order to help make it clear what is a binding to an actual type and what is just needed by Reason. In this case we take a similar naming approach to the modules (prefixing with the parent). For instance, `Apollo_Client__React_Types.QueryResult.Raw` has a `type t` that uses `t_fetchMoreOptions` which in turn uses `t_fetchMoreOptions_updateQueryOptions`. + +## Binding to Js Module Exports + +`externals` go under a `Js_` module and any types they reference should be `Js_.t` versions. + +## Binding to Methods + +Prefer T-first with `[@bs.send]`. Again, `externals` go under a `Js_` module with a `type t` and any types they reference should be `Js_.t` versions. + +## Binding to Objects + +Use records + +## Binding to Enums + +Use standard variants. You can use `jsConverter` for ints, but otherwise use manual `toJs` and `fromJs` functions. + +## General + +- Prefer T-first because that's the Reason community default +- Hooks are T-last because that makes sense given their usage +- ApolloClient methods are a Frankenstein T-first _and_ T-last because they want to maintain similarity with hooks api, but are also T-first due to [@bs.send] and T-first preference + +# Exeptional Exception Handling + +TBD diff --git a/src/ApolloClient.re b/src/ApolloClient.re new file mode 100644 index 0000000..216dc2d --- /dev/null +++ b/src/ApolloClient.re @@ -0,0 +1,67 @@ +// export { default as Observable } from 'zen-observable'; +module Observable = ApolloClient__ZenObservable.Observable; +// export { isReference, makeReference } from './utilities/graphql/storeUtils.js'; +// export { ApolloLink } from './link/core/ApolloLink.js'; +module ApolloLink = ApolloClient__Link_Core_ApolloLink; +// export { execute } from './link/core/execute.js'; +let execute = ApolloClient__Link_Core_ApolloLink.Static.execute; +// export { ApolloError, isApolloError } from './errors/ApolloError.js'; +// export { NetworkStatus } from './core/networkStatus.js'; +// export { ObservableQuery } from './core/ObservableQuery.js'; +// export { serializeFetchParameter } from './link/http/serializeFetchParameter.js'; +// export { selectURI } from './link/http/selectURI.js'; +// export { throwServerError } from './link/utils/throwServerError.js'; +// export { parseAndCheckHttpResponse } from './link/http/parseAndCheckHttpResponse.js'; +// export { checkFetcher } from './link/http/checkFetcher.js'; +// export { fallbackHttpConfig, selectHttpOptionsAndBody } from './link/http/selectHttpOptionsAndBody.js'; +// export { createSignalIfSupported } from './link/http/createSignalIfSupported.js'; +// export { fromError } from './link/utils/fromError.js'; +// export { createHttpLink } from './link/http/createHttpLink.js'; +let createHttpLink = ApolloClient_Link_Http_CreateHttpLink.createHttpLink; +// export { HttpLink } from './link/http/HttpLink.js'; +module HttpLink = ApolloClient_Link_Http_HttpLink; +// export { ApolloClient } from './ApolloClient.js'; +module ApolloClient = ApolloClient__ApolloClient; +// export { ApolloCache } from './cache/core/cache.js'; +module ApolloCache = ApolloClient__Cache_Core_Cache.ApolloCache; +// export { Cache } from './cache/core/types/Cache.js'; +// export { MissingFieldError } from './cache/core/types/common.js'; +// export { defaultDataIdFromObject } from './cache/inmemory/policies.js'; +let defaultDataIdFromObject = ApolloClient__Cache_InMemory_Policies.defaultDataIdFromObject; +// export { InMemoryCache } from './cache/inmemory/inMemoryCache.js'; +module InMemoryCache = ApolloClient__Cache_InMemory_InMemoryCache; +// export { empty } from './link/core/empty.js'; +let empty = ApolloClient__Link_Core_ApolloLink.Static.empty; +// export { from } from './link/core/from.js'; +let from = ApolloClient__Link_Core_ApolloLink.Static.from; +// export { split } from './link/core/split.js'; +let split = ApolloClient__Link_Core_ApolloLink.Static.split; +// export { concat } from './link/core/concat.js'; +let concat = ApolloClient__Link_Core_ApolloLink.Static.concat; +// export { toPromise } from './link/utils/toPromise.js'; +// export { fromPromise } from './link/utils/fromPromise.js'; +// export { default as gql } from 'graphql-tag'; +let gql = ApolloClient__GraphqlTag.gql; +// export { disableExperimentalFragmentVariables, disableFragmentWarnings, enableExperimentalFragmentVariables, resetCaches } from './core/index.js'; +// export { getApolloContext, resetApolloContext } from './react/context/ApolloContext.js'; +// export { ApolloProvider } from './react/context/ApolloProvider.js'; +module ApolloProvider = ApolloClient__React_ApolloProvider; +// export { ApolloConsumer } from './react/context/ApolloConsumer.js'; +// export { DocumentType, operationName, parser } from './react/parser/parser.js'; +// export { useQuery } from './react/hooks/useQuery.js'; +let useQuery = ApolloClient__React_UseQuery.useQuery; +// export { useLazyQuery } from './react/hooks/useLazyQuery.js'; +// export { useMutation } from './react/hooks/useMutation.js'; +// export { useSubscription } from './react/hooks/useSubscription.js'; +let useSubscription = ApolloClient__React_UseSubscription.useSubscription; +// export { useApolloClient } from './react/hooks/useApolloClient.js'; +// export { RenderPromises } from './react/ssr/RenderPromises.js'; + +module Utilities = ApolloClient__Utilities; + +module Extend = { + module Query = ApolloClient__React_UseQuery.Extend; + module QueryNoRequiredVariables = ApolloClient__React_UseQuery.ExtendNoRequiredVariables; + module Subscription = ApolloClient__React_UseSubscription.Extend; + module SubscriptionNoRequiredVariables = ApolloClient__React_UseSubscription.ExtendNoRequiredVariables; +}; diff --git a/src/ApolloClient__ApolloClient.re b/src/ApolloClient__ApolloClient.re new file mode 100644 index 0000000..8a78c1a --- /dev/null +++ b/src/ApolloClient__ApolloClient.re @@ -0,0 +1,548 @@ +module ApolloQueryResult = ApolloClient__Core_Types.ApolloQueryResult; +module ApolloLink = ApolloClient__Link_Core_ApolloLink; +module ApolloCache = ApolloClient__Cache_Core_Cache.ApolloCache; +module DataProxy = ApolloClient__Cache_Core_Types.DataProxy; +module ErrorPolicy = ApolloClient__Core_WatchQueryOptions.ErrorPolicy; +module FetchPolicy = ApolloClient__Core_WatchQueryOptions.FetchPolicy; +module FetchPolicy__noCacheExtracted = ApolloClient__Core_WatchQueryOptions.FetchPolicy__noCacheExtracted; +module FragmentMatcher = ApolloClient__Core_LocalState.FragmentMatcher; +module GraphQL = ApolloClient__Graphql; +module GraphqlTag = ApolloClient__GraphqlTag; +module FetchResult = ApolloClient__Link_Core_Types.FetchResult; +module MutationOptions = ApolloClient__Core_WatchQueryOptions.MutationOptions; +module MutationQueryReducersMap = ApolloClient__Core_WatchQueryOptions.MutationQueryReducersMap; +module MutationUpdaterFn = ApolloClient__Core_WatchQueryOptions.MutationUpdaterFn; +module QueryOptions = ApolloClient__Core_WatchQueryOptions.QueryOptions; +module PureQueryOptions = ApolloClient__Core_Types.PureQueryOptions; +module RefetchQueryDescription = ApolloClient__Core_WatchQueryOptions.RefetchQueryDescription; +module Resolvers = ApolloClient__Core_Types.Resolvers; +module UriFunction = ApolloClient_Link_Http_SelectHttpOptionsAndBody.UriFunction; +module Types = ApolloClient__Reason_Types; +module WatchQueryFetchPolicy = ApolloClient__Core_WatchQueryOptions.WatchQueryFetchPolicy; +module WatchQueryOptions = ApolloClient__Core_WatchQueryOptions.QueryOptions; + +module DefaultOptions = { + module DefaultWatchQueryOptions = { + module Js_ = { + // Partial; + type t = { + fetchPolicy: option(WatchQueryFetchPolicy.Js_.t), + // query: GraphQL.Language.documentNode, + // variables: option('variables), + errorPolicy: option(ErrorPolicy.Js_.t), + context: option(Js.Json.t), + }; + }; + + type t = { + fetchPolicy: option(WatchQueryFetchPolicy.t), + errorPolicy: option(ErrorPolicy.t), + context: option(Js.Json.t), + }; + + let toJs: t => Js_.t = + t => { + fetchPolicy: + t.fetchPolicy->Belt.Option.map(WatchQueryFetchPolicy.toJs), + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + context: t.context, + }; + + let make = (~fetchPolicy=?, ~errorPolicy=?, ~context=?, ()) => { + fetchPolicy, + errorPolicy, + context, + }; + }; + + module DefaultQueryOptions = { + module Js_ = { + // Partial; + type t = { + fetchPolicy: option(FetchPolicy.Js_.t), + // query: GraphQL.Language.documentNode, + // variables: option('variables), + errorPolicy: option(ErrorPolicy.Js_.t), + context: option(Js.Json.t), + }; + }; + + type t = { + fetchPolicy: option(FetchPolicy.t), + errorPolicy: option(ErrorPolicy.t), + context: option(Js.Json.t), + }; + + let toJs: t => Js_.t = + t => { + fetchPolicy: t.fetchPolicy->Belt.Option.map(FetchPolicy.toJs), + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + context: t.context, + }; + + let make = (~fetchPolicy=?, ~errorPolicy=?, ~context=?, ()) => { + fetchPolicy, + errorPolicy, + context, + }; + }; + + module DefaultMutationOptions = { + module Js_ = { + // Partial; + type t = { + context: option(Js.Json.t), + fetchPolicy: option(FetchPolicy__noCacheExtracted.Js_.t), + awaitRefetchQueries: option(bool), + errorPolicy: option(ErrorPolicy.Js_.t), + // optimisticResponse: option('variables => 'jsData), + // update: option(MutationUpdaterFn.Js_.t('jsData)), + // updateQueries: option(MutationQueryReducersMap.Js_.t('jsData)), + refetchQueries: option(RefetchQueryDescription.Js_.t), + // variables: option('variables), + }; + }; + + type t = { + context: option(Js.Json.t), + fetchPolicy: option(FetchPolicy__noCacheExtracted.t), + awaitRefetchQueries: option(bool), + errorPolicy: option(ErrorPolicy.t), + refetchQueries: option(RefetchQueryDescription.t), + }; + + let toJs: t => Js_.t = + t => { + context: t.context, + fetchPolicy: + t.fetchPolicy->Belt.Option.map(FetchPolicy__noCacheExtracted.toJs), + awaitRefetchQueries: t.awaitRefetchQueries, + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + refetchQueries: + t.refetchQueries->Belt.Option.map(RefetchQueryDescription.toJs), + }; + + let make = + ( + ~context=?, + ~fetchPolicy=?, + ~awaitRefetchQueries=?, + ~errorPolicy=?, + ~refetchQueries=?, + (), + ) => { + context, + fetchPolicy, + awaitRefetchQueries, + errorPolicy, + refetchQueries, + }; + }; + + module Js_ = { + // export interface DefaultOptions { + // watchQuery?: Partial; + // query?: Partial; + // mutate?: Partial; + // } + type t = { + watchQuery: option(DefaultWatchQueryOptions.Js_.t), + query: option(DefaultQueryOptions.Js_.t), + mutate: option(DefaultMutationOptions.Js_.t), + }; + }; + + type t = { + watchQuery: option(DefaultWatchQueryOptions.t), + query: option(DefaultQueryOptions.t), + mutate: option(DefaultMutationOptions.t), + }; + + let toJs: t => Js_.t = + t => { + watchQuery: + t.watchQuery->Belt.Option.map(DefaultWatchQueryOptions.toJs), + query: t.query->Belt.Option.map(DefaultQueryOptions.toJs), + mutate: t.mutate->Belt.Option.map(DefaultMutationOptions.toJs), + }; + + let make: + ( + ~mutate: DefaultMutationOptions.t=?, + ~query: DefaultQueryOptions.t=?, + ~watchQuery: DefaultWatchQueryOptions.t=?, + unit + ) => + t = + (~mutate=?, ~query=?, ~watchQuery=?, ()) => {watchQuery, query, mutate}; +}; + +module ApolloClientOptions = { + module Js_ = { + // export declare type ApolloClientOptions = { + // uri?: string | UriFunction; + // credentials?: string; + // headers?: Record; + // link?: ApolloLink; + // cache: ApolloCache; + // ssrForceFetchDelay?: number; + // ssrMode?: boolean; + // connectToDevTools?: boolean; + // queryDeduplication?: boolean; + // defaultOptions?: DefaultOptions; + // assumeImmutableResults?: boolean; + // resolvers?: Resolvers | Resolvers[]; + // typeDefs?: string | string[] | DocumentNode | DocumentNode[]; + // fragmentMatcher?: FragmentMatcher; + // name?: string; + // version?: string; + // }; + type t = { + uri: option(UriFunction.Js_.t), + credentials: option(string), + headers: option(Js.Dict.t(string)), + link: option(ApolloLink.Js_.t), + cache: ApolloCache.Js_.t(Js.Json.t), + ssrForceFetchDelay: option(int), + ssrMode: option(bool), + connectToDevTools: option(bool), + queryDeduplication: option(bool), + defaultOptions: option(DefaultOptions.Js_.t), + assumeImmutableResults: option(bool), + resolvers: option(array(Resolvers.Js_.t)), + typeDefs: option(array(GraphQL.documentNode)), + fragmentMatcher: option(FragmentMatcher.Js_.t), + name: option(string), + version: option(string), + }; + }; + + type t = { + uri: option(UriFunction.t), + credentials: option(string), + headers: option(Js.Dict.t(string)), + link: option(ApolloLink.t), + cache: ApolloCache.Js_.t(Js.Json.t), + ssrForceFetchDelay: option(int), + ssrMode: option(bool), + connectToDevTools: option(bool), + queryDeduplication: option(bool), + defaultOptions: option(DefaultOptions.t), + assumeImmutableResults: option(bool), + resolvers: option(array(Resolvers.t)), + typeDefs: option(array(GraphQL.documentNode)), + fragmentMatcher: option(FragmentMatcher.t), + name: option(string), + version: option(string), + }; + + let toJs: t => Js_.t = + t => { + uri: t.uri, + credentials: t.credentials, + headers: t.headers, + link: t.link, + cache: t.cache, + ssrForceFetchDelay: t.ssrForceFetchDelay, + ssrMode: t.ssrMode, + connectToDevTools: t.connectToDevTools, + queryDeduplication: t.queryDeduplication, + defaultOptions: t.defaultOptions->Belt.Option.map(DefaultOptions.toJs), + assumeImmutableResults: t.assumeImmutableResults, + resolvers: t.resolvers, + typeDefs: t.typeDefs, + fragmentMatcher: t.fragmentMatcher, + name: t.name, + version: t.version, + }; +}; + +type refetchQueryDescription = + | PureQueryOptions(PureQueryOptions.t('variables)): refetchQueryDescription + | String(string): refetchQueryDescription; + +module Js_ = { + // export declare class ApolloClient implements DataProxy { + // link: ApolloLink; + // cache: ApolloCache; + // disableNetworkFetches: boolean; + // version: string; + // queryDeduplication: boolean; + // defaultOptions: DefaultOptions; + // readonly typeDefs: ApolloClientOptions['typeDefs']; + // private queryManager; + // private devToolsHookCb; + // private resetStoreCallbacks; + // private clearStoreCallbacks; + // private localState; + // stop(): void; + // watchQuery(options: WatchQueryOptions): ObservableQuery; + // subscribe(options: SubscriptionOptions): Observable>; + // readFragment(options: DataProxy.Fragment, optimistic?: boolean): T | null; + // writeFragment(options: DataProxy.WriteFragmentOptions): void; + // __actionHookForDevTools(cb: () => any): void; + // __requestRaw(payload: GraphQLRequest): Observable; + // resetStore(): Promise[] | null>; + // clearStore(): Promise; + // onResetStore(cb: () => Promise): () => void; + // onClearStore(cb: () => Promise): () => void; + // reFetchObservableQueries(includeStandby?: boolean): Promise[]>; + // extract(optimistic?: boolean): TCacheShape; + // restore(serializedState: TCacheShape): ApolloCache; + // addResolvers(resolvers: Resolvers | Resolvers[]): void; + // setResolvers(resolvers: Resolvers | Resolvers[]): void; + // getResolvers(): Resolvers; + // setLocalStateFragmentMatcher(fragmentMatcher: FragmentMatcher): void; + // } + type t; + // mutate(options: MutationOptions): Promise>; + [@bs.send] + external mutate: + (t, ~options: MutationOptions.Js_.t('jsData, 'variables)) => + Js.Promise.t(FetchResult.Js_.t('jsData)) = + "mutate"; + + // query(options: QueryOptions): Promise>; + [@bs.send] + external query: + (t, ~options: QueryOptions.Js_.t('variables)) => + Js.Promise.t(ApolloQueryResult.Js_.t('jsData)) = + "query"; + + // readQuery(options: DataProxy.Query, optimistic?: boolean): T | null; + [@bs.send] + external readQuery: + ( + t, + ~options: DataProxy.Query.Js_.t('variables), + ~optimistic: option(bool) + ) => + Js.nullable('jsData) = + "readQuery"; + + // writeQuery(options: DataProxy.WriteQueryOptions): void; + [@bs.send] + external writeQuery: + (t, ~options: DataProxy.WriteQueryOptions.Js_.t('jsData, 'variables)) => + unit = + "writeQuery"; + + [@bs.module "@apollo/client"] [@bs.new] + external make: ApolloClientOptions.Js_.t => t = "ApolloClient"; +}; + +type t = Js_.t; + +let make: + ( + ~uri: UriFunction.t=?, + ~credentials: string=?, + ~headers: Js.Dict.t(string)=?, + ~link: ApolloLink.t=?, + ~cache: ApolloCache.Js_.t(Js.Json.t), + ~ssrForceFetchDelay: int=?, + ~ssrMode: bool=?, + ~connectToDevTools: bool=?, + ~queryDeduplication: bool=?, + ~defaultOptions: DefaultOptions.t=?, + ~assumeImmutableResults: bool=?, + ~resolvers: array(Resolvers.t)=?, + ~typeDefs: array(GraphQL.documentNode)=?, + ~fragmentMatcher: FragmentMatcher.t=?, + ~name: string=?, + ~version: string=?, + unit + ) => + t = + ( + ~uri=?, + ~credentials=?, + ~headers=?, + ~link=?, + ~cache, + ~ssrForceFetchDelay=?, + ~ssrMode=?, + ~connectToDevTools=?, + ~queryDeduplication=?, + ~defaultOptions=?, + ~assumeImmutableResults=?, + ~resolvers=?, + ~typeDefs=?, + ~fragmentMatcher=?, + ~name=?, + ~version=?, + (), + ) => + Js_.make( + ApolloClientOptions.toJs({ + uri, + credentials, + headers, + link, + cache, + ssrForceFetchDelay, + ssrMode, + connectToDevTools, + queryDeduplication, + defaultOptions, + assumeImmutableResults, + resolvers, + typeDefs, + fragmentMatcher, + name, + version, + }), + ); + +let mutate: + type data variables jsData jsVariables. + ( + t, + ~awaitRefetchQueries: bool=?, + ~context: Js.Json.t=?, + ~errorPolicy: ErrorPolicy.t=?, + ~fetchPolicy: FetchPolicy__noCacheExtracted.t=?, + ~optimisticResponse: variables => data=?, + ~refetchQueries: RefetchQueryDescription.t=?, + ~updateQueries: MutationQueryReducersMap.t(data)=?, + ~update: MutationUpdaterFn.t(data)=?, + ~variables: variables=?, + (module Types.Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + Js.Promise.t(FetchResult.t(data)) = + ( + client, + ~awaitRefetchQueries=?, + ~context=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~optimisticResponse=?, + ~refetchQueries=?, + ~updateQueries=?, + ~update=?, + ~variables=?, + (module Operation), + ) => { + Js_.mutate( + client, + ~options= + MutationOptions.toJs( + { + awaitRefetchQueries, + context, + errorPolicy, + fetchPolicy, + mutation: GraphqlTag.gql(Operation.query), + optimisticResponse, + updateQueries, + refetchQueries, + update, + variables, + }, + ~parse=Operation.parse, + ~serialize=Operation.serialize, + ), + ) + ->Js.Promise.then_( + jsResult => + jsResult + ->FetchResult.fromJs(_, ~parse=Operation.parse) + ->Js.Promise.resolve, + _, + ); + }; + +let query: + type data variables jsData jsVariables. + ( + t, + ~context: Js.Json.t=?, + ~errorPolicy: ErrorPolicy.t=?, + ~fetchPolicy: FetchPolicy.t=?, + ~variables: variables=?, + (module Types.Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + Js.Promise.t(ApolloQueryResult.t(data)) = + ( + client, + ~context=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~variables=?, + (module Operation), + ) => { + Js_.query( + client, + ~options= + QueryOptions.toJs({ + fetchPolicy, + query: GraphqlTag.gql(Operation.query), + variables, + errorPolicy, + context, + }), + ) + ->Js.Promise.then_( + jsResult => + jsResult + ->ApolloQueryResult.fromJs(_, ~parse=Operation.parse) + ->Js.Promise.resolve, + _, + ); + }; + +let readQuery: + type data variables jsData jsVariables. + ( + t, + ~id: string=?, + ~optimistic: bool=?, + ~variables: variables=?, + (module Types.Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + option(data) = + (client, ~id=?, ~optimistic=?, ~variables=?, (module Operation)) => { + Js_.readQuery( + client, + ~options={id, query: GraphqlTag.gql(Operation.query), variables}, + ~optimistic, + ) + ->Js.toOption + ->Belt.Option.map(Operation.parse); + }; + +let writeQuery: + type data variables jsData jsVariables. + ( + t, + ~broadcast: bool=?, + ~data: data, + ~id: string=?, + ~variables: variables=?, + (module Types.Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + unit = + (client, ~broadcast=?, ~data, ~id=?, ~variables=?, (module Operation)) => { + Js_.writeQuery( + client, + ~options={ + broadcast, + data: data->Operation.serialize, + id, + query: GraphqlTag.gql(Operation.query), + variables, + }, + ); + }; diff --git a/src/ApolloHooks.re b/src/ApolloHooks.re deleted file mode 100644 index 5d493c1..0000000 --- a/src/ApolloHooks.re +++ /dev/null @@ -1,163 +0,0 @@ -module Mutation = ApolloHooksMutation; -module Query = ApolloHooksQuery; -module Provider = ApolloHooksProvider; -module Subscription = ApolloHooksSubscription; - -/** - This is probably the one hook you'll use the most. A quick demo: - - {[ - open ApolloHooks; - - module Query = [%graphql {| - query MyQuery { - me { id, name } - } - |}]; - - [@react.component] - let make = () => { - /* In Reason we prefix variables that we are not going to use with _ */ - let (simple, _full) = useQuery(Query.definitions); - - /* When using simple with Reason's pattern-matching operator, the compiler will force you to cover every single branch of the variant type */ - switch(simple) { - | Loading => React.string("loading...") - | Error(error) => - Js.log(error); - React.string("Something went wrong!") - | Data(data) => - React.string("Hello, " ++ data##me##name) - /* Every. Single. One. Of Them. */ - | NoData => - React.string("Woa something went really wrong! Glady we use Reason and it forced us to handle this! Report this issue") - } - } - ]} - - Why we return a tuple? While designing and using the API we came to the conclusion that would be much more convient to have a value that would attend - the majority of simple usages and a full for when you need to do a complex UI, such as infinite scroll. - - The value [simple] ({!type:Query.variant('a)}) helps you to consume your data with simplicity, type safety and exhaustiveness check. - But for those cases where you really want do do a fine-grained control of your data flow – such as when you have [loading] and [data] at the same time – - that's when [full] ({!type:Query.queryResult('a)}) becomes more useful. - - {[ - module Query = [%graphql {| - query MyQuery { - me { id, name } - } - |}]; - - [@react.component] - let make = () => { - let (_simple, full) = useQuery(Query.definitions); - - /* `full` is a record type so you pattern against it's possible combos of values */ - switch(full) { - /* Initial loading */ - | { loading: true, data: None } => React.string("loading...") - /* Error but no data */ - | { loading: false, data: None, error: Some(error) } => React.string("Something went wrong") - /* When we have some data and we tried to refetch but got an error */ - | { loading: false, data: Some(data), error: Some(error) } => - <> - {React.string("Something went wrong")} - - - /* Just data */ - | { loading: false, data: Some(data), error: None } => - <> - {React.string("Something went wrong")} - - - | Data(data) => - React.string("Hello, " ++ data##me##name) - /* Not loading? No data? No error? That's weird */ - | {loading: false, data: None, error: null} => - React.string("Woa something went really wrong! But the programmer remembered to handle this case! Report to us") - } - } - ]} - - Quite more complex right? Gladly it's not always that we have that level of complexity. - - That covers the most common cases of usage. If you want to see more complex usages check out the examples folder. - */ -let useQuery = Query.useQuery; - -/** - Second most used! Here's a quick demo: - - {[ - open ApolloHooks; - - module Mutation = [%graphql {| - mutation MyMutation($input: MyMutationInput!) { - myMutation(input: $input) { error } - } - |}]; - - [@react.component] - let make = () => { - /* `simple` and `full` follow the same principle of `useQuery`. */ - let (mutate, simple, _full) = useMutation(Mutation.definitions); - - /* When using simple with Reason's pattern-matching operator, the compiler will force you to cover every single branch of the variant type */ - switch(simple) { - | Loading => React.string("loading...") - | Error(error) => - Js.log(error); - React.string("Something went wrong!") - | Data(data) => -
- {React.string("Hello, " ++ data##me##name)} -
- /* Every. Single. One. Of Them. */ - | NotCalled => - } - ]} - */ -let useMutation = Mutation.useMutation; - -/** useSubscription bindings */ -let useSubscription = Subscription.useSubscription; - -/** Helper to generate the shape of a query for [refetchQueries] mutation param. Take a look in examples/persons/src/EditPerson.re for a more complete demo of usage. */ -let toQueryObj = result => - ApolloClient.{ - query: ApolloClient.gql(. result##query), - variables: result##variables, - }; diff --git a/src/ApolloHooksMutation.re b/src/ApolloHooksMutation.re deleted file mode 100644 index 8c10eff..0000000 --- a/src/ApolloHooksMutation.re +++ /dev/null @@ -1,208 +0,0 @@ -open ApolloHooksTypes; - -type jsResult = { - . - "data": Js.Nullable.t(Js.Json.t), - "loading": bool, - "called": bool, - "error": Js.Nullable.t(apolloError), -}; - -type jsExecutionResult = { - . - "data": Js.Nullable.t(Js.Json.t), - "errors": Js.Nullable.t(array(graphqlError)), -}; - -type refetchQueries = jsExecutionResult => array(ApolloClient.queryObj); - -/* The type that the promise returned by the mutate function resolves to */ -type executionResult('a) = { - data: option('a), - errors: option(array(graphqlError)), -}; - -type executionVariantResult('a) = - | Data('a) - | Errors(array(graphqlError)) - | NoData; - -/* The type of the 'full' result returned by the hook */ -type controlledResult('a) = { - loading: bool, - called: bool, - data: option('a), - error: option(apolloError), -}; - -/* The type of the 'simple' result returned by the hook */ -type controlledVariantResult('a) = - | Loading - | NotCalled - | Data('a) - | Error(apolloError) - | NoData; - -[@bs.module "graphql-tag"] external gql: ReasonApolloTypes.gql = "default"; - -type mutationResult('a) = {. "data": option('a)}; - -[@bs.deriving abstract] -type options('a) = { - [@bs.optional] - variables: Js.Json.t, - [@bs.optional] - mutation: option(ReasonApolloTypes.queryString), - [@bs.optional] - client: ApolloClient.generatedApolloClient, - [@bs.optional] - refetchQueries, - [@bs.optional] - awaitRefetchQueries: bool, - [@bs.optional] - update: (ApolloClient.generatedApolloClient, mutationResult('a)) => unit, - [@bs.optional] - optimisticResponse: Js.Json.t, - [@bs.optional] - context: Context.t, -}; - -type jsMutate('a) = (. options('a)) => Js.Promise.t(jsExecutionResult); - -type mutation('a) = - ( - ~variables: Js.Json.t=?, - ~client: ApolloClient.generatedApolloClient=?, - ~refetchQueries: refetchQueries=?, - ~awaitRefetchQueries: bool=?, - ~optimisticResponse: Js.Json.t=?, - unit - ) => - Js.Promise.t((executionVariantResult('a), executionResult('a))); - -[@bs.module "@apollo/react-hooks"] -external useMutationJs: - (. ReasonApolloTypes.queryString, options('a)) => (jsMutate('a), jsResult) = - "useMutation"; - -exception Error(string); - -let useMutation: - ( - ~client: ApolloClient.generatedApolloClient=?, - ~variables: Js.Json.t=?, - ~refetchQueries: refetchQueries=?, - ~awaitRefetchQueries: bool=?, - ~update: (ApolloClient.generatedApolloClient, mutationResult('data)) => - unit - =?, - ~optimisticResponse: Js.Json.t=?, - ~context: Context.t=?, - ApolloHooksTypes.graphqlDefinition('data, _, _) - ) => - ( - mutation('data), - controlledVariantResult('data), - controlledResult('data), - ) = - ( - ~client=?, - ~variables=?, - ~refetchQueries=?, - ~awaitRefetchQueries=?, - ~update=?, - ~optimisticResponse=?, - ~context=?, - (parse, query, _), - ) => { - let (jsMutate, jsResult) = - useMutationJs(. - gql(. query), - options( - ~client?, - ~variables?, - ~refetchQueries?, - ~awaitRefetchQueries?, - ~update?, - ~optimisticResponse?, - ~context?, - (), - ), - ); - - let mutate = - React.useMemo1( - ( - (), - ~variables=?, - ~client=?, - ~refetchQueries=?, - ~awaitRefetchQueries=?, - ~optimisticResponse=?, - (), - ) => - jsMutate(. - options( - ~variables?, - ~client?, - ~refetchQueries?, - ~awaitRefetchQueries?, - ~optimisticResponse?, - (), - ), - ) - |> Js.Promise.then_(jsResult => { - let full = { - data: - Js.Nullable.toOption(jsResult##data) - ->Belt.Option.map(parse), - errors: - switch (Js.Nullable.toOption(jsResult##errors)) { - | Some(errors) when Js.Array.length(errors) > 0 => - Some(errors) - | _ => None - }, - }; - - let simple = - switch (full) { - | {errors: Some(errors)} => ( - Errors(errors): executionVariantResult('data) - ) - | {data: Some(data)} => Data(data) - | {errors: None, data: None} => NoData - }; - - (simple, full) |> Js.Promise.resolve; - }), - [|variables|], - ); - - let full = - React.useMemo1( - () => - { - loading: jsResult##loading, - called: jsResult##called, - data: - jsResult##data->Js.Nullable.toOption->Belt.Option.map(parse), - error: jsResult##error->Js.Nullable.toOption, - }, - [|jsResult|], - ); - - let simple = - React.useMemo1( - () => - switch (full) { - | {loading: true} => Loading - | {error: Some(error)} => Error(error) - | {data: Some(data)} => Data(data) - | {called: false} => NotCalled - | _ => NoData - }, - [|full|], - ); - - (mutate, simple, full); - }; diff --git a/src/ApolloHooksProvider.re b/src/ApolloHooksProvider.re deleted file mode 100644 index 7ad79c1..0000000 --- a/src/ApolloHooksProvider.re +++ /dev/null @@ -1,5 +0,0 @@ -[@bs.module "@apollo/react-hooks"] [@react.component] -external make: - (~client: ApolloClient.generatedApolloClient, ~children: React.element) => - React.element = - "ApolloProvider"; \ No newline at end of file diff --git a/src/ApolloHooksQuery.re b/src/ApolloHooksQuery.re deleted file mode 100644 index 0357526..0000000 --- a/src/ApolloHooksQuery.re +++ /dev/null @@ -1,220 +0,0 @@ -open ApolloHooksTypes; - -type variant('a) = - | Data('a) - | Error(apolloError) - | Loading - | NoData; - -/** - * - * apollo-client/src/core/ObservableQuery.ts - */ -[@bs.deriving abstract] -type updateQueryOptions = { - [@bs.optional] - fetchMoreResult: Js.Json.t, - [@bs.optional] - variables: Js.Json.t, -}; - -type updateQueryT = (Js.Json.t, updateQueryOptions) => Js.Json.t; - -/** - * https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/watchQueryOptions.ts#L139 - */ -type updateSubscriptionOptionsJs = { - . - "subscriptionData": {. "data": Js.Json.t}, - "variables": Js.Nullable.t(Js.Json.t), -}; - -type updateQuerySubscribeToMoreT = - (Js.Json.t, updateSubscriptionOptionsJs) => Js.Json.t; - -[@bs.deriving abstract] -type subscribeToMoreOptionsJs = { - document: ReasonApolloTypes.queryString, - [@bs.optional] - variables: Js.Json.t, - [@bs.optional] - updateQuery: updateQuerySubscribeToMoreT, -}; - -type unsubscribeFnT = unit => unit; - -type refetch('a) = (~variables: Js.Json.t=?, unit) => Js.Promise.t('a); -type queryResult('a) = { - data: option('a), - loading: bool, - error: option(apolloError), - refetch: refetch('a), - fetchMore: - (~variables: Js.Json.t=?, ~updateQuery: updateQueryT, unit) => - Js.Promise.t(unit), - networkStatus: ApolloHooksTypes.networkStatus, - startPolling: int => unit, - stopPolling: unit => unit, - subscribeToMore: - ( - ~document: ReasonApolloTypes.queryString, - ~variables: Js.Json.t=?, - ~updateQuery: updateQuerySubscribeToMoreT=?, - unit - ) => - unsubscribeFnT, -}; - -/** - * apollo-client/src/core/watchQueryOptions.ts - */ -[@bs.deriving abstract] -type fetchMoreOptions = { - [@bs.optional] - variables: Js.Json.t, - updateQuery: updateQueryT, -}; - -[@bs.module "graphql-tag"] external gql: ReasonApolloTypes.gql = "default"; - -[@bs.deriving abstract] -type options = { - [@bs.optional] - variables: Js.Json.t, - [@bs.optional] - client: ApolloClient.generatedApolloClient, - [@bs.optional] - notifyOnNetworkStatusChange: bool, - [@bs.optional] - fetchPolicy: string, - [@bs.optional] - errorPolicy: string, - [@bs.optional] - skip: bool, - [@bs.optional] - pollInterval: int, - [@bs.optional] - context: Context.t, -}; - -[@bs.module "@apollo/react-hooks"] -external useQueryJs: - (ReasonApolloTypes.queryString, options) => - { - . - "data": Js.Nullable.t(Js.Json.t), - "loading": bool, - "error": Js.Nullable.t(apolloError), - [@bs.meth] - "refetch": Js.Nullable.t(Js.Json.t) => Js.Promise.t(Js.Json.t), - [@bs.meth] "fetchMore": fetchMoreOptions => Js.Promise.t(unit), - "networkStatus": Js.Nullable.t(int), - [@bs.meth] "stopPolling": unit => unit, - [@bs.meth] "startPolling": int => unit, - [@bs.meth] "subscribeToMore": subscribeToMoreOptionsJs => unsubscribeFnT, - } = - "useQuery"; - -let useQuery: - ( - ~client: ApolloClient.generatedApolloClient=?, - ~variables: Js.Json.t=?, - ~notifyOnNetworkStatusChange: bool=?, - ~fetchPolicy: ApolloHooksTypes.fetchPolicy=?, - ~errorPolicy: ApolloHooksTypes.errorPolicy=?, - ~skip: bool=?, - ~pollInterval: int=?, - ~context: Context.t=?, - graphqlDefinition('data, _, _) - ) => - (variant('data), queryResult('data)) = - ( - ~client=?, - ~variables=?, - ~notifyOnNetworkStatusChange=?, - ~fetchPolicy=?, - ~errorPolicy=?, - ~skip=?, - ~pollInterval=?, - ~context=?, - (parse, query, _), - ) => { - let jsResult = - useQueryJs( - gql(. query), - options( - ~variables?, - ~client?, - ~notifyOnNetworkStatusChange?, - ~fetchPolicy=? - fetchPolicy->Belt.Option.map(ApolloHooksTypes.fetchPolicyToJs), - ~errorPolicy=? - errorPolicy->Belt.Option.map(ApolloHooksTypes.errorPolicyToJs), - ~skip?, - ~pollInterval?, - ~context?, - (), - ), - ); - - let getData = obj => - obj - ->Js.Json.decodeObject - ->Belt.Option.flatMap(x => Js.Dict.get(x, "data")) - ->Belt.Option.getExn; - - let result = - React.useMemo1( - () => - { - data: - jsResult##data - ->Js.Nullable.toOption - ->Belt.Option.flatMap(data => - switch (parse(data)) { - | parsedData => Some(parsedData) - | exception _ => None - } - ), - loading: jsResult##loading, - error: jsResult##error->Js.Nullable.toOption, - networkStatus: - ApolloHooksTypes.toNetworkStatus(jsResult##networkStatus), - refetch: (~variables=?, ()) => - jsResult##refetch(Js.Nullable.fromOption(variables)) - |> Js.Promise.then_(result => - parse(result->getData) |> Js.Promise.resolve - ), - fetchMore: (~variables=?, ~updateQuery, ()) => - jsResult##fetchMore( - fetchMoreOptions(~variables?, ~updateQuery, ()), - ), - stopPolling: () => jsResult##stopPolling(), - startPolling: interval => jsResult##startPolling(interval), - subscribeToMore: (~document, ~variables=?, ~updateQuery=?, ()) => - jsResult##subscribeToMore( - subscribeToMoreOptionsJs( - ~document, - ~variables?, - ~updateQuery?, - (), - ), - ), - }, - [|jsResult|], - ); - - let simple = - React.useMemo1( - () => - switch (result) { - | {loading: true} => Loading - | {error: Some(error)} => Error(error) - | {data: Some(data)} => Data(data) - | _ => NoData - }, - [|result|], - ); - - (simple, result); - }; diff --git a/src/ApolloHooksSubscription.re b/src/ApolloHooksSubscription.re deleted file mode 100644 index 81aea7a..0000000 --- a/src/ApolloHooksSubscription.re +++ /dev/null @@ -1,70 +0,0 @@ -type error = {. "message": string}; - -type variant('a) = - | Data('a) - | Error(error) - | Loading - | NoData; - -type result('a) = { - data: option('a), - loading: bool, - error: option(error), -}; - -[@bs.module "graphql-tag"] external gql: ReasonApolloTypes.gql = "default"; - -[@bs.deriving abstract] -type options = { - [@bs.optional] - variables: Js.Json.t, - [@bs.optional] - skip: bool, - [@bs.optional] - onSubscriptionData: unit => unit, - [@bs.optional] - client: ApolloClient.generatedApolloClient, -}; - -[@bs.module "@apollo/react-hooks"] -external useSubscriptionJs: - (ReasonApolloTypes.queryString, options) => - { - . - "data": Js.Nullable.t(Js.Json.t), - "loading": bool, - "error": Js.Nullable.t(error), - } = - "useSubscription"; - -let useSubscription: - ( - ~variables: Js.Json.t=?, - ~client: ApolloClient.generatedApolloClient=?, - ~skip: bool=?, - ApolloHooksTypes.graphqlDefinition('data, _, _) - ) => - (variant('data), result('data)) = - (~variables=?, ~client=?, ~skip=?, (parse, query, _)) => { - let jsResult = - useSubscriptionJs( - gql(. query), - options(~variables?, ~client?, ~skip?, ()), - ); - - let result = { - data: jsResult##data->Js.Nullable.toOption->Belt.Option.map(parse), - loading: jsResult##loading, - error: jsResult##error->Js.Nullable.toOption, - }; - - ( - switch (result) { - | {data: Some(data)} => Data(data) - | {error: Some(error)} => Error(error) - | {loading: true} => Loading - | _ => NoData - }, - result, - ); - }; diff --git a/src/ApolloHooksTypes.re b/src/ApolloHooksTypes.re deleted file mode 100644 index cd27d31..0000000 --- a/src/ApolloHooksTypes.re +++ /dev/null @@ -1,100 +0,0 @@ -/** - * apollo-client/src/core/networkStatus - */ -type networkStatus = - | Loading - | SetVariables - | FetchMore - | Refetch - | Poll - | Ready - | Error - | Unknown; - -let toNetworkStatus = (status: Js.Nullable.t(int)) => { - switch (status->Js.Nullable.toOption) { - | Some(1) => Loading - | Some(2) => SetVariables - | Some(3) => FetchMore - | Some(4) => Refetch - | Some(6) => Poll - | Some(7) => Ready - | Some(8) => Error - | _ => Unknown - }; -}; - -/** - * apollo-client/src/core/watchQueryOptions.ts - */ -type fetchPolicy = - | CacheFirst - | CacheAndNetwork - | NetworkOnly - | CacheOnly - | NoCache - | Standby; - -let fetchPolicyToJs = fetchPolicy => { - switch (fetchPolicy) { - | CacheFirst => "cache-first" - | CacheAndNetwork => "cache-and-network" - | NetworkOnly => "network-only" - | CacheOnly => "cache-only" - | NoCache => "no-cache" - | Standby => "standby" - }; -}; - -/** - * apollo-client/src/core/watchQueryOptions.ts - */ -type errorPolicy = - | None - | Ignore - | All; - -let errorPolicyToJs = errorPolicy => - switch (errorPolicy) { - | None => "none" - | Ignore => "ignore" - | All => "all" - }; - -/** - * apollo-client/src/errors/ApolloError.ts - */ -type apolloErrorExtensions = {. "code": Js.Nullable.t(string)}; - -type graphqlError = { - . - "message": string, - "name": Js.Nullable.t(string), - "extensions": Js.Nullable.t(apolloErrorExtensions), - "locations": Js.Nullable.t(array(string)), - "path": Js.Nullable.t(array(string)), - "nodes": Js.Nullable.t(array(string)), -}; - -type apolloError = { - . - "message": string, - "graphQLErrors": Js.Nullable.t(array(graphqlError)), - "networkError": Js.Nullable.t(string), -}; - -type parse('a) = Js.Json.t => 'a; -type query = string; -type composeVariables('returnType, 'hookReturnType) = - (Js.Json.t => 'returnType) => 'hookReturnType; - -type graphqlDefinition('data, 'returnType, 'hookReturnType) = ( - parse('data), - query, - composeVariables('returnType, 'hookReturnType), -); - -module Context = { - type t = Js.Dict.t(string); - let make = (context): t => Js.Dict.fromList(context); -}; diff --git a/src/EXAMPLES/AMinimalClient.re b/src/EXAMPLES/AMinimalClient.re new file mode 100644 index 0000000..dd68929 --- /dev/null +++ b/src/EXAMPLES/AMinimalClient.re @@ -0,0 +1,74 @@ +let graphqlEndpoint = ""; + +let headers = {"high": "five"}; + +let httpLink = + ApolloClient.HttpLink.make( + ~uri=_ => "http://" ++ graphqlEndpoint, + ~credentials="include", + ~headers=Obj.magic(headers), + (), + ); + +let wsLink = + ApolloLinkWs.( + WebSocketLink.make( + ~uri="ws://" ++ graphqlEndpoint, + ~options= + ClientOptions.make( + ~connectionParams= + ConnectionParams(Obj.magic({"headers": headers})), + ~reconnect=true, + (), + ), + (), + ) + ); + +let terminatingLink = + ApolloClient.split( + ~test= + ({query}) => { + let definition = ApolloClient.Utilities.getOperationDefinition(query); + switch (definition) { + | Some({kind, operation}) => + kind === "OperationDefinition" && operation === "subscription" + | None => false + }; + }, + ~right=httpLink, + ~left=wsLink, + ); + +let apolloClient = + ApolloClient.ApolloClient.( + make( + ~cache=ApolloClient.InMemoryCache.make(), + ~connectToDevTools=true, + ~defaultOptions= + DefaultOptions.make( + ~mutate= + DefaultOptions.DefaultMutationOptions.make( + ~awaitRefetchQueries=true, + ~fetchPolicy=NetworkOnly, + ~errorPolicy=All, + (), + ), + ~query= + DefaultOptions.DefaultQueryOptions.make( + ~fetchPolicy=NetworkOnly, + ~errorPolicy=All, + (), + ), + ~watchQuery= + DefaultOptions.DefaultWatchQueryOptions.make( + ~fetchPolicy=NetworkOnly, + ~errorPolicy=All, + (), + ), + (), + ), + ~link=terminatingLink, + (), + ) + ); diff --git a/src/EXAMPLES/bsconfig_extension.json b/src/EXAMPLES/bsconfig_extension.json new file mode 100644 index 0000000..f77b583 --- /dev/null +++ b/src/EXAMPLES/bsconfig_extension.json @@ -0,0 +1,8 @@ +{ + "graphql": { + "extend-query": "ApolloClient.Extend.Query", + "extend-query-no-required-variables": "ApolloClient.Extend.QueryNoRequiredVariables", + "extend-subscription": "ApolloClient.Extend.Subscription", + "extend-subscription-no-required-variables": "ApolloClient.Extend.SubscriptionNoRequiredVariables" + } +} diff --git a/src/_dependencies/graphql-tag/ApolloClient__GraphqlTag.re b/src/_dependencies/graphql-tag/ApolloClient__GraphqlTag.re new file mode 100644 index 0000000..5dac315 --- /dev/null +++ b/src/_dependencies/graphql-tag/ApolloClient__GraphqlTag.re @@ -0,0 +1,3 @@ +[@bs.module "graphql-tag"] +external gql: string => ApolloClient__Graphql_Language.documentNode = + "default"; diff --git a/src/_dependencies/zen-observable/ApolloClient__ZenObservable.re b/src/_dependencies/zen-observable/ApolloClient__ZenObservable.re new file mode 100644 index 0000000..329f1eb --- /dev/null +++ b/src/_dependencies/zen-observable/ApolloClient__ZenObservable.re @@ -0,0 +1,77 @@ +module ZenObservable = { + // interface SubscriptionObserver { + // closed: boolean; + // next(value: T): void; + // error(errorValue: any): void; + // complete(): void; + // } + + // type Subscriber = (observer: SubscriptionObserver) => void | (() => void) | Subscription; + + // interface ObservableLike { + // subscribe?: Subscriber; + // [Symbol.observable](): Observable | ObservableLike; + // } + + // interface Subscription { + // closed: boolean; + // unsubscribe(): void; + // } + type subscription = { + closed: bool, + unsubscribe: unit => unit, + }; + + // interface Observer { + // start?(subscription: Subscription): any; + // next?(value: T): void; + // error?(errorValue: any): void; + // complete?(): void; + // } + type observer('t) = { + start: option(subscription => unit), + next: option('t => unit), + error: option(Js.Json.t => unit), + complete: option(unit => unit), + }; +}; + +module Observable = { + // declare class Observable { + // constructor(subscriber: ZenObservable.Subscriber) + + // subscribe(observer: ZenObservable.Observer): ZenObservable.Subscription; + // subscribe(onNext: (value: T) => void, onError?: (error: any) => void, onComplete?: () => void): ZenObservable.Subscription; + + // [Symbol.observable](): Observable; + + // forEach(callback: (value: T) => void): Promise; + // map(callback: (value: T) => R): Observable; + // filter(callback: (value: T) => boolean): Observable; + // reduce(callback: (previousValue: T, currentValue: T) => T, initialValue?: T): Observable; + // reduce(callback: (previousValue: R, currentValue: T) => R, initialValue?: R): Observable; + // flatMap(callback: (value: T) => ZenObservable.ObservableLike): Observable; + // concat(...observable: Array>): Observable; + + // static from(observable: Observable | ZenObservable.ObservableLike | ArrayLike): Observable; + // static of(...items: R[]): Observable; + // } + type t('t); + + [@bs.send] + external subscribe: + ( + t('t), + ~onNext: 't => unit, + ~onError: Js.Json.t => unit=?, + ~onComplete: unit => unit=?, + unit + ) => + ZenObservable.subscription = + "subscribe"; + + [@bs.send] + external subscribeWithObserver: + (t('t), ZenObservable.observer('t)) => ZenObservable.subscription = + "subscribe"; +}; diff --git a/src/_notDependencies/@apollo/link-ws/ApolloLinkWs.re b/src/_notDependencies/@apollo/link-ws/ApolloLinkWs.re new file mode 100644 index 0000000..f90af9c --- /dev/null +++ b/src/_notDependencies/@apollo/link-ws/ApolloLinkWs.re @@ -0,0 +1,69 @@ +module ApolloLink = ApolloClient__Link_Core_ApolloLink; +module ClientOptions = SubscriptionsTransportWs.ClientOptions; +module SubscriptionClient = SubscriptionsTransportWs.SubscriptionClient; + +module WebSocketLink = { + module Configuration = { + module Js_ = { + // export declare namespace WebSocketLink { + // interface Configuration { + // uri: string; + // options?: ClientOptions; + // webSocketImpl?: any; + // } + // } + type any; + type t = { + uri: string, + options: option(ClientOptions.Js_.t), + webSocketImpl: option(any), + }; + }; + + type t = Js_.t; + }; + + module Js_ = { + // export declare class WebSocketLink extends ApolloLink { + // private subscriptionClient; + // constructor(paramsOrClient: WebSocketLink.Configuration | SubscriptionClient); + // request(operation: Operation): Observable | null; + // } + [@bs.module "@apollo/link-ws"] [@bs.new] + external make: + ( + [@bs.unwrap] + [ + | `Configuration(Configuration.Js_.t) + | `SubscriptionClient(SubscriptionClient.Js_.t) + ] + ) => + ApolloLink.Js_.t = + "WebSocketLink"; + }; + + type webSocketImpl = Configuration.Js_.any; + + let make: + ( + ~uri: string, + ~options: ClientOptions.t=?, + ~webSocketImpl: webSocketImpl=?, + unit + ) => + ApolloLink.t = + (~uri, ~options=?, ~webSocketImpl=?, ()) => { + Js_.make( + `Configuration({ + uri, + options: options->Belt.Option.map(ClientOptions.toJs), + webSocketImpl, + }), + ); + }; + + let make_withSubscriptionClient: SubscriptionClient.t => ApolloLink.t = + subscriptionClient => { + Js_.make(`SubscriptionClient(subscriptionClient)); + }; +}; diff --git a/src/_notDependencies/subscriptions-transport-ws/SubscriptionsTransportWs.re b/src/_notDependencies/subscriptions-transport-ws/SubscriptionsTransportWs.re new file mode 100644 index 0000000..cb14659 --- /dev/null +++ b/src/_notDependencies/subscriptions-transport-ws/SubscriptionsTransportWs.re @@ -0,0 +1,170 @@ +// import { ListenerFn } from 'eventemitter3'; +// import { ExecutionResult } from 'graphql/execution/execute'; +// import { DocumentNode } from 'graphql/language/ast'; +// export interface Observer { +// next?: (value: T) => void; +// error?: (error: Error) => void; +// complete?: () => void; +// } +// export interface Observable { +// subscribe(observer: Observer): { +// unsubscribe: () => void; +// }; +// } +// export interface OperationOptions { +// query?: string | DocumentNode; +// variables?: Object; +// operationName?: string; +// [key: string]: any; +// } +// export declare type FormatedError = Error & { +// originalError?: any; +// }; +// export interface Operation { +// options: OperationOptions; +// handler: (error: Error[], result?: any) => void; +// } +// export interface Operations { +// [id: string]: Operation; +// } +// export interface Middleware { +// applyMiddleware(options: OperationOptions, next: Function): void; +// } +module ConnectionParams = { + module Js_ = { + // export declare type ConnectionParams = { + // [paramName: string]: any; + // }; + type t = Js.Json.t; + }; + + type t = Js_.t; +}; + +module ConnectionParamsOptions = { + module Js_ = { + module Union: { + type t; + let connectionParams: ConnectionParams.Js_.t => t; + let fn: (unit => ConnectionParams.Js_.t) => t; + let promise: Js.Promise.t(ConnectionParams.Js_.t) => t; + } = { + [@unboxed] + type t = + | Any('a): t; + let connectionParams = (v: ConnectionParams.Js_.t) => Any(v); + let fn = (v: unit => ConnectionParams.Js_.t) => Any(v); + let promise = (v: Js.Promise.t(ConnectionParams.Js_.t)) => Any(v); + }; + // export declare type ConnectionParamsOptions = ConnectionParams | Function | Promise; + type t = Union.t; + }; + + type t = + | ConnectionParams(ConnectionParams.t) + | Function(unit => ConnectionParams.t) + | Promise(Js.Promise.t(ConnectionParams.t)); + + let toJs: t => Js_.t = + fun + | ConnectionParams(v) => Js_.Union.connectionParams(v) + | Function(v) => Js_.Union.fn(v) + | Promise(v) => Js_.Union.promise(v); +}; + +module ClientOptions = { + module Js_ = { + // export interface ClientOptions { + // connectionParams?: ConnectionParamsOptions; + // timeout?: number; + // reconnect?: boolean; + // reconnectionAttempts?: number; + // connectionCallback?: (error: Error[], result?: any) => void; + // lazy?: boolean; + // inactivityTimeout?: number; + // } + type t = { + connectionParams: option(ConnectionParamsOptions.Js_.t), + timeout: option(int), + reconnect: option(bool), + reconnectionAttempts: option(int), + connectionCallback: + option( + (~error: array(Js.Exn.t), ~result: option(Js.Json.t)) => unit, + ), + [@bs.as "lazy"] + lazy_: option(bool), + inactivityTimeout: option(int), + }; + }; + + type t = { + connectionParams: option(ConnectionParamsOptions.t), + timeout: option(int), + reconnect: option(bool), + reconnectionAttempts: option(int), + connectionCallback: + option((~error: array(Js.Exn.t), ~result: option(Js.Json.t)) => unit), + [@bs.as "lazy"] + lazy_: option(bool), + inactivityTimeout: option(int), + }; + + let toJs: t => Js_.t = + t => { + connectionParams: + t.connectionParams->Belt.Option.map(ConnectionParamsOptions.toJs), + timeout: t.timeout, + reconnect: t.reconnect, + reconnectionAttempts: t.reconnectionAttempts, + connectionCallback: t.connectionCallback, + lazy_: t.lazy_, + inactivityTimeout: t.inactivityTimeout, + }; + + let make = + ( + ~connectionParams=?, + ~timeout=?, + ~reconnect=?, + ~reconnectionAttempts=?, + ~connectionCallback=?, + ~lazy_=?, + ~inactivityTimeout=?, + (), + ) + : t => { + connectionParams, + timeout, + reconnect, + reconnectionAttempts, + connectionCallback, + lazy_, + inactivityTimeout, + }; +}; + +module SubscriptionClient = { + module Js_ = { + // export declare class SubscriptionClient { + // client: any; + // operations: Operations; + // constructor(url: string, options?: ClientOptions, webSocketImpl?: any, webSocketProtocols?: string | string[]); + // readonly status: any; + // close(isForced?: boolean, closedByUser?: boolean): void; + // request(request: OperationOptions): Observable; + // on(eventName: string, callback: ListenerFn, context?: any): Function; + // onConnected(callback: ListenerFn, context?: any): Function; + // onConnecting(callback: ListenerFn, context?: any): Function; + // onDisconnected(callback: ListenerFn, context?: any): Function; + // onReconnected(callback: ListenerFn, context?: any): Function; + // onReconnecting(callback: ListenerFn, context?: any): Function; + // onError(callback: ListenerFn, context?: any): Function; + // unsubscribeAll(): void; + // applyMiddlewares(options: OperationOptions): Promise; + // use(middlewares: Middleware[]): SubscriptionClient; + // } + type t; + }; + type t = Js_.t; +}; diff --git a/src/_peerDependencies/graphql/ApolloClient__Graphql.re b/src/_peerDependencies/graphql/ApolloClient__Graphql.re new file mode 100644 index 0000000..01040a9 --- /dev/null +++ b/src/_peerDependencies/graphql/ApolloClient__Graphql.re @@ -0,0 +1,7 @@ +module Error = ApolloClient__Graphql_Error; +module Execution = ApolloClient__Graphql_Execution; +module GraphQLError = ApolloClient__Graphql_Error_GraphQLError; +module Language = ApolloClient__Graphql_Language; +module Location = ApolloClient__Graphql_Location; + +type documentNode = Language.documentNode; diff --git a/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error.re b/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error.re new file mode 100644 index 0000000..ff94561 --- /dev/null +++ b/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error.re @@ -0,0 +1 @@ +module GraphQLError = ApolloClient__Graphql_Error_GraphQLError; diff --git a/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error_GraphQLError.re b/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error_GraphQLError.re new file mode 100644 index 0000000..56ebf5a --- /dev/null +++ b/src/_peerDependencies/graphql/error/ApolloClient__Graphql_Error_GraphQLError.re @@ -0,0 +1,8 @@ +module Graphql_Location = ApolloClient__Graphql_Location; + +type t = { + message: string, + locations: option(array(Graphql_Location.sourceLocation)), + // Union? https://bucklescript.github.io/blog/2020/02/07/union-types-in-bucklescript + path: option(array(string)) // ACTUAL: string | number +}; diff --git a/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution.re b/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution.re new file mode 100644 index 0000000..c2dd08e --- /dev/null +++ b/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution.re @@ -0,0 +1 @@ +module Execute = ApolloClient__Graphql_Execution_Execute; diff --git a/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution_Execute.re b/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution_Execute.re new file mode 100644 index 0000000..5bbcb0f --- /dev/null +++ b/src/_peerDependencies/graphql/execution/ApolloClient__Graphql_Execution_Execute.re @@ -0,0 +1,8 @@ +module GraphQLError = ApolloClient__Graphql_Error_GraphQLError; + +module ExecutionResult = { + type t('tData) = { + errors: option(GraphQLError.t), + data: option('tData), + }; +}; diff --git a/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language.re b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language.re new file mode 100644 index 0000000..4df1af3 --- /dev/null +++ b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language.re @@ -0,0 +1,3 @@ +module Ast = ApolloClient__Graphql_Language_Ast; + +type documentNode; diff --git a/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Ast.re b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Ast.re new file mode 100644 index 0000000..ea567b1 --- /dev/null +++ b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Ast.re @@ -0,0 +1,177 @@ +module Source = ApolloClient__Graphql_Language_Source; + +// export interface FieldNode { +// readonly kind: 'Field'; +// readonly loc?: Location; +// readonly alias?: NameNode; +// readonly name: NameNode; +// readonly arguments?: ReadonlyArray; +// readonly directives?: ReadonlyArray; +// readonly selectionSet?: SelectionSetNode; +// } + +module TokenKindEnum = { + [@bs.deriving jsConverter] + type t = [ + | `SOF + | `EOF + | `BANG + | `DOLLAR + | `AMP + | `PAREN_L + | `PAREN_R + | `SPREAD + | `COLON + | `EQUALS + | `AT + | `BRACKET_L + | `BRACKET_R + | `BRACE_L + | `PIPE + | `BRACE_R + | `NAME + | `INT + | `FLOAT + | `STRING + | `BLOCK_STRING + | `COMMENT + ]; +}; + +module Token = { + type t = { + kind: TokenKindEnum.t, + start: int, + [@bs.as "end"] + end_: int, + line: int, + column: int, + value: option(string), + prev: Js.nullable(t), + next: Js.nullable(t), + }; +}; + +module Location = { + type t = { + start: int, + [@bs.as "end"] + end_: int, + startToken: Token.t, + endToken: Token.t, + source: Source.t, + }; +}; + +module NameNode = { + type t = { + kind: string, + loc: option(Location.t), + value: string, + }; +}; + +module NamedTypeNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + }; +}; + +module ValueNode = { + type t; +}; + +module ArgumentNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + value: ValueNode.t, + }; +}; + +module DirectiveNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + arguments: option(array(ArgumentNode.t)), + }; +}; + +module SelectionNode = { + type t; +}; + +module SelectionSetNode = { + type t = { + kind: string, + loc: option(Location.t), + selections: array(SelectionNode.t), + }; +}; + +module FieldNode = { + type t = { + kind: string, + loc: option(Location.t), + alias: option(NameNode.t), + name: NameNode.t, + arguments: option(array(ArgumentNode.t)), + directives: option(array(DirectiveNode.t)), + selectionSet: option(SelectionSetNode.t), + }; +}; + +module FragmentDefinitionNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + // // Note: fragment variable definitions are experimental and may be changed + // // or removed in the future. + // readonly variableDefinitions?: ReadonlyArray; + typeCondition: NamedTypeNode.t, + directives: option(array(DirectiveNode.t)), + selectionSet: SelectionSetNode.t, + }; +}; + +module VariableNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + }; +}; + +module VariableDefinitionNode = { + type t = { + kind: string, + loc: option(Location.t), + variable: VariableNode.t, + // [@bs.as "type"] + // type_: TypeNode.t, + defaultValue: option(ValueNode.t), + directives: option(array(DirectiveNode.t)), + }; +}; + +module OperationTypeNode = { + // export type OperationTypeNode = 'query' | 'mutation' | 'subscription'; + type t = string; +}; + +module OperationDefinitionNode = { + type t = { + kind: string, + loc: option(Location.t), + name: NameNode.t, + operation: OperationTypeNode.t, + variableDefinitions: option(array(VariableDefinitionNode.t)), + directives: option(array(DirectiveNode.t)), + selectionSet: SelectionSetNode.t, + }; +}; diff --git a/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Source.re b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Source.re new file mode 100644 index 0000000..5d09327 --- /dev/null +++ b/src/_peerDependencies/graphql/language/ApolloClient__Graphql_Language_Source.re @@ -0,0 +1,11 @@ +type location = { + line: int, + column: int, +}; + +type t = { + body: string, + name: string, + locationOffset: location, + // constructor(body: string, name?: string, locationOffset?: Location); +}; diff --git a/src/_peerDependencies/graphql/location/ApolloClient__Graphql_Location.re b/src/_peerDependencies/graphql/location/ApolloClient__Graphql_Location.re new file mode 100644 index 0000000..bb1509b --- /dev/null +++ b/src/_peerDependencies/graphql/location/ApolloClient__Graphql_Location.re @@ -0,0 +1,4 @@ +type sourceLocation = { + line: int, + column: int, +}; diff --git a/src/cache/core/ApolloClient__Cache_Core_Cache.re b/src/cache/core/ApolloClient__Cache_Core_Cache.re new file mode 100644 index 0000000..fd403ab --- /dev/null +++ b/src/cache/core/ApolloClient__Cache_Core_Cache.re @@ -0,0 +1,36 @@ +// import { DocumentNode } from 'graphql'; +// import { StoreObject } from '../../utilities/graphql/storeUtils'; +// import { DataProxy } from './types/DataProxy'; +// import { Cache } from './types/Cache'; +// export declare type Transaction = (c: ApolloCache) => void; + +module ApolloCache = { + module Js_ = { + // export declare abstract class ApolloCache implements DataProxy { + // abstract read(query: Cache.ReadOptions): T | null; + // abstract write(write: Cache.WriteOptions): void; + // abstract diff(query: Cache.DiffOptions): Cache.DiffResult; + // abstract watch(watch: Cache.WatchOptions): () => void; + // abstract reset(): Promise; + // abstract evict(options: Cache.EvictOptions): boolean; + // abstract evict(id: string, field?: string, args?: Record): boolean; + // abstract restore(serializedState: TSerialized): ApolloCache; + // abstract extract(optimistic?: boolean): TSerialized; + // abstract removeOptimistic(id: string): void; + // abstract performTransaction(transaction: Transaction): void; + // abstract recordOptimisticTransaction(transaction: Transaction, id: string): void; + // transformDocument(document: DocumentNode): DocumentNode; + // identify(object: StoreObject): string | undefined; + // gc(): string[]; + // transformForLink(document: DocumentNode): DocumentNode; + // readQuery(options: DataProxy.Query, optimistic?: boolean): QueryType | null; + // private getFragmentDoc; + // readFragment(options: DataProxy.Fragment, optimistic?: boolean): FragmentType | null; + // writeQuery(options: Cache.WriteQueryOptions): void; + // writeFragment(options: Cache.WriteFragmentOptions): void; + // } + type t('tSerialized); + }; + + type t('tSerialized) = Js_.t('tSerialized); +}; diff --git a/src/cache/core/ApolloClient__Cache_Core_Types.re b/src/cache/core/ApolloClient__Cache_Core_Types.re new file mode 100644 index 0000000..f59dda3 --- /dev/null +++ b/src/cache/core/ApolloClient__Cache_Core_Types.re @@ -0,0 +1,101 @@ +module Graphql = ApolloClient__Graphql; +module DataProxy = { + module Query = { + module Js_ = { + // interface Query { + // query: DocumentNode; + // variables?: TVariables; + // id?: string; + // } + type t('variables) = { + query: Graphql.documentNode, + variables: option('variables), + id: option(string), + }; + }; + + type t('variables) = Js_.t('variables); + }; + + module Fragment = { + module Js_ = { + // interface Fragment { + // id: string; + // fragment: DocumentNode; + // fragmentName?: string; + // variables?: TVariables; + // } + type t('variables) = { + id: string, + fragment: Graphql.documentNode, + fragmentName: option(string), + variables: option('variables), + }; + }; + + type t('variables) = Js_.t('variables); + }; + + module WriteQueryOptions = { + module Js_ = { + // interface WriteQueryOptions extends Query { + // data: TData; + // broadcast?: boolean; + // } + type t('jsData, 'variables) = { + data: 'jsData, + broadcast: option(bool), + // ...extends Query + query: Graphql.documentNode, + variables: option('variables), + id: option(string), + }; + }; + + type t('data, 'variables) = { + data: 'data, + broadcast: option(bool), + query: Graphql.documentNode, + variables: option('variables), + id: option(string), + }; + + let toJs: + (t('data, 'variables), ~parse: 'jsData => 'data) => + Js_.t('jsData, 'variables) = + (t, ~parse) => { + data: t.data->parse, + broadcast: t.broadcast, + query: t.query, + variables: t.variables, + id: t.id, + }; + }; + + module WriteFragmentOptions = { + module Js_ = { + // interface WriteFragmentOptions extends Fragment { + // data: TData; + // broadcast?: boolean; + // } + type t('jsData, 'variables) = { + data: 'jsData, + broadcast: option(bool), + // ...extends Fragment + id: string, + fragment: Graphql.documentNode, + fragmentName: option(string), + variables: option('variables), + }; + }; + + type t('data, 'variables) = { + data: 'data, + broadcast: option(bool), + id: string, + fragment: Graphql.documentNode, + fragmentName: option(string), + variables: option('variables), + }; + }; +}; diff --git a/src/cache/inmemory/ApolloClient__Cache_InMemory_InMemoryCache.re b/src/cache/inmemory/ApolloClient__Cache_InMemory_InMemoryCache.re new file mode 100644 index 0000000..05dc4d3 --- /dev/null +++ b/src/cache/inmemory/ApolloClient__Cache_InMemory_InMemoryCache.re @@ -0,0 +1,102 @@ +module KeyFieldsFunction = ApolloClient__Cache_InMemory_Policies.KeyFieldsFunction; +module PossibleTypesMap = ApolloClient__Cache_InMemory_Policies.PossibleTypesMap; +module TypePolicies = ApolloClient__Cache_InMemory_Policies.TypePolicies; + +module InMemoryCacheConfig = { + module Js_ = { + // export interface InMemoryCacheConfig extends ApolloReducerConfig { + // resultCaching?: boolean; + // possibleTypes?: PossibleTypesMap; + // typePolicies?: TypePolicies; + // } + // NOTE: Using deriving abstract here because passing properties that are undefined has effects + [@bs.deriving abstract] + type t = { + [@bs.optional] + resultCaching: bool, + [@bs.optional] + possibleTypes: PossibleTypesMap.Js_.t, + [@bs.optional] + typePolicies: TypePolicies.Js_.t, + // ...extends ApolloReducerConfig + [@bs.optional] + dataIdFromObject: KeyFieldsFunction.Js_.t, + [@bs.optional] + addTypename: bool, + }; + }; + type t = Js_.t; + let make = Js_.t; +}; + +module Js_ = { + // export declare class InMemoryCache extends ApolloCache { + // private data; + // private optimisticData; + // protected config: InMemoryCacheConfig; + // private watches; + // private addTypename; + // private typenameDocumentCache; + // private storeReader; + // private storeWriter; + // readonly policies: Policies; + // constructor(config?: InMemoryCacheConfig); + // restore(data: NormalizedCacheObject): this; + // extract(optimistic?: boolean): NormalizedCacheObject; + // read(options: Cache.ReadOptions): T | null; + // write(options: Cache.WriteOptions): void; + // diff(options: Cache.DiffOptions): Cache.DiffResult; + // watch(watch: Cache.WatchOptions): () => void; + // gc(): string[]; + // retain(rootId: string, optimistic?: boolean): number; + // release(rootId: string, optimistic?: boolean): number; + // identify(object: StoreObject): string | undefined; + // evict(idOrOptions: string | Cache.EvictOptions, fieldName?: string, args?: Record): boolean; + // reset(): Promise; + // removeOptimistic(idToRemove: string): void; + // private txCount; + // performTransaction(transaction: (cache: InMemoryCache) => any, optimisticId?: string): void; + // recordOptimisticTransaction(transaction: Transaction, id: string): void; + // transformDocument(document: DocumentNode): DocumentNode; + // protected broadcastWatches(): void; + // private maybeBroadcastWatch; + // private varDep; + // makeVar(value: T): ReactiveVar; + // } + type t = ApolloClient__Cache_Core_Cache.ApolloCache.Js_.t(Js.Json.t); + + [@bs.module "@apollo/client"] [@bs.new] + external make: InMemoryCacheConfig.Js_.t => t = "InMemoryCache"; +}; + +type t = Js_.t; + +let make: + ( + ~resultCaching: bool=?, + ~possibleTypes: PossibleTypesMap.t=?, + ~typePolicies: TypePolicies.t=?, + ~dataIdFromObject: KeyFieldsFunction.t=?, + ~addTypename: bool=?, + unit + ) => + t = + ( + ~resultCaching=?, + ~possibleTypes=?, + ~typePolicies=?, + ~dataIdFromObject=?, + ~addTypename=?, + (), + ) => { + Js_.make( + InMemoryCacheConfig.make( + ~resultCaching?, + ~possibleTypes?, + ~typePolicies?, + ~dataIdFromObject?, + ~addTypename?, + (), + ), + ); + }; diff --git a/src/cache/inmemory/ApolloClient__Cache_InMemory_Policies.re b/src/cache/inmemory/ApolloClient__Cache_InMemory_Policies.re new file mode 100644 index 0000000..995dd2f --- /dev/null +++ b/src/cache/inmemory/ApolloClient__Cache_InMemory_Policies.re @@ -0,0 +1,72 @@ +module FragmentMap = ApolloClient__Utilities_Graphql_Fragments.FragmentMap; +module SelectionSetNode = ApolloClient__Graphql_Language_Ast.SelectionSetNode; + +module KeyFieldsContext = { + module Js_ = { + // declare type KeyFieldsContext = { + // typename?: string; + // selectionSet?: SelectionSetNode; + // fragmentMap?: FragmentMap; + // keyObject?: Record; + // }; + type t = { + typename: option(string), + selectionSet: option(SelectionSetNode.t), + fragment: option(FragmentMap.t), + keyObject: option(Js.Json.t), + }; + }; + + type t = Js_.t; +}; + +module KeyFieldsFunction = { + module Js_ = { + // export declare type KeyFieldsFunction = (object: Readonly, context: KeyFieldsContext) => KeySpecifier | ReturnType; + type t = (Js.Json.t, KeyFieldsContext.t) => string; + }; + + type t = Js_.t; +}; + +// export declare const defaultDataIdFromObject: KeyFieldsFunction; +[@bs.module "@apollo/client"] +external defaultDataIdFromObject: KeyFieldsFunction.t = + "defaultDataIdFromObject"; + +module TypePolicy = { + module Js_ = { + // export declare type TypePolicy = { + // keyFields?: KeySpecifier | KeyFieldsFunction | false; + // queryType?: true; + // mutationType?: true; + // subscriptionType?: true; + // fields?: { + // [fieldName: string]: FieldPolicy | FieldReadFunction; + // }; + // }; + type t; + }; + type t = Js_.t; +}; + +module TypePolicies = { + module Js_ = { + // export declare type TypePolicies = { + // [__typename: string]: TypePolicy; + // }; + type t = Js.Dict.t(TypePolicy.Js_.t); + }; + type t = Js_.t; +}; + +module PossibleTypesMap = { + module Js_ = { + // export declare type PossibleTypesMap = { + // [supertype: string]: string[]; + // }; + type t = Js.Dict.t(array(string)); + }; + + type t = Js_.t; +}; diff --git a/src/core/ApolloClient__Core.re b/src/core/ApolloClient__Core.re new file mode 100644 index 0000000..96ec4ff --- /dev/null +++ b/src/core/ApolloClient__Core.re @@ -0,0 +1,3 @@ +module NetworkStatus = ApolloClient__Core_NetworkStatus; +module Types = ApolloClient__Core_Types; +module WatchQueryOptions = ApolloClient__Core_WatchQueryOptions; diff --git a/src/core/ApolloClient__Core_LocalState.re b/src/core/ApolloClient__Core_LocalState.re new file mode 100644 index 0000000..9cc5e43 --- /dev/null +++ b/src/core/ApolloClient__Core_LocalState.re @@ -0,0 +1,36 @@ +module FieldNode = ApolloClient__Graphql_Language_Ast.FieldNode; +module FragmentMap = ApolloClient__Utilities_Graphql_Fragments.FragmentMap; + +module FragmentMatcher = { + module Js_ = { + // export declare type FragmentMatcher = (rootValue: any, typeCondition: string, context: any) => boolean; + type t = (Js.Json.t, string, Js.Json.t) => bool; + }; + + type t = Js_.t; +}; + +// TODO: resolve dependency cycle +type apolloClient; + +module Resolver = { + module Js_ = { + type t_context = { + client: apolloClient, + cache: ApolloClient__Cache_Core_Cache.ApolloCache.t(Js.Json.t), + }; + + type t_info = { + field: FieldNode.t, + fragmentMap: FragmentMap.t, + }; + + // export declare type Resolver = (rootValue?: any, args?: any, context?: any, info?: { + // field: FieldNode; + // fragmentMap: FragmentMap; + // }) => any; + type t = (Js.Json.t, Js.Json.t, t_context, t_info) => Js.Json.t; + }; + + type t = Js_.t; +}; diff --git a/src/core/ApolloClient__Core_NetworkStatus.re b/src/core/ApolloClient__Core_NetworkStatus.re new file mode 100644 index 0000000..a37882d --- /dev/null +++ b/src/core/ApolloClient__Core_NetworkStatus.re @@ -0,0 +1,13 @@ +[@bs.deriving jsConverter] +type t = + | [@bs.as 1] Loading + | [@bs.as 2] SetVariables + | [@bs.as 3] FetchMore + | [@bs.as 4] Refetch + | [@bs.as 5] Poll + | [@bs.as 6] Ready + | [@bs.as 7] Error; + +let toJs = tToJs; + +let fromJs = string => tFromJs(string)->Belt.Option.getExn; diff --git a/src/core/ApolloClient__Core_Types.re b/src/core/ApolloClient__Core_Types.re new file mode 100644 index 0000000..85e8ca0 --- /dev/null +++ b/src/core/ApolloClient__Core_Types.re @@ -0,0 +1,137 @@ +module Graphql = ApolloClient__Graphql; +module GraphqlTag = ApolloClient__GraphqlTag; +module FetchResult = ApolloClient__Link_Core_Types.FetchResult; +module Resolver = ApolloClient__Core_LocalState.Resolver; +module Types = ApolloClient__Reason_Types; + +module PureQueryOptions = { + module Js_ = { + // export declare type PureQueryOptions = { + // query: DocumentNode; + // variables?: { + // [key: string]: any; + // }; + // context?: any; + // }; + type t('variables) = { + query: Graphql.documentNode, + variables: option('variables), + context: option(Js.Json.t), + }; + }; + + type t('variables) = { + query: string, + variables: option('variables), + context: option(Js.Json.t), + }; + + let toJs: t('variables) => Js_.t('variables) = + t => { + query: GraphqlTag.gql(t.query), + variables: t.variables, + context: t.context, + }; +}; + +module ApolloQueryResult = { + module Js_ = { + // export declare type ApolloQueryResult = { + // data?: T; + // errors?: ReadonlyArray; + // loading: boolean; + // networkStatus: NetworkStatus; + // }; + type t('jsData) = { + data: option('jsData), + errors: option(array(Graphql.Error.GraphQLError.t)), + loading: bool, + networkStatus: int, + }; + }; + + type t('parsedData) = Js_.t('parsedData); + + let fromJs: (t('jsData), ~parse: 'jsData => 'parsedData) => t('parsedData) = + ({data, errors, loading, networkStatus}, ~parse) => { + data: data->Belt.Option.map(parse), + errors, + loading, + networkStatus, + }; +}; + +module MutationQueryReducer = { + module Js_ = { + // export declare type MutationQueryReducer = (previousResult: Record, options: { + // mutationResult: FetchResult; + // queryName: string | undefined; + // queryVariables: Record; + // }) => Record; + type options('jsData) = { + mutationResult: FetchResult.Js_.t('jsData), + queryName: option(string), + queryVariables: Js.Json.t // ACTUAL: Record + }; + + type t('jsData) = (. Js.Json.t, options('jsData)) => Js.Json.t; + }; + + type options('data) = { + mutationResult: FetchResult.t('data), + queryName: option(string), + queryVariables: Js.Json.t // ACTUAL: Record + }; + + type t('data) = (Js.Json.t, options('data)) => Js.Json.t; + + let toJs: + (t('data), ~parse: Types.parse('jsData, 'data)) => + (. Js.Json.t, Js_.options('jsData)) => Js.Json.t = + (t, ~parse) => + (. previousResult, jsOptions) => + t( + previousResult, + { + mutationResult: + jsOptions.mutationResult->FetchResult.fromJs(~parse), + queryName: jsOptions.queryName, + queryVariables: jsOptions.queryVariables, + }, + ); +}; + +module MutationQueryReducersMap = { + module Js_ = { + // export declare type MutationQueryReducersMap = { + // [queryName: string]: MutationQueryReducer; + // }; + type t('jsData) = Js.Dict.t(MutationQueryReducer.Js_.t('jsData)); + }; + + type t('data) = Js.Dict.t(MutationQueryReducer.t('data)); + + let toJs: + (t('data), ~parse: Types.parse('jsData, 'data)) => Js_.t('jsData) = + (t, ~parse) => { + Js.Dict.map( + (. mutationQueryReducer) => + mutationQueryReducer->MutationQueryReducer.toJs(~parse), + t, + ); + }; +}; + +module Resolvers = { + module Js_ = { + // export interface Resolvers { + // [key: string]: { + // [field: string]: Resolver; + // }; + // } + type t = Js.Dict.t(Js.Dict.t(Resolver.Js_.t)); + }; + type t = Js_.t; +}; diff --git a/src/core/ApolloClient__Core_WatchQueryOptions.re b/src/core/ApolloClient__Core_WatchQueryOptions.re new file mode 100644 index 0000000..5b33d4e --- /dev/null +++ b/src/core/ApolloClient__Core_WatchQueryOptions.re @@ -0,0 +1,266 @@ +module ApolloCache = ApolloClient__Cache_Core_Cache.ApolloCache; +module FetchResult = ApolloClient__Link_Core_Types.FetchResult; +module GraphQL = ApolloClient__Graphql; +module GraphqlTag = ApolloClient__Graphql; +module MutationQueryReducersMap = ApolloClient__Core_Types.MutationQueryReducersMap; +module Types = ApolloClient__Reason_Types; +module PureQueryOptions = ApolloClient__Core_Types.PureQueryOptions; + +module ErrorPolicy = { + module Js_ = { + // export declare type ErrorPolicy = 'none' | 'ignore' | 'all'; + type t = string; + }; + + type t = + | None + | Ignore + | All; + + let toJs = + fun + | None => "none" + | Ignore => "ignore" + | All => "all"; +}; + +module FetchPolicy = { + module Js_ = { + // export declare type FetchPolicy = 'cache-first' | 'network-only' | 'cache-only' | 'no-cache' | 'standby'; + type t = string; + }; + + type t = + | CacheFirst + | CacheOnly + | NetworkOnly + | NoCache + | Standby; + + let toJs = + fun + | CacheFirst => "cache-first" + | CacheOnly => "cache-only" + | NetworkOnly => "network-only" + | NoCache => "no-cache" + | Standby => "standby"; +}; + +module FetchPolicy__noCacheExtracted = { + module Js_ = { + // Extract + type t = string; + }; + type t = + | CacheFirst + | CacheOnly + | NetworkOnly + | Standby; + + let toJs = + fun + | CacheFirst => "cache-first" + | CacheOnly => "cache-only" + | NetworkOnly => "network-only" + | Standby => "standby"; +}; + +module WatchQueryFetchPolicy = { + module Js_ = { + // export declare type WatchQueryFetchPolicy = FetchPolicy | 'cache-and-network'; + type t = string; + }; + + type t = + | CacheAndNetwork + | CacheFirst + | CacheOnly + | NetworkOnly + | NoCache + | Standby; + + let toJs = + fun + | CacheAndNetwork => "cache-and-network" + | CacheFirst => "cache-first" + | CacheOnly => "cache-only" + | NetworkOnly => "network-only" + | NoCache => "no-cache" + | Standby => "standby"; +}; + +module QueryOptions = { + module Js_ = { + type t('variables) = { + fetchPolicy: option(FetchPolicy.Js_.t), + // ...extends QueryBaseOptions + query: GraphQL.Language.documentNode, + variables: option('variables), + errorPolicy: option(ErrorPolicy.Js_.t), + context: option(Js.Json.t), + }; + }; + + type t('variables) = { + fetchPolicy: option(FetchPolicy.t), + query: ApolloClient__Graphql.documentNode, + variables: option('variables), + errorPolicy: option(ErrorPolicy.t), + context: option(Js.Json.t), + }; + + let toJs: t('variables) => Js_.t('variables) = + t => { + fetchPolicy: t.fetchPolicy->Belt.Option.map(FetchPolicy.toJs), + query: t.query, + variables: t.variables, + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + context: t.context, + }; +}; + +module WatchQueryOptions = { + module Js_ = { + type t('variables) = { + fetchPolicy: option(WatchQueryFetchPolicy.Js_.t), + // ...extends QueryBaseOptions + query: GraphQL.Language.documentNode, + variables: option('variables), + errorPolicy: option(ErrorPolicy.Js_.t), + context: option(Js.Json.t), + }; + }; + + type t('variables) = { + fetchPolicy: option(WatchQueryFetchPolicy.t), + query: ApolloClient__Graphql.documentNode, + variables: option('variables), + errorPolicy: option(ErrorPolicy.t), + context: option(Js.Json.t), + }; + + let toJs: t('variables) => Js_.t('variables) = + t => { + fetchPolicy: t.fetchPolicy->Belt.Option.map(WatchQueryFetchPolicy.toJs), + query: t.query, + variables: t.variables, + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + context: t.context, + }; +}; + +module MutationUpdaterFn = { + module Js_ = { + type t('jsData) = + (. ApolloCache.Js_.t(Js.Json.t), FetchResult.Js_.t('jsData)) => unit; + }; + + type t('data) = (ApolloCache.t(Js.Json.t), FetchResult.t('data)) => unit; + + let toJs: + (t('data), ~parse: Types.parse('jsData, 'data)) => Js_.t('jsData) = + (mutationUpdaterFn, ~parse) => + (. cache, jsFetchResult) => + mutationUpdaterFn(cache, jsFetchResult->FetchResult.fromJs(~parse)); +}; + +module RefetchQueryDescription = { + module Js_ = { + // CAVEAT: I did not try very hard to find a simpler alternative + module Union: { + type t; + let string: string => t; + let pureQueryOptions: PureQueryOptions.Js_.t(_) => t; + } = { + [@unboxed] + type t = + | Any('a): t; + let string = (v: string) => Any(v); + let pureQueryOptions = (v: PureQueryOptions.Js_.t(_)) => Any(v); + }; + + type t = array(Union.t); + }; + + type t_variant = + | PureQueryOptions(PureQueryOptions.t('variables)): t_variant + | String(string): t_variant; + + type t = array(t_variant); + + let toJs: t => Js_.t = + Belt.Array.map( + _, + fun + | PureQueryOptions(options) => + Js_.Union.pureQueryOptions(options->PureQueryOptions.toJs) + | String(string) => Js_.Union.string(string), + ); +}; + +module MutationOptions = { + module Js_ = { + // export interface MutationOptions extends MutationBaseOptions { + // mutation: DocumentNode; + // context?: any; + // fetchPolicy?: Extract; + // } + type t('jsData, 'variables) = { + mutation: GraphqlTag.documentNode, + context: option(Js.Json.t), + fetchPolicy: option(FetchPolicy__noCacheExtracted.Js_.t), + // ...extends MutationBaseOption, + awaitRefetchQueries: option(bool), + errorPolicy: option(ErrorPolicy.Js_.t), + optimisticResponse: option('variables => 'jsData), + update: option(MutationUpdaterFn.Js_.t('jsData)), + updateQueries: option(MutationQueryReducersMap.Js_.t('jsData)), + refetchQueries: option(RefetchQueryDescription.Js_.t), + variables: option('variables), + }; + }; + + type t('data, 'variables) = { + context: option(Js.Json.t), + fetchPolicy: option(FetchPolicy__noCacheExtracted.t), + mutation: GraphqlTag.documentNode, + // ...extends MutationBaseOption, + awaitRefetchQueries: option(bool), + errorPolicy: option(ErrorPolicy.t), + optimisticResponse: option('variables => 'data), + refetchQueries: option(RefetchQueryDescription.t), + update: option(MutationUpdaterFn.t('data)), + updateQueries: option(MutationQueryReducersMap.t('data)), + variables: option('variables), + }; + + let toJs: + ( + t('data, 'variables), + ~parse: Types.parse('jsData, 'data), + ~serialize: Types.serialize('data, 'jsData) + ) => + Js_.t('jsData, 'variables) = + (t, ~parse, ~serialize) => { + awaitRefetchQueries: t.awaitRefetchQueries, + context: t.context, + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + fetchPolicy: + t.fetchPolicy->Belt.Option.map(FetchPolicy__noCacheExtracted.toJs), + mutation: t.mutation, + optimisticResponse: + t.optimisticResponse + ->Belt.Option.map((optimisticResponse, variables) => + optimisticResponse(variables)->serialize + ), + refetchQueries: + t.refetchQueries->Belt.Option.map(RefetchQueryDescription.toJs), + update: t.update->Belt.Option.map(MutationUpdaterFn.toJs(~parse)), + updateQueries: + t.updateQueries + ->Belt.Option.map(MutationQueryReducersMap.toJs(~parse)), + variables: t.variables, + }; +}; diff --git a/src/errors/ApolloClient__ApolloError.re b/src/errors/ApolloClient__ApolloError.re new file mode 100644 index 0000000..52799ff --- /dev/null +++ b/src/errors/ApolloClient__ApolloError.re @@ -0,0 +1,55 @@ +module Graphql = ApolloClient__Graphql; +module GraphQLError = Graphql.Error.GraphQLError; + +module Js_ = { + // export declare class ApolloError extends Error { + // message: string; + // graphQLErrors: ReadonlyArray; + // networkError: Error | null; + // extraInfo: any; + // } + type t = { + extraInfo: Js.Json.t, + graphQLErrors: array(Graphql.Error.GraphQLError.t), + networkError: Js.nullable(Js.Exn.t), + // ...extends Error + name: string, + message: string, + stack: option(string), + }; + + type make_args = { + graphQLErrors: option(array(GraphQLError.t)), + networkError: Js.nullable(Js.Exn.t), + errorMessage: option(string), + extraInfo: option(Js.Json.t), + }; + + // constructor({ graphQLErrors, networkError, errorMessage, extraInfo, }: { + // graphQLErrors?: ReadonlyArray; + // networkError?: Error | null; + // errorMessage?: string; + // extraInfo?: any; + // }); + [@bs.module "@apollo/client"] [@bs.new] + external make: make_args => t = "ApolloError"; +}; + +type t = Js_.t; + +let make: + ( + ~graphQLErrors: array(GraphQLError.t)=?, + ~networkError: Js.Exn.t=?, + ~errorMessage: string=?, + ~extraInfo: Js.Json.t=?, + unit + ) => + t = + (~graphQLErrors=?, ~networkError=?, ~errorMessage=?, ~extraInfo=?, ()) => + Js_.make({ + graphQLErrors, + networkError: Js.Nullable.fromOption(networkError), + errorMessage, + extraInfo, + }); diff --git a/src/link/core/ApolloClient__Link_Core_ApolloLink.re b/src/link/core/ApolloClient__Link_Core_ApolloLink.re new file mode 100644 index 0000000..77d4df3 --- /dev/null +++ b/src/link/core/ApolloClient__Link_Core_ApolloLink.re @@ -0,0 +1,70 @@ +module FetchResult = ApolloClient__Link_Core_Types.FetchResult; +module GraphQLRequest = ApolloClient__Link_Core_Types.GraphQLRequest; +module Observable = ApolloClient__ZenObservable.Observable; +module Operation = ApolloClient__Link_Core_Types.Operation; +module RequestHandler = ApolloClient__Link_Core_Types.RequestHandler; + +module Js_ = { + // export declare class ApolloLink { + // static empty(): ApolloLink; + // static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + // static split(test: (op: Operation) => boolean, left: ApolloLink | RequestHandler, right?: ApolloLink | RequestHandler): ApolloLink; + // static execute(link: ApolloLink, operation: GraphQLRequest): Observable; + // static concat(first: ApolloLink | RequestHandler, second: ApolloLink | RequestHandler): ApolloLink; + // constructor(request?: RequestHandler); + // split(test: (op: Operation) => boolean, left: ApolloLink | RequestHandler, right?: ApolloLink | RequestHandler): ApolloLink; + // concat(next: ApolloLink | RequestHandler): ApolloLink; + // request(operation: Operation, forward?: NextLink): Observable | null; + // protected onError(reason: any): void; + // setOnError(fn: (reason: any) => any): this; + // } + type t; + + module Static = { + // static empty(): ApolloLink; + [@bs.module "@apollo/client"] [@bs.scope "ApolloLink"] + external empty: unit => t = "empty"; + // static from(links: (ApolloLink | RequestHandler)[]): ApolloLink; + [@bs.module "@apollo/client"] [@bs.scope "ApolloLink"] + external from: array(t) => t = "from"; + // static split(test: (op: Operation) => boolean, left: ApolloLink | RequestHandler, right?: ApolloLink | RequestHandler): ApolloLink; + [@bs.module "@apollo/client"] [@bs.scope "ApolloLink"] + external split: (~test: Operation.Js_.t => bool, ~left: t, ~right: t) => t = + "split"; + // static execute(link: ApolloLink, operation: GraphQLRequest): Observable; + [@bs.module "@apollo/client"] [@bs.scope "ApolloLink"] + external execute: + (t, GraphQLRequest.t) => Observable.t(FetchResult.Js_.t(Js.Json.t)) = + "execute"; + // static concat(first: ApolloLink | RequestHandler, second: ApolloLink | RequestHandler): ApolloLink; + [@bs.module "@apollo/client"] [@bs.scope "ApolloLink"] + external concat: (t, t) => t = "concat"; + }; + + [@bs.module "@apollo/client"] [@bs.new] + external make: RequestHandler.Js_.t => t = "ApolloLink"; + + [@bs.send] external concat: (t, t) => t = "concat"; + + [@bs.send] external from: (t, array(t)) => t = "from"; + + [@bs.send] external setOnError: (t, Js.Json.t => unit) => unit = "onError"; + + [@bs.send] + external split: (t, ~test: Operation.Js_.t => bool, ~left: t, ~right: t) => t = + "split"; +}; + +module Static = Js_.Static; + +type t = Js_.t; + +let make: RequestHandler.t => t = Js_.make; + +let concat = Js_.concat; + +let from = Js_.from; + +let setOnError = Js_.setOnError; + +let split: (t, ~test: Operation.t => bool, ~left: t, ~right: t) => t = Js_.split; diff --git a/src/link/core/ApolloClient__Link_Core_Types.re b/src/link/core/ApolloClient__Link_Core_Types.re new file mode 100644 index 0000000..14a1656 --- /dev/null +++ b/src/link/core/ApolloClient__Link_Core_Types.re @@ -0,0 +1,107 @@ +module Graphql = ApolloClient__Graphql; +module GraphQLError = ApolloClient__Graphql_Error_GraphQLError; +module Observable = ApolloClient__ZenObservable.Observable; + +module GraphQLRequest = { + module Js_ = { + // export interface GraphQLRequest { + // query: DocumentNode; + // variables?: Record; + // operationName?: string; + // context?: Record; + // extensions?: Record; + // } + type t = { + query: Graphql.documentNode, + variables: option(Js.Json.t), + operationName: option(string), + context: option(Js.Json.t), + extensions: option(Js.Json.t), + }; + }; + + type t = Js_.t; +}; + +module Operation = { + module Js_ = { + // export interface Operation { + // query: DocumentNode; + // variables: Record; + // operationName: string; + // extensions: Record; + // setContext: (context: Record) => Record; + // getContext: () => Record; + // } + type t = { + query: Graphql.documentNode, + variables: Js.Json.t, + operationName: string, + extensions: Js.Json.t, + setContext: Js.Json.t => Js.Json.t, + getContext: unit => Js.Json.t, + }; + }; + + type t = Js_.t; +}; + +module FetchResult = { + module Js_ = { + // export interface FetchResult, E = Record> extends ExecutionResult { + // data?: TData | null; + // extensions?: E; + // context?: C; + // } + type t('jsData) = { + data: Js.Nullable.t('jsData), + extensions: option(Js.Json.t), // ACTUAL: Record + context: option(Js.Json.t), // ACTUAL: Record + // ...extends ExecutionResult + errors: option(GraphQLError.t), + }; + }; + + type t('data) = { + data: option('data), + extensions: option(Js.Json.t), // ACTUAL: Record + context: option(Js.Json.t), // ACTUAL: Record + // ...extends ExecutionResult + errors: option(GraphQLError.t), + }; + + let fromJs: + ( + Js_.t('jsData), + ~parse: ApolloClient__Reason_Types.parse('jsData, 'data) + ) => + t('data) = + (js, ~parse) => { + { + data: js.data->Js.toOption->Belt.Option.map(parse), + extensions: js.extensions, + context: js.context, + errors: js.errors, + }; + }; +}; + +module NextLink = { + module Js_ = { + // export declare type NextLink = (operation: Operation) => Observable; + type t = Operation.Js_.t => Observable.t(FetchResult.Js_.t(Js.Json.t)); + }; +}; + +module RequestHandler = { + module Js_ = { + // export declare type RequestHandler = (operation: Operation, forward: NextLink) => Observable | null; + type t = + (Operation.Js_.t, NextLink.Js_.t) => + Js.nullable(Observable.t(FetchResult.Js_.t(Js.Json.t))); + }; + + type t = Js_.t; +}; diff --git a/src/link/http/ApolloClient_Link_Http_CreateHttpLink.re b/src/link/http/ApolloClient_Link_Http_CreateHttpLink.re new file mode 100644 index 0000000..9c9571b --- /dev/null +++ b/src/link/http/ApolloClient_Link_Http_CreateHttpLink.re @@ -0,0 +1,10 @@ +module ApolloLink = ApolloClient__Link_Core_ApolloLink; +module HttpOptions = ApolloClient_Link_Http_SelectHttpOptionsAndBody.HttpOptions; + +module Js_ = { + [@bs.module "@apollo/client"] + external createHttpLink: HttpOptions.Js_.t => ApolloLink.Js_.t = + "createHttpLink"; +}; + +let createHttpLink: HttpOptions.t => ApolloLink.t = Js_.createHttpLink; diff --git a/src/link/http/ApolloClient_Link_Http_HttpLink.re b/src/link/http/ApolloClient_Link_Http_HttpLink.re new file mode 100644 index 0000000..6c13a72 --- /dev/null +++ b/src/link/http/ApolloClient_Link_Http_HttpLink.re @@ -0,0 +1,46 @@ +module ApolloLink = ApolloClient__Link_Core_ApolloLink; +module HttpOptions = ApolloClient_Link_Http_SelectHttpOptionsAndBody.HttpOptions; +module UriFunction = ApolloClient_Link_Http_SelectHttpOptionsAndBody.UriFunction; + +module Js_ = { + // export declare class HttpLink extends ApolloLink { + // options: HttpOptions; + // requester: RequestHandler; + // constructor(options?: HttpOptions); + // } + [@bs.module "@apollo/client"] [@bs.new] + external make: HttpOptions.Js_.t => ApolloLink.Js_.t = "HttpLink"; +}; + +let make: + ( + ~uri: UriFunction.t=?, + ~includeExtensions: bool=?, + ~fetch: HttpOptions.Js_.t_fetch=?, + ~headers: Js.Json.t=?, + ~credentials: string=?, + ~fetchOptions: Js.Json.t=?, + ~useGETForQueries: bool=?, + unit + ) => + ApolloLink.t = + ( + ~uri=?, + ~includeExtensions=?, + ~fetch=?, + ~headers=?, + ~credentials=?, + ~fetchOptions=?, + ~useGETForQueries=?, + (), + ) => { + Js_.make({ + uri, + includeExtensions, + fetch, + headers, + credentials, + fetchOptions, + useGETForQueries, + }); + }; diff --git a/src/link/http/ApolloClient_Link_Http_SelectHttpOptionsAndBody.re b/src/link/http/ApolloClient_Link_Http_SelectHttpOptionsAndBody.re new file mode 100644 index 0000000..eeb75c8 --- /dev/null +++ b/src/link/http/ApolloClient_Link_Http_SelectHttpOptionsAndBody.re @@ -0,0 +1,79 @@ +module Operation = ApolloClient__Link_Core_Types.Operation; + +// import { Operation } from '../core/types'; +// export interface Body { +// query?: string; +// operationName?: string; +// variables?: Record; +// extensions?: Record; +// } + +module UriFunction = { + module Js_ = { + type t = Operation.Js_.t => string; + }; + + type t = Js_.t; +}; + +module HttpOptions = { + module Js_ = { + // export interface HttpOptions { + // uri?: string | UriFunction; + // includeExtensions?: boolean; + // fetch?: WindowOrWorkerGlobalScope['fetch']; + // headers?: any; + // credentials?: string; + // fetchOptions?: any; + // useGETForQueries?: boolean; + // } + type t_fetch; + + type t = { + uri: option(UriFunction.Js_.t), + includeExtensions: option(bool), + fetch: option(t_fetch), + headers: option(Js.Json.t), + credentials: option(string), + fetchOptions: option(Js.Json.t), + useGETForQueries: option(bool), + }; + }; + + type t = Js_.t; +}; +// export interface HttpOptions { +// uri?: string | UriFunction; +// includeExtensions?: boolean; +// fetch?: WindowOrWorkerGlobalScope['fetch']; +// headers?: any; +// credentials?: string; +// fetchOptions?: any; +// useGETForQueries?: boolean; +// } +// export interface HttpQueryOptions { +// includeQuery?: boolean; +// includeExtensions?: boolean; +// } +// export interface HttpConfig { +// http?: HttpQueryOptions; +// options?: any; +// headers?: any; +// credentials?: any; +// } +// export declare const fallbackHttpConfig: { +// http: HttpQueryOptions; +// headers: { +// accept: string; +// 'content-type': string; +// }; +// options: { +// method: string; +// }; +// }; +// export declare const selectHttpOptionsAndBody: (operation: Operation, fallbackConfig: HttpConfig, ...configs: Array) => { +// options: HttpConfig & Record; +// body: Body; +// }; + +(); diff --git a/src/react/ApolloClient__React.re b/src/react/ApolloClient__React.re new file mode 100644 index 0000000..568d7e1 --- /dev/null +++ b/src/react/ApolloClient__React.re @@ -0,0 +1,5 @@ +module ApolloProvider = ApolloClient__React_ApolloProvider; +module Types = ApolloClient__React_Types; + +let useQuery = ApolloClient__React_UseQuery.useQuery; +let useSubscription = ApolloClient__React_UseSubscription.useSubscription; diff --git a/src/react/ApolloClient__React_ApolloProvider.re b/src/react/ApolloClient__React_ApolloProvider.re new file mode 100644 index 0000000..6f8b5d4 --- /dev/null +++ b/src/react/ApolloClient__React_ApolloProvider.re @@ -0,0 +1,10 @@ +// export interface ApolloProviderProps { +// client: ApolloClient; +// children: React.ReactNode | React.ReactNode[] | null; +// } +// export declare const ApolloProvider: React.FC>; +[@bs.module "@apollo/client"] [@react.component] +external make: + (~client: ApolloClient__ApolloClient.t, ~children: React.element) => + React.element = + "ApolloProvider"; diff --git a/src/react/hooks/ApolloClient__React_UseQuery.re b/src/react/hooks/ApolloClient__React_UseQuery.re new file mode 100644 index 0000000..1145024 --- /dev/null +++ b/src/react/hooks/ApolloClient__React_UseQuery.re @@ -0,0 +1,246 @@ +module ApolloError = ApolloClient__ApolloError; +module ErrorPolicy = ApolloClient__Core_WatchQueryOptions.ErrorPolicy; +module Graphql = ApolloClient__Graphql; +module GraphqlTag = ApolloClient__GraphqlTag; +module QueryHookOptions = ApolloClient__React_Types.QueryHookOptions; +module QueryResult = ApolloClient__React_Types.QueryResult; +module Types = ApolloClient__Reason_Types; +module WatchQueryFetchPolicy = ApolloClient__Core_WatchQueryOptions.WatchQueryFetchPolicy; + +module type Operation = ApolloClient__Reason_Types.Operation; +module type OperationNoRequiredVars = ApolloClient__Reason_Types.OperationNoRequiredVars; + +exception InternalReasonApolloExn; +let exceptionQueryResult = () => + QueryResult.{ + called: true, + client: Obj.magic(), + data: None, + error: + Some( + ApolloError.make( + ~errorMessage="Internal Reason Apollo Exception", + (), + ), + ), + fetchMore: + (~context as _=?, ~variables as _=?, ~updateQuery as _=?, ()) => + Js.Promise.reject(InternalReasonApolloExn), + loading: false, + networkStatus: Ready, + }; + +module Js_ = { + // export declare function useQuery(query: DocumentNode, options?: QueryHookOptions): QueryResult; + [@bs.module "@apollo/client"] + external useQuery: + ( + ~query: Graphql.documentNode, + ~options: QueryHookOptions.Js_.t('jsData, 'variables)=? + ) => + QueryResult.Js_.t('jsData, 'variables) = + "useQuery"; +}; + +let useQuery: + type data jsData jsVariables. + ( + ~client: ApolloClient__ApolloClient.t=?, + ~context: Js.Json.t=?, + ~displayName: string=?, + ~errorPolicy: ErrorPolicy.t=?, + ~fetchPolicy: WatchQueryFetchPolicy.t=?, + ~notifyOnNetworkStatusChange: bool=?, + ~onCompleted: data => unit=?, + ~onError: ApolloError.t => unit=?, + ~partialRefetch: bool=?, + ~pollInterval: int=?, + ~skip: bool=?, + ~ssr: bool=?, + ~variables: jsVariables, + (module Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + QueryResult.t(data, jsVariables) = + ( + ~client=?, + ~context=?, + ~displayName=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~notifyOnNetworkStatusChange=?, + ~onCompleted=?, + ~onError=?, + ~partialRefetch=?, + ~pollInterval=?, + ~skip=?, + ~ssr=?, + ~variables, + (module Operation), + ) => + try({ + let jsQueryResult = + Js_.useQuery( + ~query=GraphqlTag.gql(Operation.query), + ~options= + QueryHookOptions.toJs( + { + client, + context, + displayName, + errorPolicy, + fetchPolicy, + onCompleted, + onError, + notifyOnNetworkStatusChange, + partialRefetch, + pollInterval, + query: None, + skip, + ssr, + variables, + }, + ~parse=Operation.parse, + ), + ); + + ApolloClient__Reason_Utils.useGuaranteedMemo1( + () => { + jsQueryResult->QueryResult.fromJs( + ~parse=Operation.parse, + ~serialize=Operation.serialize, + ) + }, + jsQueryResult, + ); + }) { + | _anyExn => exceptionQueryResult() + }; + +let useQuery0: + type data jsData jsVariables. + ( + ~client: ApolloClient__ApolloClient.t=?, + ~context: Js.Json.t=?, + ~displayName: string=?, + ~errorPolicy: ErrorPolicy.t=?, + ~fetchPolicy: WatchQueryFetchPolicy.t=?, + ~notifyOnNetworkStatusChange: bool=?, + ~onCompleted: data => unit=?, + ~onError: ApolloError.t => unit=?, + ~partialRefetch: bool=?, + ~pollInterval: int=?, + ~skip: bool=?, + ~ssr: bool=?, + (module Types.OperationNoRequiredVars with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + QueryResult.t(data, jsVariables) = + ( + ~client=?, + ~context=?, + ~displayName=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~notifyOnNetworkStatusChange=?, + ~onCompleted=?, + ~onError=?, + ~partialRefetch=?, + ~pollInterval=?, + ~skip=?, + ~ssr=?, + (module Operation), + ) => { + useQuery( + ~client?, + ~context?, + ~displayName?, + ~errorPolicy?, + ~fetchPolicy?, + ~notifyOnNetworkStatusChange?, + ~onCompleted?, + ~onError?, + ~partialRefetch?, + ~pollInterval?, + ~skip?, + ~ssr?, + ~variables=Operation.makeDefaultVariables(), + (module Operation), + ); + }; + +module Extend = (M: Operation) => { + let use = + ( + ~client=?, + ~context=?, + ~displayName=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~notifyOnNetworkStatusChange=?, + ~onCompleted=?, + ~onError=?, + ~partialRefetch=?, + ~pollInterval=?, + ~skip=?, + ~ssr=?, + ~variables, + (), + ) => { + useQuery( + ~client?, + ~context?, + ~displayName?, + ~errorPolicy?, + ~fetchPolicy?, + ~notifyOnNetworkStatusChange?, + ~onCompleted?, + ~onError?, + ~partialRefetch?, + ~pollInterval?, + ~skip?, + ~ssr?, + ~variables, + (module M), + ); + }; +}; + +module ExtendNoRequiredVariables = (M: OperationNoRequiredVars) => { + let use = + ( + ~client=?, + ~context=?, + ~displayName=?, + ~errorPolicy=?, + ~fetchPolicy=?, + ~notifyOnNetworkStatusChange=?, + ~onCompleted=?, + ~onError=?, + ~partialRefetch=?, + ~pollInterval=?, + ~skip=?, + ~ssr=?, + (), + ) => { + useQuery0( + ~client?, + ~context?, + ~displayName?, + ~errorPolicy?, + ~fetchPolicy?, + ~notifyOnNetworkStatusChange?, + ~onCompleted?, + ~onError?, + ~partialRefetch?, + ~pollInterval?, + ~skip?, + ~ssr?, + (module M), + ); + }; +}; diff --git a/src/react/hooks/ApolloClient__React_UseSubscription.re b/src/react/hooks/ApolloClient__React_UseSubscription.re new file mode 100644 index 0000000..4009434 --- /dev/null +++ b/src/react/hooks/ApolloClient__React_UseSubscription.re @@ -0,0 +1,185 @@ +module ApolloError = ApolloClient__ApolloError; +module BaseSubscriptionOptions = ApolloClient__React_Types.BaseSubscriptionOptions; +module FetchPolicy = ApolloClient__Core_WatchQueryOptions.FetchPolicy; +module Graphql = ApolloClient__Graphql; +module GraphqlTag = ApolloClient__GraphqlTag; +module OnSubscriptionDataOptions = ApolloClient__React_Types.OnSubscriptionDataOptions; +module SubscriptionHookOptions = ApolloClient__React_Types.SubscriptionHookOptions; +module Types = ApolloClient__Reason_Types; + +module type Operation = ApolloClient__Reason_Types.Operation; +module type OperationNoRequiredVars = ApolloClient__Reason_Types.OperationNoRequiredVars; + +module Js_ = { + type useSubscription_result('jsData, 'variables) = { + variables: option('variables), + loading: bool, + data: option('jsData), + error: option(ApolloError.Js_.t), + }; + + // export declare function useSubscription(subscription: DocumentNode, options?: SubscriptionHookOptions): { + // variables: TVariables | undefined; + // loading: boolean; + // data?: TData | undefined; + // error?: import("../..").ApolloError | undefined; + // }; + [@bs.module "@apollo/client"] + external useSubscription: + ( + ~subscription: Graphql.Language.documentNode, + ~options: SubscriptionHookOptions.Js_.t('jsData, 'variables)=? + ) => + useSubscription_result('jsData, 'variables) = + "useSubscription"; +}; + +type useSubscription_result('data, 'variables) = { + variables: option('variables), + loading: bool, + data: option('data), + error: option(ApolloError.Js_.t), +}; + +let useSubscription: + type data jsData jsVariables. + ( + ~client: ApolloClient__ApolloClient.t=?, + ~fetchPolicy: FetchPolicy.t=?, + ~onSubscriptionData: OnSubscriptionDataOptions.t(data) => unit=?, + ~onSubscriptionComplete: unit => unit=?, + ~shouldResubscribe: BaseSubscriptionOptions.t(data, jsVariables) => bool + =?, + ~skip: bool=?, + ~variables: jsVariables, + (module Operation with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + useSubscription_result(data, jsVariables) = + ( + ~client=?, + ~fetchPolicy=?, + ~onSubscriptionData=?, + ~onSubscriptionComplete=?, + ~shouldResubscribe=?, + ~skip=?, + ~variables, + (module Operation), + ) => { + let jsSubscriptionResult = + Js_.useSubscription( + ~subscription=GraphqlTag.gql(Operation.query), + ~options= + SubscriptionHookOptions.toJs( + { + client, + fetchPolicy, + onSubscriptionData, + onSubscriptionComplete, + subscription: None, + shouldResubscribe, + skip, + variables, + }, + ~parse=Operation.parse, + ), + ); + + ApolloClient__Reason_Utils.useGuaranteedMemo1( + () => + { + variables: jsSubscriptionResult.variables, + loading: jsSubscriptionResult.loading, + data: jsSubscriptionResult.data->Belt.Option.map(Operation.parse), + error: jsSubscriptionResult.error, + }, + jsSubscriptionResult, + ); + }; + +let useSubscription0: + type data jsData jsVariables. + ( + ~client: ApolloClient__ApolloClient.t=?, + ~fetchPolicy: FetchPolicy.t=?, + ~onSubscriptionData: OnSubscriptionDataOptions.t(data) => unit=?, + ~onSubscriptionComplete: unit => unit=?, + ~shouldResubscribe: BaseSubscriptionOptions.t(data, jsVariables) => bool + =?, + ~skip: bool=?, + (module Types.OperationNoRequiredVars with + type t = data and + type Raw.t = jsData and + type Raw.t_variables = jsVariables) + ) => + useSubscription_result(data, jsVariables) = + ( + ~client=?, + ~fetchPolicy=?, + ~onSubscriptionData=?, + ~onSubscriptionComplete=?, + ~shouldResubscribe=?, + ~skip=?, + (module Operation), + ) => { + useSubscription( + ~client?, + ~fetchPolicy?, + ~onSubscriptionData?, + ~onSubscriptionComplete?, + ~shouldResubscribe?, + ~skip?, + ~variables=Operation.makeDefaultVariables(), + (module Operation), + ); + }; + +module Extend = (M: Operation) => { + let use = + ( + ~client=?, + ~fetchPolicy=?, + ~onSubscriptionData=?, + ~onSubscriptionComplete=?, + ~shouldResubscribe=?, + ~skip=?, + ~variables, + (), + ) => { + useSubscription( + ~client?, + ~fetchPolicy?, + ~onSubscriptionData?, + ~onSubscriptionComplete?, + ~shouldResubscribe?, + ~skip?, + ~variables, + (module M), + ); + }; +}; + +module ExtendNoRequiredVariables = (M: OperationNoRequiredVars) => { + let use = + ( + ~client=?, + ~fetchPolicy=?, + ~onSubscriptionData=?, + ~onSubscriptionComplete=?, + ~shouldResubscribe=?, + ~skip=?, + (), + ) => { + useSubscription0( + ~client?, + ~fetchPolicy?, + ~onSubscriptionData?, + ~onSubscriptionComplete?, + ~shouldResubscribe?, + ~skip?, + (module M), + ); + }; +}; diff --git a/src/react/types/ApolloClient__React_Types.re b/src/react/types/ApolloClient__React_Types.re new file mode 100644 index 0000000..f5c4143 --- /dev/null +++ b/src/react/types/ApolloClient__React_Types.re @@ -0,0 +1,361 @@ +module ApolloError = ApolloClient__ApolloError; +module ApolloQueryResult = ApolloClient__Core_Types.ApolloQueryResult; +module ErrorPolicy = ApolloClient__Core_WatchQueryOptions.ErrorPolicy; +module FetchPolicy = ApolloClient__Core_WatchQueryOptions.FetchPolicy; +module Graphql = ApolloClient__Graphql; +module NetworkStatus = ApolloClient__Core_NetworkStatus; +module WatchQueryFetchPolicy = ApolloClient__Core_WatchQueryOptions.WatchQueryFetchPolicy; + +module QueryHookOptions = { + module Js_ = { + // export interface QueryHookOptions extends QueryFunctionOptions { + // query?: DocumentNode; + // } + type t('jsData, 'variables) = { + query: option(Graphql.documentNode), + // ...extends QueryFunctionOptions + displayName: option(string), + skip: option(bool), + onCompleted: option('jsData => unit), + onError: option(ApolloError.t => unit), + // ..extends BaseQueryOptions + client: option(ApolloClient__ApolloClient.t), + context: option(Js.Json.t), // ACTUAL: Record + errorPolicy: option(string), + fetchPolicy: option(string), + notifyOnNetworkStatusChange: option(bool), + partialRefetch: option(bool), + pollInterval: option(int), + // INTENTIONALLY IGNORED + // returnPartialData: option(bool), + ssr: option(bool), + // Intentionally restricted to not be non-optional. `option(unit)` does not compile cleanly to `undefined` + variables: 'variables, + }; + }; + + type t('data, 'variables) = { + query: option(Graphql.documentNode), + // ...extends QueryFunctionOptions + displayName: option(string), + skip: option(bool), + // consider parsing? + onCompleted: option('data => unit), + onError: option(ApolloError.t => unit), + // ...extends BaseQueryOptions + client: option(ApolloClient__ApolloClient.t), + context: option(Js.Json.t), + errorPolicy: option(ErrorPolicy.t), + fetchPolicy: option(WatchQueryFetchPolicy.t), + notifyOnNetworkStatusChange: option(bool), + partialRefetch: option(bool), + pollInterval: option(int), + // INTENTIONALLY IGNORED + // returnPartialData: option(bool), + ssr: option(bool), + variables: 'variables, + }; + + let toJs = + (t: t('data, 'variables), ~parse: 'jsData => 'data) + : Js_.t('jsData, 'variables) => { + client: t.client, + context: t.context, + displayName: t.displayName, + errorPolicy: t.errorPolicy->Belt.Option.map(ErrorPolicy.toJs), + onCompleted: + t.onCompleted + ->Belt.Option.map((onCompleted, jsData) => onCompleted(jsData->parse)), + onError: t.onError, + fetchPolicy: t.fetchPolicy->Belt.Option.map(WatchQueryFetchPolicy.toJs), + notifyOnNetworkStatusChange: t.notifyOnNetworkStatusChange, + query: t.query, + pollInterval: t.pollInterval, + partialRefetch: t.partialRefetch, + skip: t.skip, + ssr: t.ssr, + variables: t.variables, + }; +}; + +module QueryResult = { + module Js_ = { + type t_fetchMoreOptions_updateQueryOptions('parsedData, 'variables) = { + fetchMoreResult: option('parsedData), + variables: option('variables), + }; + + type t_fetchMoreOptions('jsData, 'variables) = { + query: option(Graphql.Language.documentNode), + // ...extends FetchMoreQueryOptions + variables: option('variables), + context: option(Js.Json.t), + // ...extends FetchMoreOptions + updateQuery: + option( + ( + 'jsData, + t_fetchMoreOptions_updateQueryOptions('jsData, 'variables) + ) => + 'jsData, + ), + }; + + // export interface QueryResult extends ObservableQueryFields { + // client: ApolloClient; + // data: TData | undefined; + // error?: ApolloError; + // loading: boolean; + // networkStatus: NetworkStatus; + // called: true; + // } + type t('jsData, 'variables) = { + called: bool, + client: ApolloClient__ApolloClient.t, + data: option('jsData), + error: option(ApolloError.t), + loading: bool, + networkStatus: NetworkStatus.t, + // ...extends ObservableQueryFields + fetchMore: + t_fetchMoreOptions('jsData, 'variables) => + Js.Promise.t(ApolloQueryResult.t('jsData)), + }; + }; + + type t('parsedData, 'variables) = { + called: bool, + client: ApolloClient__ApolloClient.t, + data: option('parsedData), + error: option(ApolloError.t), + fetchMore: + ( + ~context: Js.Json.t=?, + ~variables: 'variables=?, + ~updateQuery: ( + 'parsedData, + Js_.t_fetchMoreOptions_updateQueryOptions( + 'parsedData, + 'variables, + ) + ) => + 'parsedData + =?, + unit + ) => + Js.Promise.t(ApolloQueryResult.t('parsedData)), + loading: bool, + networkStatus: NetworkStatus.t, + }; + + let fromJs: + ( + Js_.t('jsData, 'variables), + ~parse: 'jsData => 'parsedData, + ~serialize: 'parsedData => 'jsData + ) => + t('parsedData, 'variables) = + (js, ~parse, ~serialize) => { + called: js.called, + client: js.client, + data: js.data->Belt.Option.map(parse), + error: js.error, + fetchMore: + (~context=?, ~variables=?, ~updateQuery as jsUpdateQuery=?, ()) => { + js.fetchMore({ + context, + query: None, + updateQuery: + jsUpdateQuery->Belt.Option.map( + ( + jsUpdateQuery, + previousResult, + Js_.{fetchMoreResult, variables}, + ) => + jsUpdateQuery( + parse(previousResult), + { + fetchMoreResult: fetchMoreResult->Belt.Option.map(parse), + variables, + }, + ) + ->serialize + ), + variables, + }) + ->Js.Promise.then_( + jsResult => + Js.Promise.resolve(ApolloQueryResult.fromJs(jsResult, ~parse)), + _, + ); + }, + loading: js.loading, + networkStatus: js.networkStatus, + }; +}; + +module SubscriptionResult = { + module Js_ = { + // export interface SubscriptionResult { + // loading: boolean; + // data?: TData; + // error?: ApolloError; + // } + type t('jsData) = { + loading: bool, + data: option('jsData), + error: option(ApolloError.Js_.t), + }; + }; + + type t('data) = { + loading: bool, + data: option('data), + error: option(ApolloError.Js_.t), + }; + + let fromJs: (Js_.t('jsData), ~parse: 'jsData => 'data) => t('data) = + (js, ~parse) => { + loading: js.loading, + data: js.data->Belt.Option.map(parse), + error: js.error, + }; +}; + +module OnSubscriptionDataOptions = { + module Js_ = { + // export interface OnSubscriptionDataOptions { + // client: ApolloClient; + // subscriptionData: SubscriptionResult; + // } + type t('jsData) = { + client: ApolloClient__ApolloClient.t, + subscriptionData: SubscriptionResult.Js_.t('jsData), + }; + }; + + type t('data) = { + client: ApolloClient__ApolloClient.t, + subscriptionData: SubscriptionResult.t('data), + }; + + let fromJs: (Js_.t('jsData), ~parse: 'jsData => 'data) => t('data) = + (js, ~parse) => { + client: js.client, + subscriptionData: + js.subscriptionData->SubscriptionResult.fromJs(~parse), + }; +}; + +module BaseSubscriptionOptions = { + module Js_ = { + // export interface BaseSubscriptionOptions { + // variables?: TVariables; + // fetchPolicy?: FetchPolicy; + // shouldResubscribe?: boolean | ((options: BaseSubscriptionOptions) => boolean); + // client?: ApolloClient; + // skip?: boolean; + // onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; + // onSubscriptionComplete?: () => void; + // } + type t('jsData, 'variables) = { + variables: option('variables), + fetchPolicy: option(FetchPolicy.t), + shouldResubscribe: option((. t('jsData, 'variables)) => bool), + client: option(ApolloClient__ApolloClient.t), + skip: option(bool), + onSubscriptionData: + option((. OnSubscriptionDataOptions.Js_.t('jsData)) => unit), + onSubscriptionComplete: option(unit => unit), + }; + }; + + type t('data, 'variables) = { + variables: option('variables), + fetchPolicy: option(FetchPolicy.t), + shouldResubscribe: option(t('data, 'variables) => bool), + client: option(ApolloClient__ApolloClient.t), + skip: option(bool), + onSubscriptionData: option(OnSubscriptionDataOptions.t('data) => unit), + onSubscriptionComplete: option(unit => unit), + }; + + let fromJs: Js_.t('jsData, 'variables) => t('data, 'variables) = + js => { + variables: js.variables, + fetchPolicy: js.fetchPolicy, + // shouldResubscribe: what to do here? + shouldResubscribe: Obj.magic(js.shouldResubscribe), + client: js.client, + skip: js.skip, + // onSubscriptionData: what to do here? + onSubscriptionData: Obj.magic(js.onSubscriptionData), + onSubscriptionComplete: js.onSubscriptionComplete, + }; +}; + +module SubscriptionHookOptions = { + module Js_ = { + // export interface SubscriptionHookOptions extends BaseSubscriptionOptions { + // subscription?: DocumentNode; + // } + type t('jsData, 'variables) = { + subscription: option(Graphql.documentNode), + // ...extends BaseSubscriptionOptions + // Intentionally restricted to not be non-optional. `option(unit)` does not compile cleanly to `undefined` + variables: 'variables, + fetchPolicy: option(FetchPolicy.t), + shouldResubscribe: + option( + (. BaseSubscriptionOptions.Js_.t('jsData, 'variables)) => bool, + ), + client: option(ApolloClient__ApolloClient.t), + skip: option(bool), + onSubscriptionData: + option((. OnSubscriptionDataOptions.Js_.t('jsData)) => unit), + onSubscriptionComplete: option(unit => unit), + }; + }; + + type t('data, 'variables) = { + subscription: option(Graphql.documentNode), + variables: 'variables, + fetchPolicy: option(FetchPolicy.t), + shouldResubscribe: + option(BaseSubscriptionOptions.t('data, 'variables) => bool), + client: option(ApolloClient__ApolloClient.t), + skip: option(bool), + onSubscriptionData: option(OnSubscriptionDataOptions.t('data) => unit), + onSubscriptionComplete: option(unit => unit), + }; + + let toJs: + (t('data, 'variables), ~parse: 'jsData => 'data) => + Js_.t('jsData, 'variables) = + (t, ~parse) => { + subscription: t.subscription, + variables: t.variables, + fetchPolicy: t.fetchPolicy, + shouldResubscribe: + t.shouldResubscribe + ->Belt.Option.map(shouldResubscribe => + (. jsBaseSubscriptionOptions) => + shouldResubscribe( + jsBaseSubscriptionOptions->BaseSubscriptionOptions.fromJs, + ) + ), + client: t.client, + skip: t.skip, + onSubscriptionData: + t.onSubscriptionData + ->Belt.Option.map(onSubscriptionData => + (. jsOnSubscriptionDataOptions) => + onSubscriptionData( + jsOnSubscriptionDataOptions->OnSubscriptionDataOptions.fromJs( + ~parse, + ), + ) + ), + onSubscriptionComplete: t.onSubscriptionComplete, + }; +}; diff --git a/src/reason/ApolloClient__Reason.re b/src/reason/ApolloClient__Reason.re new file mode 100644 index 0000000..f3e3c0a --- /dev/null +++ b/src/reason/ApolloClient__Reason.re @@ -0,0 +1,2 @@ +module Types = ApolloClient__Reason_Types; +module Utils = ApolloClient__Reason_Utils; diff --git a/src/reason/ApolloClient__Reason_Types.re b/src/reason/ApolloClient__Reason_Types.re new file mode 100644 index 0000000..7b1272b --- /dev/null +++ b/src/reason/ApolloClient__Reason_Types.re @@ -0,0 +1,27 @@ +type parse('raw_t, 't) = 'raw_t => 't; +type serialize('t, 'raw_t) = 't => 'raw_t; +type query = string; + +type graphqlDefinition('t, 'raw_t) = ( + parse('raw_t, 't), + query, + serialize('t, 'raw_t), +); + +module type Operation = { + let query: string; + + module Raw: { + type t; + type t_variables; + }; + type t; + + let parse: Raw.t => t; + let serialize: t => Raw.t; +}; + +module type OperationNoRequiredVars = { + include Operation; + let makeDefaultVariables: unit => Raw.t_variables; +}; diff --git a/src/reason/ApolloClient__Reason_Utils.re b/src/reason/ApolloClient__Reason_Utils.re new file mode 100644 index 0000000..06bb0cc --- /dev/null +++ b/src/reason/ApolloClient__Reason_Utils.re @@ -0,0 +1,13 @@ +let exceptionsAsApolloError = () => (); + +let useGuaranteedMemo1 = (f, dependency) => { + let value = React.useRef(f()); + let previousDependency = React.useRef(dependency); + + if (dependency !== previousDependency->React.Ref.current) { + value->React.Ref.setCurrent(f()); + previousDependency->React.Ref.setCurrent(dependency); + }; + + value->React.Ref.current; +}; diff --git a/src/utilities/ApolloClient__Utilities.re b/src/utilities/ApolloClient__Utilities.re new file mode 100644 index 0000000..41ce668 --- /dev/null +++ b/src/utilities/ApolloClient__Utilities.re @@ -0,0 +1 @@ +let getOperationDefinition = ApolloClient__Utilities_Graphql_GetFromAst.getOperationDefinition; diff --git a/src/utilities/graphql/ApolloClient__Utilities_Graphql_Fragments.re b/src/utilities/graphql/ApolloClient__Utilities_Graphql_Fragments.re new file mode 100644 index 0000000..36b36b6 --- /dev/null +++ b/src/utilities/graphql/ApolloClient__Utilities_Graphql_Fragments.re @@ -0,0 +1,9 @@ +module FragmentDefinitionNode = ApolloClient__Graphql_Language_Ast.FragmentDefinitionNode; + +module FragmentMap = { + module Js_ = { + type t = Js.Dict.t(FragmentDefinitionNode.t); + }; + + type t = Js_.t; +}; diff --git a/src/utilities/graphql/ApolloClient__Utilities_Graphql_GetFromAst.re b/src/utilities/graphql/ApolloClient__Utilities_Graphql_GetFromAst.re new file mode 100644 index 0000000..91b57f2 --- /dev/null +++ b/src/utilities/graphql/ApolloClient__Utilities_Graphql_GetFromAst.re @@ -0,0 +1,7 @@ +module Graphql = ApolloClient__Graphql; + +[@bs.module "@apollo/client/utilities"] +external getOperationDefinition: + Graphql.documentNode => + option(Graphql.Language.Ast.OperationDefinitionNode.t) = + "getOperationDefinition";