diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 456d226..a8894ce 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -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. diff --git a/packages/docs/src/guide-advanced/ssr.md b/packages/docs/src/guide-advanced/ssr.md index 5369a60..cb8367b 100644 --- a/packages/docs/src/guide-advanced/ssr.md +++ b/packages/docs/src/guide-advanced/ssr.md @@ -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 diff --git a/packages/docs/src/guide-composable/error-handling.md b/packages/docs/src/guide-composable/error-handling.md index 7ef4e90..5d86151 100644 --- a/packages/docs/src/guide-composable/error-handling.md +++ b/packages/docs/src/guide-composable/error-handling.md @@ -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 } }) -``` \ No newline at end of file +``` diff --git a/packages/docs/src/guide-composable/fragments.md b/packages/docs/src/guide-composable/fragments.md index c4b122e..7327980 100644 --- a/packages/docs/src/guide-composable/fragments.md +++ b/packages/docs/src/guide-composable/fragments.md @@ -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, }) ``` diff --git a/packages/docs/src/guide-composable/subscription.md b/packages/docs/src/guide-composable/subscription.md index aa796fb..c3d56c6 100644 --- a/packages/docs/src/guide-composable/subscription.md +++ b/packages/docs/src/guide-composable/subscription.md @@ -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/`, diff --git a/packages/docs/src/guide-option/subscriptions.md b/packages/docs/src/guide-option/subscriptions.md index 045fb86..e2111e9 100644 --- a/packages/docs/src/guide-option/subscriptions.md +++ b/packages/docs/src/guide-option/subscriptions.md @@ -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' diff --git a/packages/docs/src/guide/installation.md b/packages/docs/src/guide/installation.md index e1c0385..4e5565c 100644 --- a/packages/docs/src/guide/installation.md +++ b/packages/docs/src/guide/installation.md @@ -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({ diff --git a/packages/docs/src/migration/README.md b/packages/docs/src/migration/README.md index 2bb8d2e..3245e03 100644 --- a/packages/docs/src/migration/README.md +++ b/packages/docs/src/migration/README.md @@ -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). \ No newline at end of file +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). diff --git a/packages/docs/src/zh-cn/guide/apollo/subscriptions.md b/packages/docs/src/zh-cn/guide/apollo/subscriptions.md index 0a006a7..1b79f00 100644 --- a/packages/docs/src/zh-cn/guide/apollo/subscriptions.md +++ b/packages/docs/src/zh-cn/guide/apollo/subscriptions.md @@ -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' diff --git a/packages/docs/src/zh-cn/guide/installation.md b/packages/docs/src/zh-cn/guide/installation.md index 6693250..69e6e4f 100644 --- a/packages/docs/src/zh-cn/guide/installation.md +++ b/packages/docs/src/zh-cn/guide/installation.md @@ -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({ diff --git a/packages/docs/src/zh-cn/guide/ssr.md b/packages/docs/src/zh-cn/guide/ssr.md index d3f7b8e..dac0334 100644 --- a/packages/docs/src/zh-cn/guide/ssr.md +++ b/packages/docs/src/zh-cn/guide/ssr.md @@ -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 插件 diff --git a/packages/docs/src/zh-cn/migration/README.md b/packages/docs/src/zh-cn/migration/README.md index f2c1791..ab99069 100644 --- a/packages/docs/src/zh-cn/migration/README.md +++ b/packages/docs/src/zh-cn/migration/README.md @@ -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)。 \ No newline at end of file +之后: + +```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)。 diff --git a/packages/test-e2e-composition/src/vue-apollo.js b/packages/test-e2e-composition/src/vue-apollo.js index c664ac0..0cbb10c 100644 --- a/packages/test-e2e-composition/src/vue-apollo.js +++ b/packages/test-e2e-composition/src/vue-apollo.js @@ -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' diff --git a/packages/vue-apollo-composable/package.json b/packages/vue-apollo-composable/package.json index fac1642..88edf9d 100644 --- a/packages/vue-apollo-composable/package.json +++ b/packages/vue-apollo-composable/package.json @@ -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", diff --git a/packages/vue-apollo-composable/src/useApolloClient.ts b/packages/vue-apollo-composable/src/useApolloClient.ts index d7dacc5..ed1a1cd 100644 --- a/packages/vue-apollo-composable/src/useApolloClient.ts +++ b/packages/vue-apollo-composable/src/useApolloClient.ts @@ -15,14 +15,14 @@ export interface UseApolloClientReturn { readonly client: ApolloClient } -function resolveDefaultClient (providedApolloClients: ClientDict, providedApolloClient: ApolloClient): NullableApolloClient { +function resolveDefaultClient (providedApolloClients: ClientDict | null, providedApolloClient: ApolloClient | null): NullableApolloClient { const resolvedClient = providedApolloClients ? providedApolloClients.default - : providedApolloClient + : (providedApolloClient ?? undefined) return resolvedClient } -function resolveClientWithId (providedApolloClients: ClientDict, clientId: ClientId): NullableApolloClient { +function resolveClientWithId (providedApolloClients: ClientDict | null, clientId: ClientId): NullableApolloClient { if (!providedApolloClients) { throw new Error(`No apolloClients injection found, tried to resolve '${clientId}' clientId`) } @@ -35,10 +35,10 @@ export function useApolloClient (clientId?: ClientId): UseApo if (!getCurrentInstance()) { resolveImpl = () => currentApolloClient } else { - const providedApolloClients: ClientDict = inject(ApolloClients, null) - const providedApolloClient: ApolloClient = inject(DefaultApolloClient, null) + const providedApolloClients: ClientDict | null = inject(ApolloClients, null) + const providedApolloClient: ApolloClient | null = inject(DefaultApolloClient, null) - resolveImpl = (id: ClientId) => { + resolveImpl = (id?: ClientId) => { if (currentApolloClient) { return currentApolloClient } else if (id) { @@ -48,10 +48,10 @@ export function useApolloClient (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 (client: ApolloClient (fn: () => TFnResult) { const result = fn() - currentApolloClient = null + currentApolloClient = undefined return result } } diff --git a/packages/vue-apollo-composable/src/useLazyQuery.ts b/packages/vue-apollo-composable/src/useLazyQuery.ts index b79cfd5..f2cc0ad 100644 --- a/packages/vue-apollo-composable/src/useLazyQuery.ts +++ b/packages/vue-apollo-composable/src/useLazyQuery.ts @@ -6,7 +6,7 @@ export function useLazyQuery< TResult = any, TVariables = any, > ( - document: DocumentParameter, + document: DocumentParameter, variables?: VariablesParameter, options?: OptionsParameter, ) { diff --git a/packages/vue-apollo-composable/src/useMutation.ts b/packages/vue-apollo-composable/src/useMutation.ts index ef87e48..7f0139a 100644 --- a/packages/vue-apollo-composable/src/useMutation.ts +++ b/packages/vue-apollo-composable/src/useMutation.ts @@ -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 | ReactiveFunction +type DocumentParameter = DocumentNode | Ref | ReactiveFunction | TypedDocumentNode | Ref> | ReactiveFunction> type OptionsParameter = UseMutationOptions | Ref> | ReactiveFunction> export type MutateOverrideOptions = Pick, 'update' | 'optimisticResponse' | 'context' | 'updateQueries' | 'refetchQueries' | 'awaitRefetchQueries' | 'errorPolicy' | 'fetchPolicy' | 'clientId'> export type MutateResult = Promise, Record>> -export type MutateFunction = (variables?: TVariables, overrideOptions?: MutateOverrideOptions) => MutateResult +export type MutateFunction = (variables?: TVariables | null, overrideOptions?: MutateOverrideOptions) => MutateResult export interface UseMutationReturn { mutate: MutateFunction loading: Ref - error: Ref + error: Ref called: Ref onDone: (fn: (param: FetchResult, Record>) => void) => { off: () => void @@ -40,14 +40,12 @@ export function useMutation< TResult = any, TVariables extends OperationVariables = OperationVariables > ( - document: DocumentParameter, - options?: OptionsParameter, + document: DocumentParameter, + options: OptionsParameter = {}, ): UseMutationReturn { - if (!options) options = {} - const loading = ref(false) trackMutation(loading) - const error = ref(null) + const error = ref(null) const called = ref(false) const doneEvent = useEventHook, Record>>() @@ -56,7 +54,7 @@ export function useMutation< // Apollo Client const { resolveClient } = useApolloClient() - async function mutate (variables?: TVariables, overrideOptions: Omit, 'variables'> = {}) { + async function mutate (variables?: TVariables | null, overrideOptions: Omit, '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({ + const result = await client.mutate({ 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) diff --git a/packages/vue-apollo-composable/src/useQuery.ts b/packages/vue-apollo-composable/src/useQuery.ts index 7e933b2..3ace2b8 100644 --- a/packages/vue-apollo-composable/src/useQuery.ts +++ b/packages/vue-apollo-composable/src/useQuery.ts @@ -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 | ReactiveFunction +export type DocumentParameter = DocumentNode | Ref | ReactiveFunction | TypedDocumentNode | Ref> | ReactiveFunction> export type VariablesParameter = TVariables | Ref | ReactiveFunction export type OptionsParameter = UseQueryOptions | Ref> | ReactiveFunction> // Return export interface UseQueryReturn { - result: Ref + result: Ref loading: Ref - networkStatus: Ref - error: Ref + networkStatus: Ref + error: Ref start: () => void stop: () => void restart: () => void forceDisabled: Ref document: Ref - variables: Ref + variables: Ref options: UseQueryOptions | Ref> - query: Ref> - refetch: (variables?: TVariables) => Promise> - fetchMore: (options: FetchMoreQueryOptions & FetchMoreOptions) => Promise> + query: Ref | null | undefined> + refetch: (variables?: TVariables) => Promise> | undefined + fetchMore: (options: FetchMoreQueryOptions & FetchMoreOptions) => Promise> | undefined subscribeToMore: (options: SubscribeToMoreOptions | Ref> | ReactiveFunction>) => void onResult: (fn: (param: ApolloQueryResult) => void) => { off: () => void @@ -80,21 +83,21 @@ export interface UseQueryReturn { * Use a query that does not require variables or options. * */ export function useQuery ( - document: DocumentParameter + document: DocumentParameter ): UseQueryReturn /** * Use a query that has optional variables but not options */ export function useQuery ( - document: DocumentParameter + document: DocumentParameter ): UseQueryReturn /** * Use a query that has required variables but not options */ export function useQuery ( - document: DocumentParameter, + document: DocumentParameter, variables: VariablesParameter ): UseQueryReturn @@ -102,7 +105,7 @@ export function useQuery ( - document: DocumentParameter, + document: DocumentParameter, variables: undefined | null, options: OptionsParameter, ): UseQueryReturn @@ -111,7 +114,7 @@ export function useQuery ( * Use a query that requires variables and options. */ export function useQuery ( - document: DocumentParameter, + document: DocumentParameter, variables: VariablesParameter, options: OptionsParameter, ): UseQueryReturn @@ -120,7 +123,7 @@ export function useQuery< TResult, TVariables extends OperationVariables > ( - document: DocumentParameter, + document: DocumentParameter, variables?: VariablesParameter, options?: OptionsParameter, ): UseQueryReturn { @@ -131,17 +134,15 @@ export function useQueryImpl< TResult, TVariables extends OperationVariables > ( - document: DocumentParameter, + document: DocumentParameter, variables?: VariablesParameter, - options?: OptionsParameter, + options: OptionsParameter = {}, lazy: boolean = false, ): UseQueryReturn { // 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() + const result = ref() const resultEvent = useEventHook>() - const error = ref(null) + const error = ref(null) const errorEvent = useEventHook() // 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> = ref() - let observer: ObservableSubscription + const query: Ref | 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({ 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) { - 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).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) diff --git a/packages/vue-apollo-composable/src/useResult.ts b/packages/vue-apollo-composable/src/useResult.ts index 5641c3c..1b51b4e 100644 --- a/packages/vue-apollo-composable/src/useResult.ts +++ b/packages/vue-apollo-composable/src/useResult.ts @@ -16,9 +16,9 @@ export type UseResultReturn = Readonly>> * @param {Ref} result A `result` returned from `useQuery` to resolve. * @returns Readonly ref with `undefined` or the resolved `result`. */ -export function useResult ( +export function useResult = keyof NonNullable> ( result: Ref -): UseResultReturn> +): UseResultReturn, TResultKey>> /** * Resolve a `result`, returning either the first key of the `result` if there @@ -34,10 +34,10 @@ export function useResult ( +export function useResult = keyof NonNullable> ( result: Ref, defaultValue: TDefaultValue -): UseResultReturn> +): UseResultReturn, TResultKey>> /** * Resolve a `result`, returning the `result` mapped with the `pick` function. diff --git a/packages/vue-apollo-composable/src/useSubscription.ts b/packages/vue-apollo-composable/src/useSubscription.ts index 843d550..4cdbc79 100644 --- a/packages/vue-apollo-composable/src/useSubscription.ts +++ b/packages/vue-apollo-composable/src/useSubscription.ts @@ -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 | ReactiveFunction +type DocumentParameter = DocumentNode | Ref | ReactiveFunction | TypedDocumentNode | Ref> | ReactiveFunction> type VariablesParameter = TVariables | Ref | ReactiveFunction type OptionsParameter = UseSubscriptionOptions | Ref> | ReactiveFunction> export interface UseSubscriptionReturn { - result: Ref + result: Ref loading: Ref - error: Ref + error: Ref start: () => void stop: () => void restart: () => void document: Ref - variables: Ref + variables: Ref options: UseSubscriptionOptions | Ref> - subscription: Ref, Record>>> + subscription: Ref, Record>> | null> onResult: (fn: (param: FetchResult, Record>) => void) => { off: () => void } @@ -62,14 +65,14 @@ export interface UseSubscriptionReturn { * Use a subscription that does not require variables or options. * */ export function useSubscription ( - document: DocumentParameter + document: DocumentParameter ): UseSubscriptionReturn /** * Use a subscription that requires options but not variables. */ export function useSubscription ( - document: DocumentParameter, + document: DocumentParameter, variables: undefined | null, options: OptionsParameter ): UseSubscriptionReturn @@ -78,7 +81,7 @@ export function useSubscription ( * Use a subscription that requires variables. */ export function useSubscription ( - document: DocumentParameter, + document: DocumentParameter, variables: VariablesParameter ): UseSubscriptionReturn @@ -86,14 +89,14 @@ export function useSubscription ( - document: DocumentParameter + document: DocumentParameter, ): UseSubscriptionReturn /** * Use a subscription that requires variables and options. */ export function useSubscription ( - document: DocumentParameter, + document: DocumentParameter, variables: VariablesParameter, options: OptionsParameter ): UseSubscriptionReturn @@ -102,23 +105,21 @@ export function useSubscription < TResult, TVariables > ( - document: DocumentParameter, - variables: VariablesParameter = null, - options: OptionsParameter = null, + document: DocumentParameter, + variables: VariablesParameter | undefined = undefined, + options: OptionsParameter = {}, ): UseSubscriptionReturn { // 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() + const result = ref() const resultEvent = useEventHook>() - const error = ref(null) + const error = ref(null) const errorEvent = useEventHook() const loading = ref(false) @@ -127,8 +128,8 @@ export function useSubscription < // Apollo Client const { resolveClient } = useApolloClient() - const subscription: Ref>> = ref() - let observer: ObservableSubscription + const subscription: Ref> | 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({ 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) diff --git a/packages/vue-apollo-composable/src/util/loadingTracking.ts b/packages/vue-apollo-composable/src/util/loadingTracking.ts index 336359a..931b69c 100644 --- a/packages/vue-apollo-composable/src/util/loadingTracking.ts +++ b/packages/vue-apollo-composable/src/util/loadingTracking.ts @@ -1,4 +1,5 @@ import { Ref, watch, onUnmounted, ref, getCurrentInstance, onBeforeUnmount } from 'vue-demi' +import type { CurrentInstance } from './types' export interface LoadingTracking { queries: Ref @@ -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 { diff --git a/packages/vue-apollo-composable/src/util/types.ts b/packages/vue-apollo-composable/src/util/types.ts new file mode 100644 index 0000000..fff847c --- /dev/null +++ b/packages/vue-apollo-composable/src/util/types.ts @@ -0,0 +1,9 @@ +import type { ComponentInternalInstance } from 'vue-demi' +import type { AppLoadingTracking } from './loadingTracking' + +export interface CurrentInstance extends Omit { + _apolloAppTracking?: AppLoadingTracking + $root?: CurrentInstance + root?: CurrentInstance + $isServer?: boolean +} diff --git a/packages/vue-apollo-composable/tests/fixtures/graphql-example-types.ts b/packages/vue-apollo-composable/tests/fixtures/graphql-example-types.ts index b9d3fd8..e20b7b6 100644 --- a/packages/vue-apollo-composable/tests/fixtures/graphql-example-types.ts +++ b/packages/vue-apollo-composable/tests/fixtures/graphql-example-types.ts @@ -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 = ExampleDocument + +export const ExampleTypedMutationDocument: TypedDocumentNode = ExampleDocument + +export const ExampleTypedSubscriptionDocument: TypedDocumentNode = ExampleDocument diff --git a/packages/vue-apollo-composable/tests/types/tsconfig.json b/packages/vue-apollo-composable/tests/types/tsconfig.json index 63bdf70..7262ba8 100644 --- a/packages/vue-apollo-composable/tests/types/tsconfig.json +++ b/packages/vue-apollo-composable/tests/types/tsconfig.json @@ -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" diff --git a/packages/vue-apollo-composable/tests/types/useApolloClient-types.test.ts b/packages/vue-apollo-composable/tests/types/useApolloClient-types.test.ts index 97d1ad2..eb2de76 100644 --- a/packages/vue-apollo-composable/tests/types/useApolloClient-types.test.ts +++ b/packages/vue-apollo-composable/tests/types/useApolloClient-types.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>(withType) - assertExactType(store) + assertExactType(store) } diff --git a/packages/vue-apollo-composable/tests/types/useMutation-types.test.ts b/packages/vue-apollo-composable/tests/types/useMutation-types.test.ts index 9c80b2e..185d25d 100644 --- a/packages/vue-apollo-composable/tests/types/useMutation-types.test.ts +++ b/packages/vue-apollo-composable/tests/types/useMutation-types.test.ts @@ -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 | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) @@ -108,8 +112,11 @@ import { assertExactType } from './assertions' useMutationOnlyMutationTypeWithOptions.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) @@ -131,8 +138,11 @@ import { assertExactType } from './assertions' useMutationAllTyped.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } @@ -149,8 +159,11 @@ import { assertExactType } from './assertions' useMutationAllTyped.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } @@ -162,8 +175,11 @@ import { assertExactType } from './assertions' useMutationAllTyped.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } @@ -207,8 +223,11 @@ import { assertExactType } from './assertions' withVariablesInOptions.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } @@ -230,8 +249,11 @@ import { assertExactType } from './assertions' withNoOptions.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } @@ -273,8 +295,35 @@ import { assertExactType } from './assertions' withNoVariablesInOptions.onDone(param => { assertExactType | undefined>(param) - assertExactType( - param.data.exampleUpdate, + assertExactType< + NonNullable['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 | undefined>(param) + assertExactType< + NonNullable['data']>['exampleUpdate'], + ExampleUpdatePayload | null | undefined + >( + param?.data?.exampleUpdate, ) }) } diff --git a/packages/vue-apollo-composable/tests/types/useQuery-types.test.ts b/packages/vue-apollo-composable/tests/types/useQuery-types.test.ts index d7cbcb5..9cc8707 100644 --- a/packages/vue-apollo-composable/tests/types/useQuery-types.test.ts +++ b/packages/vue-apollo-composable/tests/types/useQuery-types.test.ts @@ -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(ExampleDocument) const useQueryOnlyQueryTypeResult = useQueryOnlyQueryType.result.value - assertExactType(useQueryOnlyQueryTypeResult) + assertExactType(useQueryOnlyQueryTypeResult) const useQueryOnlyQueryTypeVariables = useQueryOnlyQueryType.variables.value assertExactType(useQueryOnlyQueryTypeVariables) @@ -46,10 +47,10 @@ import { assertExactType } from './assertions' const useQueryWithVars = useQuery(ExampleDocument, { id: 'asdf' }) const useQueryWithVarsResult = useQueryWithVars.result.value - assertExactType(useQueryWithVarsResult) + assertExactType(useQueryWithVarsResult) const useQueryWithVarsVariables = useQueryWithVars.variables.value - assertExactType(useQueryWithVarsVariables) + assertExactType(useQueryWithVarsVariables) } // ============================================================================= @@ -63,10 +64,10 @@ import { assertExactType } from './assertions' }) const useQueryAllTypedResult = useQueryAllTyped.result.value - assertExactType(useQueryAllTypedResult) + assertExactType(useQueryAllTypedResult) const useQueryAllTypedVariables = useQueryAllTyped.variables.value - assertExactType( + assertExactType( useQueryAllTypedVariables, ) } @@ -80,10 +81,10 @@ import { assertExactType } from './assertions' const useQueryAllTyped = useQuery(ExampleDocument) const useQueryAllTypedResult = useQueryAllTyped.result.value - assertExactType(useQueryAllTypedResult) + assertExactType(useQueryAllTypedResult) const useQueryAllTypedVariables = useQueryAllTyped.variables.value - assertExactType( + assertExactType( useQueryAllTypedVariables, ) } @@ -104,13 +105,13 @@ import { assertExactType } from './assertions' const useQueryOnlyQueryTypeNoVarsWithOptionsResult = useQueryOnlyQueryTypeNoVarsWithOptions.result.value - assertExactType( + assertExactType( useQueryOnlyQueryTypeNoVarsWithOptionsResult, ) const useQueryOnlyQueryTypeNoVarsWithOptionsVariables = useQueryOnlyQueryTypeNoVarsWithOptions.variables.value - assertExactType( + assertExactType( useQueryOnlyQueryTypeNoVarsWithOptionsVariables, ) } @@ -125,13 +126,13 @@ import { assertExactType } from './assertions' const useQueryOnlyQueryTypeNoVarsWithOptionsResult = useQueryOnlyQueryTypeNoVarsWithOptions.result.value - assertExactType( + assertExactType( useQueryOnlyQueryTypeNoVarsWithOptionsResult, ) const useQueryOnlyQueryTypeNoVarsWithOptionsVariables = useQueryOnlyQueryTypeNoVarsWithOptions.variables.value - assertExactType( + assertExactType( useQueryOnlyQueryTypeNoVarsWithOptionsVariables, ) } @@ -161,14 +162,33 @@ import { assertExactType } from './assertions' ) const useQueryWithOptionsResult = useQueryWithOptions.result.value - assertExactType(useQueryWithOptionsResult) + assertExactType(useQueryWithOptionsResult) const useQueryWithOptionsVariables = useQueryWithOptions.variables.value - assertExactType( + assertExactType( 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(useQueryAllTypedResult) + + const useQueryAllTypedVariables = useQueryAllTyped.variables.value + assertExactType( + useQueryAllTypedVariables, + ) +} + // ====== Expected failures, uncomment to test ====== // // @ts-expect-error - should require variables to be OperationType diff --git a/packages/vue-apollo-composable/tests/types/useResult-types.test.ts b/packages/vue-apollo-composable/tests/types/useResult-types.test.ts index 9af2073..7b4c8f1 100644 --- a/packages/vue-apollo-composable/tests/types/useResult-types.test.ts +++ b/packages/vue-apollo-composable/tests/types/useResult-types.test.ts @@ -94,8 +94,8 @@ const { result: multiKeyResult } = multiKeyQuery assertExactType(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< diff --git a/packages/vue-apollo-composable/tests/types/useSubscription-types.test.ts b/packages/vue-apollo-composable/tests/types/useSubscription-types.test.ts index 0737440..ee1e4a1 100644 --- a/packages/vue-apollo-composable/tests/types/useSubscription-types.test.ts +++ b/packages/vue-apollo-composable/tests/types/useSubscription-types.test.ts @@ -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( + assertExactType( 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( + assertExactType( useSubscription_WithVarsResult, ) // Variables type should match the passed in variables type const useSubscription_WithVarsVariables = useSubscription_WithVars.variables.value - assertExactType( + assertExactType( 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( + assertExactType( useSubscription_AllTypedResult, ) // Variables type should match the passed in variables type const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value - assertExactType( + assertExactType( 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( + assertExactType( useSubscription_AllTypedResult, ) // Variables type should match the passed in variables type const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value - assertExactType( + assertExactType( 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( + assertExactType( useSubscription_WithOptionsResult, ) const useSubscription_WithOptionsVariables = useSubscription_WithOptions.variables.value - assertExactType( + assertExactType( 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( + useSubscription_AllTypedResult, + ) + + // Variables type should match the passed in variables type + const useSubscription_AllTypedVariables = useSubscription_AllTyped.variables.value + assertExactType( + 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 diff --git a/packages/vue-apollo-composable/tsconfig.json b/packages/vue-apollo-composable/tsconfig.json index e5d6cf9..bdbf84e 100644 --- a/packages/vue-apollo-composable/tsconfig.json +++ b/packages/vue-apollo-composable/tsconfig.json @@ -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/**/*", - ], -} \ No newline at end of file + "src/**/*" + ] +} diff --git a/packages/vue-apollo-option/types/vue-apollo.d.ts b/packages/vue-apollo-option/types/vue-apollo.d.ts index 211ec6b..39e41b1 100644 --- a/packages/vue-apollo-option/types/vue-apollo.d.ts +++ b/packages/vue-apollo-option/types/vue-apollo.d.ts @@ -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, diff --git a/packages/vue-apollo-ssr/package.json b/packages/vue-apollo-ssr/package.json index 4644c16..935f77f 100644 --- a/packages/vue-apollo-ssr/package.json +++ b/packages/vue-apollo-ssr/package.json @@ -35,6 +35,7 @@ "serialize-javascript": "^5.0.1" }, "devDependencies": { + "@types/serialize-javascript": "^4.0.0", "typescript": "^4" } } diff --git a/packages/vue-apollo-ssr/src/index.ts b/packages/vue-apollo-ssr/src/index.ts index 951432c..5b77c7a 100644 --- a/packages/vue-apollo-ssr/src/index.ts +++ b/packages/vue-apollo-ssr/src/index.ts @@ -23,7 +23,7 @@ export function getStates (apolloClients: ApolloClients, options: GetStatesOptio const finalOptions = Object.assign({}, { exportNamespace: '', }, options) - const states = {} + const states: Record = {} for (const key in apolloClients) { const client = apolloClients[key] const state = client.cache.extract() diff --git a/packages/vue-apollo-ssr/tsconfig.json b/packages/vue-apollo-ssr/tsconfig.json index 5cd0252..2305420 100644 --- a/packages/vue-apollo-ssr/tsconfig.json +++ b/packages/vue-apollo-ssr/tsconfig.json @@ -3,9 +3,10 @@ "target": "es5", "module": "commonjs", "sourceMap": true, - "skipLibCheck": true + "skipLibCheck": true, + "strict": true }, "include": [ - "src/**/*", - ], -} \ No newline at end of file + "src/**/*" + ] +} diff --git a/packages/vue-apollo-util/src/errorLog.ts b/packages/vue-apollo-util/src/errorLog.ts index 73ecb48..73cd91b 100644 --- a/packages/vue-apollo-util/src/errorLog.ts +++ b/packages/vue-apollo-util/src/errorLog.ts @@ -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 = {} 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') } diff --git a/packages/vue-apollo-util/tsconfig.json b/packages/vue-apollo-util/tsconfig.json index 3fb6adb..20c8b74 100644 --- a/packages/vue-apollo-util/tsconfig.json +++ b/packages/vue-apollo-util/tsconfig.json @@ -3,8 +3,10 @@ "target": "es5", "module": "commonjs", "sourceMap": true, + "esModuleInterop": true, + "strict": true }, "include": [ - "src/**/*", - ], -} \ No newline at end of file + "src/**/*" + ] +}