types: strict tsconfig and @apollo/client v3 docs (#1062)

This commit is contained in:
javiertury
2020-10-18 12:30:53 +02:00
committed by GitHub
parent 6f291101bd
commit fb1ab4d25a
36 changed files with 377 additions and 422 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ A clear and concise description of what you expected to happen.
**Versions**
vue:
vue-apollo:
apollo-client:
@apollo/client:
**Additional context**
Add any other context about the problem here.
+1 -3
View File
@@ -137,9 +137,7 @@ Here is an example:
// apollo.js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
import VueApollo from '@vue/apollo-option'
// Install the vue plugin
@@ -55,10 +55,10 @@ export default {
## Network Errors
When using Apollo Link, the ability to handle network errors is way more powerful. The best way to do this is to use the `apollo-link-error` to catch and handle server errors, network errors, and GraphQL errors. If you would like to combine with other links, see [composing links](https://www.apollographql.com/docs/link/composition).
When using Apollo Link, the ability to handle network errors is way more powerful. The best way to do this is to use the `@apollo/client/link/error` to catch and handle server errors, network errors, and GraphQL errors. If you would like to combine with other links, see [composing links](https://www.apollographql.com/docs/link/composition).
```js
import { onError } from 'apollo-link-error'
import { onError } from '@apollo/client/link/error'
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
@@ -75,7 +75,7 @@ const link = onError(({ graphQLErrors, networkError }) => {
You can also use the `logErrorMessages` function from the `@vue/apollo-util` package to format the error in the browser console:
```js
import { onError } from 'apollo-link-error'
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
@@ -90,7 +90,7 @@ Example error:
If you are using Webpack or Vue CLI, it's a good idea to only use it in development:
```js
import { onError } from 'apollo-link-error'
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
@@ -121,4 +121,4 @@ onError(({ response, operation }) => {
response.errors = null
}
})
```
```
@@ -203,105 +203,27 @@ query {
}
```
In the query above, `allPeople` returns a result of type `Character[]`. Both `Jedi` and `Droid` are possible concrete types of `Character`, but on the client there is no way to know that without having some information about the schema. By default, Apollo Client's cache will use a heuristic fragment matcher, which assumes that a fragment matched if the result included all the fields in its selection set, and didn't match when any field was missing. This works in most cases, but it also means that Apollo Client cannot check the server response for you, and it cannot tell you when you're manually writing invalid data into the store using `update`, `updateQuery`, `writeQuery`, etc. Also, the heuristic fragment matcher will not work accurately when using fragments with unions or interfaces. Apollo Client will let you know this with a console warning (in development), if it attempts to use the default heuristic fragment matcher with unions/interfaces. The `IntrospectionFragmentMatcher` is the solution for working with unions/interfaces, and is explained in more detail below.
In the query above, `allPeople` returns a result of type `Character[]`. Both `Jedi` and `Droid` are possible concrete types of `Character`, but on the client there is no way to know that without having some information about the schema. By default, Apollo Client's cache will use a heuristic fragment matcher, which assumes that a fragment matched if the result included all the fields in its selection set, and didn't match when any field was missing. This works in most cases, but it also means that Apollo Client cannot check the server response for you, and it cannot tell you when you're manually writing invalid data into the store using `update`, `updateQuery`, `writeQuery`, etc. To inform the cache store about these polymorphic relationships, you need to pass `possibleTypes` option to `InMemoryCache` below.
The section below explains how to pass the necessary schema knowledge to the Apollo Client cache so unions and interfaces can be accurately matched and results validated before writing them into the store.
To support result validation and accurate fragment matching on unions and interfaces, a special fragment matcher called the `IntrospectionFragmentMatcher` can be used. If there are any changes related to union or interface types in your schema, you will have to update the fragment matcher accordingly.
We recommend setting up a build step that extracts the necessary information from the schema into a JSON file, where it can be imported from when constructing the fragment matcher. To set it up, follow the three steps below:
We recommend setting up a build step that extracts the necessary information from the schema into a JSON file, where it can be imported from when constructing the cache. To set it up, follow the steps below:
1. Query your server / schema to obtain the necessary information about unions and interfaces and write it to a file.
You can automate this or set this as a script to run at build time.
Read the documentation about how to [extract possibleTypes automatically](https://www.apollographql.com/docs/react/data/fragments/#generating-possibletypes-automatically) using an introspection query. Or use the plugin [fragment-matcher](https://graphql-code-generator.com/docs/plugins/fragment-matcher) for graphql-codegen and configure it for [apollo client 3](https://graphql-code-generator.com/docs/plugins/fragment-matcher#usage-with-apollo-client-3).
If you want to auto-generate the introspection result, there's a tool called [GraphQL Code Generator](https://graphql-code-generator.com) that does it. Define where your GraphQL Schema is available and where to write the file:
```yaml
# codegen.yml
schema: YOUR_API
overwrite: true
generates:
./fragmentTypes.json:
plugins:
- fragment-matcher
```
With all of that, you simply run:
```shell
gql-gen
```
> To learn more, you can read the ["Fragment Matcher" chapter](https://graphql-code-generator.com/docs/plugins/fragment-matcher).
In order to introspect the server manually, set this as a script to run at build time.
2. Use `possibleTypes.json` to configure your cache during construction. Then, you pass your newly configured cache to `ApolloClient` to complete the process.
```js
const fetch = require('node-fetch')
const fs = require('fs')
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import possibleTypes from './possibleTypes.json'
fetch(`${YOUR_API_HOST}/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
variables: {},
query: `
{
__schema {
types {
kind
name
possibleTypes {
name
}
}
}
}
`,
}),
})
.then(result => result.json())
.then(result => {
// here we're filtering out any type information unrelated to unions or interfaces
const filteredData = result.data.__schema.types.filter(
type => type.possibleTypes !== null,
)
result.data.__schema.types = filteredData
fs.writeFile('./fragmentTypes.json', JSON.stringify(result.data), err => {
if (err) {
console.error('Error writing fragmentTypes file', err)
} else {
console.log('Fragment types successfully extracted!')
}
})
})
```
2. Create a new IntrospectionFragment matcher by passing in the `fragmentTypes.json` file you just created. You'll want to do this in the same file where you initialize the cache for Apollo Client.
```js
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionQueryResultData from './fragmentTypes.json'
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
})
```
3. Pass in the newly created `IntrospectionFragmentMatcher` to configure your cache during construction. Then, you pass your newly configured cache to `ApolloClient` to complete the process.
```js
import ApolloClient from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
// add fragmentMatcher code from step 2 here
const cache = new InMemoryCache({ fragmentMatcher })
const cache = new InMemoryCache({ possibleTypes })
const httpLink = createHttpLink({ uri });
const client = new ApolloClient({
cache,
link: new HttpLink(),
link: httpLink,
})
```
@@ -53,26 +53,14 @@ A future version of Apollo or GraphQL might include support for live queries, wh
## Client setup
The most popular transport for GraphQL subscriptions today is [`subscriptions-transport-ws`](https://github.com/apollographql/subscriptions-transport-ws). This package is maintained by the Apollo community, but can be used with any client or server GraphQL implementation. In this article, we'll explain how to set it up on the client, but you'll also need a server implementation. You can [read about how to use subscriptions with a JavaScript server](https://www.apollographql.com/docs/graphql-subscriptions/setup), or enjoy subscriptions set up out of the box if you are using a GraphQL backend as a service like [Graphcool](https://www.graph.cool/docs/tutorials/worldchat-subscriptions-example-ui0eizishe/).
In this article, we'll explain how to set it up on the client, but you'll also need a server implementation. You can [read about how to use subscriptions with a JavaScript server](https://www.apollographql.com/docs/graphql-subscriptions/setup), or enjoy subscriptions set up out of the box if you are using a GraphQL backend as a service like [Graphcool](https://www.graph.cool/docs/tutorials/worldchat-subscriptions-example-ui0eizishe/).
Let's look at how to add support for this transport to Apollo Client.
First, install the WebSocket Apollo Link (`apollo-link-ws`) from npm:
```shell
npm install --save apollo-link-ws subscriptions-transport-ws
```
Or:
```shell
yarn add apollo-link-ws subscriptions-transport-ws
```
Then, initialize a GraphQL subscriptions transport link:
First, initialize a GraphQL web socket link:
```js
import { WebSocketLink } from "apollo-link-ws";
import { WebSocketLink } from "@apollo/client/link/ws";
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
@@ -85,10 +73,9 @@ const wsLink = new WebSocketLink({
We need to either use the `WebSocketLink` or the `HttpLink` depending on the operation type:
```js
import { split } from "apollo-link";
import { HttpLink } from "apollo-link-http";
import { WebSocketLink } from "apollo-link-ws";
import { getMainDefinition } from "apollo-utilities";
import { HttpLink, split } from "@apollo/client/core";
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
// Create an http link:
const httpLink = new HttpLink({
@@ -569,7 +556,7 @@ subscribeToMore(() => ({
In many cases it is necessary to authenticate clients before allowing them to receive subscription results. To do this, the `SubscriptionClient` constructor accepts a `connectionParams` field, which passes a custom object that the server can use to validate the connection before setting up any subscriptions.
```js
import { WebSocketLink } from 'apollo-link-ws';
import { WebSocketLink } from "@apollo/client/link/ws";
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
@@ -4,21 +4,11 @@
*For the server implementation, you can take a look at [this simple example](https://github.com/Akryum/apollo-server-example).*
To enable the websocket-based subscription, a bit of additional setup is required:
```
npm install --save apollo-link-ws apollo-utilities
```
```js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
// New Imports
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
import VueApollo from '@vue/apollo-option'
+3 -38
View File
@@ -16,55 +16,20 @@ Then you can skip to next section: [Basic Usage](./apollo/).
## Manual installation
You can either use [Apollo Boost](#apollo-boost) or [Apollo Client directly](#apollo-client-full-configuration) (more configuration work).
### Apollo Boost
Apollo Boost is a zero-config way to start using Apollo Client. It includes some sensible defaults, such as our recommended `InMemoryCache` and `HttpLink`, which come configured for you with our recommended settings and it's perfect for starting to develop fast.
Install it alongside `vue-apollo` and `graphql`:
```shell
npm install --save graphql apollo-boost
npm install --save graphql graphql-tag @apollo/client
```
Or:
```shell
yarn add graphql apollo-boost
yarn add graphql graphql-tag @apollo/client
```
In your app, create an `ApolloClient` instance:
```js
import ApolloClient from 'apollo-boost'
const apolloClient = new ApolloClient({
// You should use an absolute URL here
uri: 'https://api.graphcms.com/simple/v1/awesomeTalksClone'
})
```
### Apollo client full configuration
If you want some more fine grained control install these packages instead of apollo-boost:
```shell
npm install --save graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
Or:
```shell
yarn add graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
In your app, create an `ApolloClient` instance:
```js
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
// HTTP connection to the API
const httpLink = createHttpLink({
+18 -16
View File
@@ -1,4 +1,4 @@
# From vue-apollo 2 and Apollo 1
# From vue-apollo 3
The main changes are related to the apollo client setup. Your components code shouldn't be affected. Apollo now uses a more flexible [apollo-link](https://github.com/apollographql/apollo-link) system that allows compositing multiple links together to add more features (like batching, offline support and more).
@@ -9,13 +9,13 @@ The main changes are related to the apollo client setup. Your components code sh
Before:
```
npm install --save vue-apollo apollo-client
npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
After:
```
npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
npm install --save vue-apollo@next @apollo/client
```
### Imports
@@ -24,7 +24,9 @@ Before:
```js
import Vue from 'vue'
import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from '@vue/apollo-option'
```
@@ -32,9 +34,7 @@ After:
```js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
import VueApollo from '@vue/apollo-option'
```
@@ -168,29 +168,31 @@ Query reducers have been removed. Use the `update` API to update the cache now.
Before:
```
npm install --save subscriptions-transport-ws
npm install --save apollo-link-ws apollo-utilities
```
After:
```
npm install --save apollo-link-ws apollo-utilities
npm install --save @apollo/client
```
### Imports
Before:
```js
import { SubscriptionClient, addGraphQLSubscriptions } from 'subscriptions-transport-ws'
```
After:
```js
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
```
Learn more at the [official apollo documentation](https://www.apollographql.com/docs/react/2.0-migration.html).
After:
```js
import { split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
```
Learn more at the [official apollo documentation](https://www.apollographql.com/docs/react/2.0-migration.html).
@@ -4,21 +4,11 @@
*关于服务端实现,你可以看看 [这个简单的示例](https://github.com/Akryum/apollo-server-example)。*
要启用基于 websocket 的订阅,需要做一些额外的设置:
```
npm install --save apollo-link-ws apollo-utilities
```
```js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
// 新的引入文件
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
import VueApollo from '@vue/apollo-option'
+3 -38
View File
@@ -18,55 +18,20 @@ vue add apollo
### 1. Apollo Client
你可以使用 [Apollo Boost](#apollo-boost) 或 [直接使用 Apollo Client](#apollo-client-full-configuration)(需要更多配置工作)。
#### Apollo Boost
Apollo Boost 是一种零配置开始使用 Apollo Client 的方式。它包含一些实用的默认值,例如我们推荐的 `InMemoryCache``HttpLink`,它非常适合用于快速启动开发。
将它与 `vue-apollo``graphql` 一起安装:
```
npm install --save vue-apollo graphql apollo-boost
npm install --save graphql graphql-tag @apollo/client
```
或:
```
yarn add vue-apollo graphql apollo-boost
yarn add graphql graphql-tag @apollo/client
```
在你的应用中创建一个 `ApolloClient` 实例:
```js
import ApolloClient from 'apollo-boost'
const apolloClient = new ApolloClient({
// 你需要在这里使用绝对路径
uri: 'https://api.graphcms.com/simple/v1/awesomeTalksClone'
})
```
#### Apollo 客户端完整配置
如果你想要更细粒度的控制,安装这些包来代替 `apollo-boost`
```
npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
或:
```
yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
在你的应用中创建一个 `ApolloClient` 实例:
```js
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
// 与 API 的 HTTP 连接
const httpLink = createHttpLink({
+1 -3
View File
@@ -125,9 +125,7 @@ export default {
// apollo.js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
import VueApollo from '@vue/apollo-option'
// 安装 vue 插件
+17 -15
View File
@@ -9,13 +9,13 @@
之前:
```
npm install --save vue-apollo apollo-client
npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
```
之后:
```
npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
npm install --save vue-apollo@next @apollo/client
```
### 导入
@@ -24,7 +24,9 @@ npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link
```js
import Vue from 'vue'
import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from '@vue/apollo-option'
```
@@ -32,9 +34,7 @@ import VueApollo from '@vue/apollo-option'
```js
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
import VueApollo from '@vue/apollo-option'
```
@@ -111,7 +111,7 @@ const apolloClient = new ApolloClient({
```js
// 创建 apollo 客户端
const apolloClient = new ApolloClient({
networkInterface: createBatchingNetworkInterface({
link: createHttpLink({
uri: 'http://localhost:3020/graphql',
}),
connectToDevTools: true,
@@ -174,23 +174,25 @@ npm install --save subscriptions-transport-ws
之后:
```
npm install --save apollo-link-ws apollo-utilities
npm install --save @apollo/client
```
### 导入
之前:
```js
import { SubscriptionClient, addGraphQLSubscriptions } from 'subscriptions-transport-ws'
```
之后:
```js
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
```
了解更多请查看 [apollo 官方文档](https://www.apollographql.com/docs/react/2.0-migration.html)。
之后:
```js
import { split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
```
了解更多请查看 [apollo 官方文档](https://www.apollographql.com/docs/react/2.0-migration.html)。
@@ -1,7 +1,7 @@
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
import { logErrorMessages } from '@vue/apollo-util'
// import { print } from 'graphql'
import { onError } from 'apollo-link-error'
import { onError } from '@apollo/client/link/error'
// Name of the localStorage item
const AUTH_TOKEN = 'apollo-token'
@@ -46,6 +46,7 @@
"vue": "^2.6.10"
},
"devDependencies": {
"@apollo/client": "^3.0.0",
"@types/throttle-debounce": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
@@ -15,14 +15,14 @@ export interface UseApolloClientReturn<TCacheShape> {
readonly client: ApolloClient<TCacheShape>
}
function resolveDefaultClient<T> (providedApolloClients: ClientDict<T>, providedApolloClient: ApolloClient<T>): NullableApolloClient<T> {
function resolveDefaultClient<T> (providedApolloClients: ClientDict<T> | null, providedApolloClient: ApolloClient<T> | null): NullableApolloClient<T> {
const resolvedClient = providedApolloClients
? providedApolloClients.default
: providedApolloClient
: (providedApolloClient ?? undefined)
return resolvedClient
}
function resolveClientWithId<T> (providedApolloClients: ClientDict<T>, clientId: ClientId): NullableApolloClient<T> {
function resolveClientWithId<T> (providedApolloClients: ClientDict<T> | null, clientId: ClientId): NullableApolloClient<T> {
if (!providedApolloClients) {
throw new Error(`No apolloClients injection found, tried to resolve '${clientId}' clientId`)
}
@@ -35,10 +35,10 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
if (!getCurrentInstance()) {
resolveImpl = () => currentApolloClient
} else {
const providedApolloClients: ClientDict<TCacheShape> = inject(ApolloClients, null)
const providedApolloClient: ApolloClient<TCacheShape> = inject(DefaultApolloClient, null)
const providedApolloClients: ClientDict<TCacheShape> | null = inject(ApolloClients, null)
const providedApolloClient: ApolloClient<TCacheShape> | null = inject(DefaultApolloClient, null)
resolveImpl = (id: ClientId) => {
resolveImpl = (id?: ClientId) => {
if (currentApolloClient) {
return currentApolloClient
} else if (id) {
@@ -48,10 +48,10 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
}
}
function resolveClient (id: ClientId = clientId) {
function resolveClient (id: ClientId | undefined = clientId) {
const client = resolveImpl(id)
if (!client) {
throw new Error(`Apollo client with id ${id || 'default'} not found. Use provideApolloClient() if you are outside of a component setup.`)
throw new Error(`Apollo client with id ${id ?? 'default'} not found. Use provideApolloClient() if you are outside of a component setup.`)
}
return client
}
@@ -70,7 +70,7 @@ export function provideApolloClient<TCacheShape = any> (client: ApolloClient<TCa
currentApolloClient = client
return function <TFnResult = any> (fn: () => TFnResult) {
const result = fn()
currentApolloClient = null
currentApolloClient = undefined
return result
}
}
@@ -6,7 +6,7 @@ export function useLazyQuery<
TResult = any,
TVariables = any,
> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables?: VariablesParameter<TVariables>,
options?: OptionsParameter<TResult, TVariables>,
) {
@@ -1,5 +1,5 @@
import { DocumentNode } from 'graphql'
import { MutationOptions, OperationVariables, FetchResult } from '@apollo/client/core'
import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode } from '@apollo/client/core'
import { ref, onBeforeUnmount, isRef, Ref } from 'vue-demi'
import { useApolloClient } from './useApolloClient'
import { ReactiveFunction } from './util/ReactiveFunction'
@@ -16,17 +16,17 @@ export interface UseMutationOptions<
clientId?: string
}
type DocumentParameter = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode>
type DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables>> | ReactiveFunction<TypedDocumentNode<TResult, TVariables>>
type OptionsParameter<TResult, TVariables> = UseMutationOptions<TResult, TVariables> | Ref<UseMutationOptions<TResult, TVariables>> | ReactiveFunction<UseMutationOptions<TResult, TVariables>>
export type MutateOverrideOptions = Pick<UseMutationOptions<any, OperationVariables>, 'update' | 'optimisticResponse' | 'context' | 'updateQueries' | 'refetchQueries' | 'awaitRefetchQueries' | 'errorPolicy' | 'fetchPolicy' | 'clientId'>
export type MutateResult<TResult> = Promise<FetchResult<TResult, Record<string, any>, Record<string, any>>>
export type MutateFunction<TResult, TVariables> = (variables?: TVariables, overrideOptions?: MutateOverrideOptions) => MutateResult<TResult>
export type MutateFunction<TResult, TVariables> = (variables?: TVariables | null, overrideOptions?: MutateOverrideOptions) => MutateResult<TResult>
export interface UseMutationReturn<TResult, TVariables> {
mutate: MutateFunction<TResult, TVariables>
loading: Ref<boolean>
error: Ref<Error>
error: Ref<Error | null>
called: Ref<boolean>
onDone: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
off: () => void
@@ -40,14 +40,12 @@ export function useMutation<
TResult = any,
TVariables extends OperationVariables = OperationVariables
> (
document: DocumentParameter,
options?: OptionsParameter<TResult, TVariables>,
document: DocumentParameter<TResult, TVariables>,
options: OptionsParameter<TResult, TVariables> = {},
): UseMutationReturn<TResult, TVariables> {
if (!options) options = {}
const loading = ref<boolean>(false)
trackMutation(loading)
const error = ref<Error>(null)
const error = ref<Error | null>(null)
const called = ref<boolean>(false)
const doneEvent = useEventHook<FetchResult<TResult, Record<string, any>, Record<string, any>>>()
@@ -56,7 +54,7 @@ export function useMutation<
// Apollo Client
const { resolveClient } = useApolloClient()
async function mutate (variables?: TVariables, overrideOptions: Omit<UseMutationOptions<TResult, TVariables>, 'variables'> = {}) {
async function mutate (variables?: TVariables | null, overrideOptions: Omit<UseMutationOptions<TResult, TVariables>, 'variables'> = {}) {
let currentDocument: DocumentNode
if (typeof document === 'function') {
currentDocument = document()
@@ -79,14 +77,16 @@ export function useMutation<
loading.value = true
called.value = true
try {
const result = await client.mutate<TResult>({
const result = await client.mutate<TResult, TVariables>({
mutation: currentDocument,
...currentOptions,
...overrideOptions,
variables: {
...currentOptions.variables || {},
...variables || {},
},
variables: (variables ?? currentOptions.variables)
? {
...(currentOptions.variables as TVariables),
...(variables as TVariables),
}
: undefined,
})
loading.value = false
doneEvent.trigger(result)
+40 -36
View File
@@ -20,6 +20,7 @@ import {
FetchMoreQueryOptions,
FetchMoreOptions,
ObservableSubscription,
TypedDocumentNode,
} from '@apollo/client/core'
import { throttle, debounce } from 'throttle-debounce'
import { useApolloClient } from './useApolloClient'
@@ -29,6 +30,8 @@ import { paramToReactive } from './util/paramToReactive'
import { useEventHook } from './util/useEventHook'
import { trackQuery } from './util/loadingTracking'
import type { CurrentInstance } from './util/types'
export interface UseQueryOptions<
// eslint-disable-next-line @typescript-eslint/no-unused-vars
TResult = any,
@@ -47,26 +50,26 @@ interface SubscribeToMoreItem {
}
// Parameters
export type DocumentParameter = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode>
export type DocumentParameter<TResult, TVariables = undefined> = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables>> | ReactiveFunction<TypedDocumentNode<TResult, TVariables>>
export type VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>
export type OptionsParameter<TResult, TVariables> = UseQueryOptions<TResult, TVariables> | Ref<UseQueryOptions<TResult, TVariables>> | ReactiveFunction<UseQueryOptions<TResult, TVariables>>
// Return
export interface UseQueryReturn<TResult, TVariables> {
result: Ref<TResult>
result: Ref<TResult | undefined>
loading: Ref<boolean>
networkStatus: Ref<number>
error: Ref<Error>
networkStatus: Ref<number | undefined>
error: Ref<Error | null>
start: () => void
stop: () => void
restart: () => void
forceDisabled: Ref<boolean>
document: Ref<DocumentNode>
variables: Ref<TVariables>
variables: Ref<TVariables | undefined>
options: UseQueryOptions<TResult, TVariables> | Ref<UseQueryOptions<TResult, TVariables>>
query: Ref<ObservableQuery<TResult, TVariables>>
refetch: (variables?: TVariables) => Promise<ApolloQueryResult<TResult>>
fetchMore: <K extends keyof TVariables>(options: FetchMoreQueryOptions<TVariables, K> & FetchMoreOptions<TResult, TVariables>) => Promise<ApolloQueryResult<TResult>>
query: Ref<ObservableQuery<TResult, TVariables> | null | undefined>
refetch: (variables?: TVariables) => Promise<ApolloQueryResult<TResult>> | undefined
fetchMore: <K extends keyof TVariables>(options: FetchMoreQueryOptions<TVariables, K> & FetchMoreOptions<TResult, TVariables>) => Promise<ApolloQueryResult<TResult>> | undefined
subscribeToMore: <TSubscriptionVariables = OperationVariables, TSubscriptionData = TResult>(options: SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData> | Ref<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>> | ReactiveFunction<SubscribeToMoreOptions<TResult, TSubscriptionVariables, TSubscriptionData>>) => void
onResult: (fn: (param: ApolloQueryResult<TResult>) => void) => {
off: () => void
@@ -80,21 +83,21 @@ export interface UseQueryReturn<TResult, TVariables> {
* Use a query that does not require variables or options.
* */
export function useQuery<TResult = any> (
document: DocumentParameter
document: DocumentParameter<TResult, undefined>
): UseQueryReturn<TResult, undefined>
/**
* Use a query that has optional variables but not options
*/
export function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter
document: DocumentParameter<TResult, TVariables>
): UseQueryReturn<TResult, TVariables>
/**
* Use a query that has required variables but not options
*/
export function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables: VariablesParameter<TVariables>
): UseQueryReturn<TResult, TVariables>
@@ -102,7 +105,7 @@ export function useQuery<TResult = any, TVariables extends OperationVariables =
* Use a query that requires options but not variables.
*/
export function useQuery<TResult = any> (
document: DocumentParameter,
document: DocumentParameter<TResult, undefined>,
variables: undefined | null,
options: OptionsParameter<TResult, null>,
): UseQueryReturn<TResult, null>
@@ -111,7 +114,7 @@ export function useQuery<TResult = any> (
* Use a query that requires variables and options.
*/
export function useQuery<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables: VariablesParameter<TVariables>,
options: OptionsParameter<TResult, TVariables>,
): UseQueryReturn<TResult, TVariables>
@@ -120,7 +123,7 @@ export function useQuery<
TResult,
TVariables extends OperationVariables
> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables?: VariablesParameter<TVariables>,
options?: OptionsParameter<TResult, TVariables>,
): UseQueryReturn<TResult, TVariables> {
@@ -131,17 +134,15 @@ export function useQueryImpl<
TResult,
TVariables extends OperationVariables
> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables?: VariablesParameter<TVariables>,
options?: OptionsParameter<TResult, TVariables>,
options: OptionsParameter<TResult, TVariables> = {},
lazy: boolean = false,
): UseQueryReturn<TResult, TVariables> {
// Is on server?
const vm: any = getCurrentInstance()
const isServer = vm?.$isServer
const vm = getCurrentInstance() as CurrentInstance | null
const isServer = vm?.$isServer ?? false
if (variables == null) variables = ref()
if (options == null) options = {}
const documentRef = paramToRef(document)
const variablesRef = paramToRef(variables)
const optionsRef = paramToReactive(options)
@@ -150,9 +151,9 @@ export function useQueryImpl<
/**
* Result from the query
*/
const result = ref<TResult>()
const result = ref<TResult | undefined>()
const resultEvent = useEventHook<ApolloQueryResult<TResult>>()
const error = ref<Error>(null)
const error = ref<Error | null>(null)
const errorEvent = useEventHook<Error>()
// Loading
@@ -168,7 +169,7 @@ export function useQueryImpl<
let firstResolve: Function | undefined
let firstReject: Function | undefined
onServerPrefetch?.(() => {
if (!isEnabled.value || (isServer && currentOptions.value.prefetch === false)) return
if (!isEnabled.value || (isServer && currentOptions.value?.prefetch === false)) return
return new Promise((resolve, reject) => {
firstResolve = () => {
@@ -189,8 +190,8 @@ export function useQueryImpl<
// Query
const query: Ref<ObservableQuery<TResult, TVariables>> = ref()
let observer: ObservableSubscription
const query: Ref<ObservableQuery<TResult, TVariables> | null | undefined> = ref()
let observer: ObservableSubscription | undefined
let started = false
/**
@@ -199,7 +200,7 @@ export function useQueryImpl<
function start () {
if (
started || !isEnabled.value ||
(isServer && currentOptions.value.prefetch === false)
(isServer && currentOptions.value?.prefetch === false)
) {
if (firstResolve) firstResolve()
return
@@ -208,7 +209,7 @@ export function useQueryImpl<
started = true
loading.value = true
const client = resolveClient(currentOptions.value.clientId)
const client = resolveClient(currentOptions.value?.clientId)
query.value = client.watchQuery<TResult, TVariables>({
query: currentDocument,
@@ -221,10 +222,10 @@ export function useQueryImpl<
startQuerySubscription()
if (!isServer && (currentOptions.value.fetchPolicy !== 'no-cache' || currentOptions.value.notifyOnNetworkStatusChange)) {
if (!isServer && (currentOptions.value?.fetchPolicy !== 'no-cache' || currentOptions.value.notifyOnNetworkStatusChange)) {
const currentResult = query.value.getCurrentResult()
if (!currentResult.loading || currentOptions.value.notifyOnNetworkStatusChange) {
if (!currentResult.loading || currentOptions.value?.notifyOnNetworkStatusChange) {
onNextResult(currentResult)
}
}
@@ -268,14 +269,14 @@ export function useQueryImpl<
}
function processNextResult (queryResult: ApolloQueryResult<TResult>) {
result.value = queryResult.data && Object.keys(queryResult.data).length === 0 ? null : queryResult.data
result.value = queryResult.data && Object.keys(queryResult.data).length === 0 ? undefined : queryResult.data
loading.value = queryResult.loading
networkStatus.value = queryResult.networkStatus
resultEvent.trigger(queryResult)
}
function onError (queryError: any) {
processNextResult(query.value.getCurrentResult())
processNextResult((query.value as ObservableQuery<TResult, TVariables>).getCurrentResult())
processError(queryError)
if (firstReject) {
firstReject(queryError)
@@ -322,7 +323,7 @@ export function useQueryImpl<
if (observer) {
observer.unsubscribe()
observer = null
observer = undefined
}
}
@@ -352,9 +353,9 @@ export function useQueryImpl<
if (!currentOptions) {
debouncedRestart = baseRestart
} else {
if (currentOptions.value.throttle) {
if (currentOptions.value?.throttle) {
debouncedRestart = throttle(currentOptions.value.throttle, baseRestart)
} else if (currentOptions.value.debounce) {
} else if (currentOptions.value?.debounce) {
debouncedRestart = debounce(currentOptions.value.debounce, baseRestart)
} else {
debouncedRestart = baseRestart
@@ -378,7 +379,7 @@ export function useQueryImpl<
})
// Applying variables
let currentVariables: TVariables
let currentVariables: TVariables | undefined
let currentVariablesSerialized: string
watch(variablesRef, (value, oldValue) => {
const serialized = JSON.stringify(value)
@@ -410,7 +411,7 @@ export function useQueryImpl<
// Fefetch
function refetch (variables: TVariables = null) {
function refetch (variables: TVariables | undefined = undefined) {
if (query.value) {
if (variables) {
currentVariables = variables
@@ -465,6 +466,9 @@ export function useQueryImpl<
function addSubscribeToMore (item: SubscribeToMoreItem) {
if (!started) return
if (!query.value) {
throw new Error('Query is not defined')
}
const unsubscribe = query.value.subscribeToMore(item.options)
onStopHandlers.push(unsubscribe)
item.unsubscribeFns.push(unsubscribe)
@@ -16,9 +16,9 @@ export type UseResultReturn<T> = Readonly<Ref<Readonly<T>>>
* @param {Ref<TResult>} result A `result` returned from `useQuery` to resolve.
* @returns Readonly ref with `undefined` or the resolved `result`.
*/
export function useResult<TResult, TResultKey extends keyof TResult = keyof TResult> (
export function useResult<TResult, TResultKey extends keyof NonNullable<TResult> = keyof NonNullable<TResult>> (
result: Ref<TResult>
): UseResultReturn<undefined | ExtractSingleKey<TResult, TResultKey>>
): UseResultReturn<undefined | ExtractSingleKey<NonNullable<TResult>, TResultKey>>
/**
* Resolve a `result`, returning either the first key of the `result` if there
@@ -34,10 +34,10 @@ export function useResult<TResult, TResultKey extends keyof TResult = keyof TRes
* @param {TDefaultValue} defaultValue The default return value before `result` is resolved.
* @returns Readonly ref with the `defaultValue` or the resolved `result`.
*/
export function useResult<TResult, TDefaultValue, TResultKey extends keyof TResult = keyof TResult> (
export function useResult<TResult, TDefaultValue, TResultKey extends keyof NonNullable<TResult> = keyof NonNullable<TResult>> (
result: Ref<TResult>,
defaultValue: TDefaultValue
): UseResultReturn<TDefaultValue | ExtractSingleKey<TResult, TResultKey>>
): UseResultReturn<TDefaultValue | ExtractSingleKey<NonNullable<TResult>, TResultKey>>
/**
* Resolve a `result`, returning the `result` mapped with the `pick` function.
@@ -15,6 +15,7 @@ import {
FetchResult,
Observable,
ObservableSubscription,
TypedDocumentNode,
} from '@apollo/client/core'
import { throttle, debounce } from 'throttle-debounce'
import { ReactiveFunction } from './util/ReactiveFunction'
@@ -24,6 +25,8 @@ import { useApolloClient } from './useApolloClient'
import { useEventHook } from './util/useEventHook'
import { trackSubscription } from './util/loadingTracking'
import type { CurrentInstance } from './util/types'
export interface UseSubscriptionOptions <
// eslint-disable-next-line @typescript-eslint/no-unused-vars
TResult = any,
@@ -35,21 +38,21 @@ export interface UseSubscriptionOptions <
debounce?: number
}
type DocumentParameter = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode>
type DocumentParameter<TResult, TVariables> = DocumentNode | Ref<DocumentNode> | ReactiveFunction<DocumentNode> | TypedDocumentNode<TResult, TVariables> | Ref<TypedDocumentNode<TResult, TVariables>> | ReactiveFunction<TypedDocumentNode<TResult, TVariables>>
type VariablesParameter<TVariables> = TVariables | Ref<TVariables> | ReactiveFunction<TVariables>
type OptionsParameter<TResult, TVariables> = UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>> | ReactiveFunction<UseSubscriptionOptions<TResult, TVariables>>
export interface UseSubscriptionReturn<TResult, TVariables> {
result: Ref<TResult>
result: Ref<TResult | null | undefined>
loading: Ref<boolean>
error: Ref<Error>
error: Ref<Error | null>
start: () => void
stop: () => void
restart: () => void
document: Ref<DocumentNode>
variables: Ref<TVariables>
variables: Ref<TVariables | undefined>
options: UseSubscriptionOptions<TResult, TVariables> | Ref<UseSubscriptionOptions<TResult, TVariables>>
subscription: Ref<Observable<FetchResult<TResult, Record<string, any>, Record<string, any>>>>
subscription: Ref<Observable<FetchResult<TResult, Record<string, any>, Record<string, any>>> | null>
onResult: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
off: () => void
}
@@ -62,14 +65,14 @@ export interface UseSubscriptionReturn<TResult, TVariables> {
* Use a subscription that does not require variables or options.
* */
export function useSubscription<TResult = any> (
document: DocumentParameter
document: DocumentParameter<TResult, undefined>
): UseSubscriptionReturn<TResult, undefined>
/**
* Use a subscription that requires options but not variables.
*/
export function useSubscription<TResult = any> (
document: DocumentParameter,
document: DocumentParameter<TResult, undefined>,
variables: undefined | null,
options: OptionsParameter<TResult, null>
): UseSubscriptionReturn<TResult, null>
@@ -78,7 +81,7 @@ export function useSubscription<TResult = any> (
* Use a subscription that requires variables.
*/
export function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables: VariablesParameter<TVariables>
): UseSubscriptionReturn<TResult, TVariables>
@@ -86,14 +89,14 @@ export function useSubscription<TResult = any, TVariables extends OperationVaria
* Use a subscription that has optional variables.
*/
export function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter
document: DocumentParameter<TResult, TVariables>,
): UseSubscriptionReturn<TResult, TVariables>
/**
* Use a subscription that requires variables and options.
*/
export function useSubscription<TResult = any, TVariables extends OperationVariables = OperationVariables> (
document: DocumentParameter,
document: DocumentParameter<TResult, TVariables>,
variables: VariablesParameter<TVariables>,
options: OptionsParameter<TResult, TVariables>
): UseSubscriptionReturn<TResult, TVariables>
@@ -102,23 +105,21 @@ export function useSubscription <
TResult,
TVariables
> (
document: DocumentParameter,
variables: VariablesParameter<TVariables> = null,
options: OptionsParameter<TResult, TVariables> = null,
document: DocumentParameter<TResult, TVariables>,
variables: VariablesParameter<TVariables> | undefined = undefined,
options: OptionsParameter<TResult, TVariables> = {},
): UseSubscriptionReturn<TResult, TVariables> {
// Is on server?
const vm: any = getCurrentInstance()
const isServer = vm?.$isServer
const vm = getCurrentInstance() as CurrentInstance | null
const isServer = vm?.$isServer ?? false
if (variables == null) variables = ref()
if (!options) options = {}
const documentRef = paramToRef(document)
const variablesRef = paramToRef(variables)
const optionsRef = paramToReactive(options)
const result = ref<TResult>()
const result = ref<TResult | null | undefined>()
const resultEvent = useEventHook<FetchResult<TResult>>()
const error = ref<Error>(null)
const error = ref<Error | null>(null)
const errorEvent = useEventHook<Error>()
const loading = ref(false)
@@ -127,8 +128,8 @@ export function useSubscription <
// Apollo Client
const { resolveClient } = useApolloClient()
const subscription: Ref<Observable<FetchResult<TResult>>> = ref()
let observer: ObservableSubscription
const subscription: Ref<Observable<FetchResult<TResult>> | null> = ref(null)
let observer: ObservableSubscription | null = null
let started = false
function start () {
@@ -136,7 +137,7 @@ export function useSubscription <
started = true
loading.value = true
const client = resolveClient(currentOptions.value.clientId)
const client = resolveClient(currentOptions.value?.clientId)
subscription.value = client.subscribe<TResult, TVariables>({
query: currentDocument,
@@ -197,9 +198,9 @@ export function useSubscription <
let debouncedRestart: Function
function updateRestartFn () {
if (currentOptions.value.throttle) {
if (currentOptions.value?.throttle) {
debouncedRestart = throttle(currentOptions.value.throttle, baseRestart)
} else if (currentOptions.value.debounce) {
} else if (currentOptions.value?.debounce) {
debouncedRestart = debounce(currentOptions.value.debounce, baseRestart)
} else {
debouncedRestart = baseRestart
@@ -237,7 +238,7 @@ export function useSubscription <
})
// Applying variables
let currentVariables: TVariables
let currentVariables: TVariables | undefined
let currentVariablesSerialized: string
watch(variablesRef, (value, oldValue) => {
const serialized = JSON.stringify(value)
@@ -1,4 +1,5 @@
import { Ref, watch, onUnmounted, ref, getCurrentInstance, onBeforeUnmount } from 'vue-demi'
import type { CurrentInstance } from './types'
export interface LoadingTracking {
queries: Ref<number>
@@ -11,8 +12,12 @@ export interface AppLoadingTracking extends LoadingTracking {
}
export function getAppTracking () {
const vm: any = getCurrentInstance()
const root: any = vm.$root || vm.root
const vm = getCurrentInstance() as CurrentInstance | null
const root = vm?.$root ?? vm?.root
if (!root) {
throw new Error('Instance $root not found')
}
let appTracking: AppLoadingTracking
if (!root._apolloAppTracking) {
@@ -54,7 +59,7 @@ export function getCurrentTracking () {
appTracking.components.delete(vm)
})
} else {
tracking = appTracking.components.get(vm)
tracking = appTracking.components.get(vm) as LoadingTracking
}
return {
@@ -0,0 +1,9 @@
import type { ComponentInternalInstance } from 'vue-demi'
import type { AppLoadingTracking } from './loadingTracking'
export interface CurrentInstance extends Omit<ComponentInternalInstance, 'root' | '$root'> {
_apolloAppTracking?: AppLoadingTracking
$root?: CurrentInstance
root?: CurrentInstance
$isServer?: boolean
}
@@ -1,4 +1,5 @@
import gql from 'graphql-tag'
import { TypedDocumentNode } from '@apollo/client/core'
export type ID = string
@@ -83,3 +84,9 @@ export interface MultiKeyExampleQuery {
__typename?: 'OtherExample'
}
}
export const ExampleTypedQueryDocument: TypedDocumentNode<ExampleQuery, ExampleQueryVariables> = ExampleDocument
export const ExampleTypedMutationDocument: TypedDocumentNode<ExampleUpdateMutation, ExampleUpdateMutationVariables> = ExampleDocument
export const ExampleTypedSubscriptionDocument: TypedDocumentNode<ExampleUpdatedSubscription, ExampleUpdatedSubscriptionVariables> = ExampleDocument
@@ -4,8 +4,8 @@
"module": "commonjs",
"noEmit": true,
"esModuleInterop": true,
"skipLibCheck": true
// "strict": true // TODO: this should be enabled, but src is broken with strict
"skipLibCheck": true,
"strict": true
},
"include": [
"*.test.ts"
@@ -7,7 +7,7 @@ import { assertExactType } from './assertions'
// =============================================================================
{
const noClientId = useApolloClient()
noClientId.client.extract(true).storeType.is.any
noClientId.client?.extract(true).storeType.is.any
}
// =============================================================================
@@ -16,7 +16,7 @@ import { assertExactType } from './assertions'
// =============================================================================
{
const withClientId = useApolloClient('88K2tP')
withClientId.client.extract(true).storeType.is.any
withClientId.client?.extract(true).storeType.is.any
}
// =============================================================================
@@ -25,8 +25,8 @@ import { assertExactType } from './assertions'
// =============================================================================
{
const withType = useApolloClient<'cacheShape'>('38pX2d')
const store = withType.client.extract(true)
const store = withType.client?.extract(true)
assertExactType<typeof withType, UseApolloClientReturn<'cacheShape'>>(withType)
assertExactType<typeof store, 'cacheShape'>(store)
assertExactType<typeof store, 'cacheShape' | undefined>(store)
}
@@ -5,6 +5,7 @@ import {
ExampleUpdateMutation,
ExampleUpdateMutationVariables,
ExampleUpdatePayload,
ExampleTypedMutationDocument,
} from '../fixtures/graphql-example-types'
import { assertExactType } from './assertions'
@@ -85,8 +86,11 @@ import { assertExactType } from './assertions'
useMutationOnlyMutationType.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
@@ -108,8 +112,11 @@ import { assertExactType } from './assertions'
useMutationOnlyMutationTypeWithOptions.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
@@ -131,8 +138,11 @@ import { assertExactType } from './assertions'
useMutationAllTyped.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -149,8 +159,11 @@ import { assertExactType } from './assertions'
useMutationAllTyped.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -162,8 +175,11 @@ import { assertExactType } from './assertions'
useMutationAllTyped.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -207,8 +223,11 @@ import { assertExactType } from './assertions'
withVariablesInOptions.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -230,8 +249,11 @@ import { assertExactType } from './assertions'
withNoOptions.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -273,8 +295,35 @@ import { assertExactType } from './assertions'
withNoVariablesInOptions.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<typeof param.data.exampleUpdate, ExampleUpdatePayload>(
param.data.exampleUpdate,
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
// =============================================================================
// With a TypedQueryDocument:
// - TResult should be the mutation type
// - TVariables should be the variables type
// =============================================================================
{
const useMutationAllTyped = useMutation(
ExampleTypedMutationDocument,
{ variables: { id: '1', example: { name: 'new' } } },
)
useMutationAllTyped.mutate({ id: '2', example: { name: 'remix' } }, {})
useMutationAllTyped.onDone(param => {
assertExactType<typeof param, FetchResult<ExampleUpdateMutation> | undefined>(param)
assertExactType<
NonNullable<NonNullable<typeof param>['data']>['exampleUpdate'],
ExampleUpdatePayload | null | undefined
>(
param?.data?.exampleUpdate,
)
})
}
@@ -4,6 +4,7 @@ import {
ExampleDocument,
ExampleQuery,
ExampleQueryVariables,
ExampleTypedQueryDocument,
} from '../fixtures/graphql-example-types'
import { assertExactType } from './assertions'
@@ -31,7 +32,7 @@ import { assertExactType } from './assertions'
const useQueryOnlyQueryType = useQuery<ExampleQuery>(ExampleDocument)
const useQueryOnlyQueryTypeResult = useQueryOnlyQueryType.result.value
assertExactType<typeof useQueryOnlyQueryTypeResult, ExampleQuery>(useQueryOnlyQueryTypeResult)
assertExactType<typeof useQueryOnlyQueryTypeResult, ExampleQuery | null | undefined>(useQueryOnlyQueryTypeResult)
const useQueryOnlyQueryTypeVariables = useQueryOnlyQueryType.variables.value
assertExactType<typeof useQueryOnlyQueryTypeVariables, undefined>(useQueryOnlyQueryTypeVariables)
@@ -46,10 +47,10 @@ import { assertExactType } from './assertions'
const useQueryWithVars = useQuery<ExampleQuery>(ExampleDocument, { id: 'asdf' })
const useQueryWithVarsResult = useQueryWithVars.result.value
assertExactType<typeof useQueryWithVarsResult, ExampleQuery>(useQueryWithVarsResult)
assertExactType<typeof useQueryWithVarsResult, ExampleQuery | null | undefined>(useQueryWithVarsResult)
const useQueryWithVarsVariables = useQueryWithVars.variables.value
assertExactType<typeof useQueryWithVarsVariables, OperationVariables>(useQueryWithVarsVariables)
assertExactType<typeof useQueryWithVarsVariables, OperationVariables | undefined>(useQueryWithVarsVariables)
}
// =============================================================================
@@ -63,10 +64,10 @@ import { assertExactType } from './assertions'
})
const useQueryAllTypedResult = useQueryAllTyped.result.value
assertExactType<typeof useQueryAllTypedResult, ExampleQuery>(useQueryAllTypedResult)
assertExactType<typeof useQueryAllTypedResult, ExampleQuery | null | undefined>(useQueryAllTypedResult)
const useQueryAllTypedVariables = useQueryAllTyped.variables.value
assertExactType<typeof useQueryAllTypedVariables, ExampleQueryVariables>(
assertExactType<typeof useQueryAllTypedVariables, ExampleQueryVariables | undefined>(
useQueryAllTypedVariables,
)
}
@@ -80,10 +81,10 @@ import { assertExactType } from './assertions'
const useQueryAllTyped = useQuery<ExampleQuery, ExampleQueryVariables>(ExampleDocument)
const useQueryAllTypedResult = useQueryAllTyped.result.value
assertExactType<typeof useQueryAllTypedResult, ExampleQuery>(useQueryAllTypedResult)
assertExactType<typeof useQueryAllTypedResult, ExampleQuery | null | undefined>(useQueryAllTypedResult)
const useQueryAllTypedVariables = useQueryAllTyped.variables.value
assertExactType<typeof useQueryAllTypedVariables, ExampleQueryVariables>(
assertExactType<typeof useQueryAllTypedVariables, ExampleQueryVariables | undefined>(
useQueryAllTypedVariables,
)
}
@@ -104,13 +105,13 @@ import { assertExactType } from './assertions'
const useQueryOnlyQueryTypeNoVarsWithOptionsResult =
useQueryOnlyQueryTypeNoVarsWithOptions.result.value
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsResult, ExampleQuery>(
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsResult, ExampleQuery | null | undefined>(
useQueryOnlyQueryTypeNoVarsWithOptionsResult,
)
const useQueryOnlyQueryTypeNoVarsWithOptionsVariables =
useQueryOnlyQueryTypeNoVarsWithOptions.variables.value
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsVariables, null>(
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsVariables, null | undefined>(
useQueryOnlyQueryTypeNoVarsWithOptionsVariables,
)
}
@@ -125,13 +126,13 @@ import { assertExactType } from './assertions'
const useQueryOnlyQueryTypeNoVarsWithOptionsResult =
useQueryOnlyQueryTypeNoVarsWithOptions.result.value
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsResult, ExampleQuery>(
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsResult, ExampleQuery | undefined>(
useQueryOnlyQueryTypeNoVarsWithOptionsResult,
)
const useQueryOnlyQueryTypeNoVarsWithOptionsVariables =
useQueryOnlyQueryTypeNoVarsWithOptions.variables.value
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsVariables, null>(
assertExactType<typeof useQueryOnlyQueryTypeNoVarsWithOptionsVariables, null | undefined>(
useQueryOnlyQueryTypeNoVarsWithOptionsVariables,
)
}
@@ -161,14 +162,33 @@ import { assertExactType } from './assertions'
)
const useQueryWithOptionsResult = useQueryWithOptions.result.value
assertExactType<typeof useQueryWithOptionsResult, ExampleQuery>(useQueryWithOptionsResult)
assertExactType<typeof useQueryWithOptionsResult, ExampleQuery | null | undefined>(useQueryWithOptionsResult)
const useQueryWithOptionsVariables = useQueryWithOptions.variables.value
assertExactType<typeof useQueryWithOptionsVariables, ExampleQueryVariables>(
assertExactType<typeof useQueryWithOptionsVariables, ExampleQueryVariables | undefined>(
useQueryWithOptionsVariables,
)
}
// =============================================================================
// With a TypedQueryDocument:
// - TResult should be the query type
// - TVariables should be OperationVariables
// =============================================================================
{
const useQueryAllTyped = useQuery(ExampleTypedQueryDocument, {
id: 'k3x47b',
})
const useQueryAllTypedResult = useQueryAllTyped.result.value
assertExactType<typeof useQueryAllTypedResult, ExampleQuery | null | undefined>(useQueryAllTypedResult)
const useQueryAllTypedVariables = useQueryAllTyped.variables.value
assertExactType<typeof useQueryAllTypedVariables, ExampleQueryVariables | undefined>(
useQueryAllTypedVariables,
)
}
// ====== Expected failures, uncomment to test ======
// // @ts-expect-error - should require variables to be OperationType
@@ -94,8 +94,8 @@ const { result: multiKeyResult } = multiKeyQuery
assertExactType<typeof result, 'secret'>(result)
useResult_WithDefaultValue_MultiKey.value
} else {
useResult_WithDefaultValue_MultiKey.value.example?.__typename
useResult_WithDefaultValue_MultiKey.value.otherExample?.__typename
useResult_WithDefaultValue_MultiKey.value?.example?.__typename
useResult_WithDefaultValue_MultiKey.value?.otherExample?.__typename
}
}
@@ -107,7 +107,7 @@ const { result: multiKeyResult } = multiKeyQuery
const useResult_WithPickFunction = useResult(
multiKeyResult,
[] as const,
data => data.otherExample?.__typename,
data => data?.otherExample?.__typename,
)
assertExactType<
@@ -4,6 +4,7 @@ import {
ExampleDocument,
ExampleUpdatedSubscription,
ExampleUpdatedSubscriptionVariables,
ExampleTypedSubscriptionDocument,
} from '../fixtures/graphql-example-types'
import { assertExactType } from './assertions'
@@ -42,7 +43,7 @@ import { assertExactType } from './assertions'
// Result type should match the passed in subscription type
const useSubscription_OnlySubscriptionTypeResult =
useSubscription_OnlySubscriptionType.result.value
assertExactType<typeof useSubscription_OnlySubscriptionTypeResult, ExampleUpdatedSubscription>(
assertExactType<typeof useSubscription_OnlySubscriptionTypeResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_OnlySubscriptionTypeResult,
)
@@ -69,13 +70,13 @@ import { assertExactType } from './assertions'
// Result type should match the passed in subscription type
const useSubscription_WithVarsResult = useSubscription_WithVars.result.value
assertExactType<typeof useSubscription_WithVarsResult, ExampleUpdatedSubscription>(
assertExactType<typeof useSubscription_WithVarsResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_WithVarsResult,
)
// Variables type should match the passed in variables type
const useSubscription_WithVarsVariables = useSubscription_WithVars.variables.value
assertExactType<typeof useSubscription_WithVarsVariables, OperationVariables>(
assertExactType<typeof useSubscription_WithVarsVariables, OperationVariables | undefined>(
useSubscription_WithVarsVariables,
)
@@ -96,13 +97,13 @@ import { assertExactType } from './assertions'
// Result type should match the passed in subscription type
const useSubscription_AllTypedResult = useSubscription_AllTyped.result.value
assertExactType<typeof useSubscription_AllTypedResult, ExampleUpdatedSubscription>(
assertExactType<typeof useSubscription_AllTypedResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_AllTypedResult,
)
// Variables type should match the passed in variables type
const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value
assertExactType<typeof useSubscription_AllTypedVariables, ExampleUpdatedSubscriptionVariables>(
assertExactType<typeof useSubscription_AllTypedVariables, ExampleUpdatedSubscriptionVariables | undefined>(
useSubscription_AllTypedVariables,
)
@@ -123,13 +124,13 @@ import { assertExactType } from './assertions'
// Result type should match the passed in subscription type
const useSubscription_AllTypedResult = useSubscription_AllTyped.result.value
assertExactType<typeof useSubscription_AllTypedResult, ExampleUpdatedSubscription>(
assertExactType<typeof useSubscription_AllTypedResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_AllTypedResult,
)
// Variables type should match the passed in variables type
const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value
assertExactType<typeof useSubscription_AllTypedVariables, ExampleUpdatedSubscriptionVariables>(
assertExactType<typeof useSubscription_AllTypedVariables, ExampleUpdatedSubscriptionVariables | undefined>(
useSubscription_AllTypedVariables,
)
@@ -154,7 +155,7 @@ import { assertExactType } from './assertions'
useSubscription_OnlySubscriptionType_NoVarsWithOptions.result.value
assertExactType<
typeof useSubscription_OnlySubscriptionType_NoVarsWithOptionsResult,
ExampleUpdatedSubscription
ExampleUpdatedSubscription | null | undefined
>(useSubscription_OnlySubscriptionType_NoVarsWithOptionsResult)
// Variables type should be `undefined`
@@ -162,7 +163,7 @@ import { assertExactType } from './assertions'
useSubscription_OnlySubscriptionType_NoVarsWithOptions.variables.value
assertExactType<
typeof useSubscription_OnlySubscriptionType_NoVarsWithOptionsVariables,
null
null | undefined
>(useSubscription_OnlySubscriptionType_NoVarsWithOptionsVariables)
// Result data type should be the passed in result
@@ -193,12 +194,12 @@ import { assertExactType } from './assertions'
)
const useSubscription_WithOptionsResult = useSubscription_WithOptions.result.value
assertExactType<typeof useSubscription_WithOptionsResult, ExampleUpdatedSubscription>(
assertExactType<typeof useSubscription_WithOptionsResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_WithOptionsResult,
)
const useSubscription_WithOptionsVariables = useSubscription_WithOptions.variables.value
assertExactType<typeof useSubscription_WithOptionsVariables, ExampleUpdatedSubscriptionVariables>(
assertExactType<typeof useSubscription_WithOptionsVariables, ExampleUpdatedSubscriptionVariables | undefined>(
useSubscription_WithOptionsVariables,
)
@@ -206,6 +207,30 @@ import { assertExactType } from './assertions'
useSubscription_WithOptions.onResult(result => result?.data?.exampleUpdated.name)
}
// =============================================================================
// With a TypedQueryDocument:
// - TResult should be the subscription type
// - TVariables should be the variables type
// =============================================================================
{
const useSubscription_AllTyped = useSubscription(ExampleTypedSubscriptionDocument, { id: 'k3x47b' })
// Result type should match the passed in subscription type
const useSubscription_AllTypedResult = useSubscription_AllTyped.result.value
assertExactType<typeof useSubscription_AllTypedResult, ExampleUpdatedSubscription | null | undefined>(
useSubscription_AllTypedResult,
)
// Variables type should match the passed in variables type
const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value
assertExactType<typeof useSubscription_AllTypedVariables, ExampleUpdatedSubscriptionVariables | undefined>(
useSubscription_AllTypedVariables,
)
// Result data type should be the passed in result
useSubscription_AllTyped.onResult(result => result?.data?.exampleUpdated.name)
}
// // ====== Expected failures, uncomment to test ======
// // @ts-expect-error - should require variables to be OperationType
+6 -5
View File
@@ -3,8 +3,8 @@
"target": "es5",
"module": "amd",
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"outDir": "dist",
"moduleResolution": "node",
"lib": [
@@ -12,9 +12,10 @@
"ES2015",
"ES2020.Symbol.WellKnown"
],
"declaration": true
"declaration": true,
"strict": true
},
"include": [
"src/**/*",
],
}
"src/**/*"
]
}
+1 -1
View File
@@ -9,8 +9,8 @@ import {
SubscriptionOptions,
OperationVariables,
FetchResult,
Observable
} from '@apollo/client/core'
import { Observable } from '@apollo/client/utilities/observables/Observable'
import { ApolloProvider } from './apollo-provider'
import {
VueApolloQueryDefinition,
+1
View File
@@ -35,6 +35,7 @@
"serialize-javascript": "^5.0.1"
},
"devDependencies": {
"@types/serialize-javascript": "^4.0.0",
"typescript": "^4"
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ export function getStates (apolloClients: ApolloClients, options: GetStatesOptio
const finalOptions = Object.assign({}, {
exportNamespace: '',
}, options)
const states = {}
const states: Record<string, any> = {}
for (const key in apolloClients) {
const client = apolloClients[key]
const state = client.cache.extract()
+5 -4
View File
@@ -3,9 +3,10 @@
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"skipLibCheck": true
"skipLibCheck": true,
"strict": true
},
"include": [
"src/**/*",
],
}
"src/**/*"
]
}
+23 -13
View File
@@ -1,9 +1,13 @@
import { print } from 'graphql/language/printer'
import { ApolloError } from '@apollo/client/core'
import { ErrorResponse } from '@apollo/client/link/error'
export function getErrorMessages (error) {
export function getErrorMessages (error: ErrorResponse | ApolloError) {
const messages: string[] = []
const { graphQLErrors, networkError, operation, stack } = error
let printedQuery
const { graphQLErrors, networkError } = error
const operation = 'operation' in error ? error.operation : undefined;
const stack = 'stack' in error ? error.stack : undefined;
let printedQuery: string;
if (operation) {
printedQuery = print(operation.query)
@@ -20,7 +24,7 @@ export function getErrorMessages (error) {
}
})
}
if (networkError) messages.push(`[Network error] ${networkError}`)
if (stack) messages.push(stack)
@@ -28,7 +32,7 @@ export function getErrorMessages (error) {
return messages
}
export function logErrorMessages (error, printStack = true) {
export function logErrorMessages (error: ApolloError | ErrorResponse, printStack = true) {
getErrorMessages(error).map(message => {
const result = /\[([\w ]*)](.*)/.exec(message)
if (result) {
@@ -41,6 +45,8 @@ export function logErrorMessages (error, printStack = true) {
if (printStack) {
let stack = new Error().stack
if (stack == null) return
const newLineIndex = stack.indexOf('\n')
stack = stack.substr(stack.indexOf('\n', newLineIndex + 1))
console.log(`%c${stack}`, 'color:grey;')
@@ -52,21 +58,25 @@ interface ErrorLocation {
column: number
}
function logOperation (printedQuery: string, locations: ErrorLocation[]) {
function logOperation (printedQuery: string, locations: readonly ErrorLocation[] | undefined) {
const lines = printedQuery.split('\n')
const l = lines.length
const result = lines.slice()
const lineMap = {}
const lineMap: Record<number, number> = {}
for (let i = 0; i < l; i++) {
lineMap[i] = i
}
for (const { line, column } of locations) {
const index = lineMap[line]
result.splice(index, 0, '▲'.padStart(column, ' '))
// Offset remaining lines
for (let i = index + 1; i < l; i++) {
lineMap[i]++
if (locations) {
for (const { line, column } of locations) {
const index = lineMap[line]
result.splice(index, 0, '▲'.padStart(column, ' '))
// Offset remaining lines
for (let i = index + 1; i < l; i++) {
lineMap[i]++
}
}
}
return result.join('\n')
}
+5 -3
View File
@@ -3,8 +3,10 @@
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"esModuleInterop": true,
"strict": true
},
"include": [
"src/**/*",
],
}
"src/**/*"
]
}