diff --git a/.changeset/@graphprotocol_client-add-source-name-178-dependencies.md b/.changeset/@graphprotocol_client-add-source-name-178-dependencies.md
new file mode 100644
index 00000000..2f7a05ac
--- /dev/null
+++ b/.changeset/@graphprotocol_client-add-source-name-178-dependencies.md
@@ -0,0 +1,13 @@
+---
+"@graphprotocol/client-add-source-name": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
+- Removed dependency [`@graphql-tools/delegate@9.0.3` ↗︎](https://www.npmjs.com/package/@graphql-tools/delegate/v/9.0.3) (from `dependencies`)
+- Removed dependency [`@graphql-tools/wrap@9.0.4` ↗︎](https://www.npmjs.com/package/@graphql-tools/wrap/v/9.0.4) (from `dependencies`)
+- Removed dependency [`@graphql-tools/utils@8.10.0` ↗︎](https://www.npmjs.com/package/@graphql-tools/utils/v/8.10.0) (from `dependencies`)
+- Added dependency [`@graphql-tools/delegate@9.0.3` ↗︎](https://www.npmjs.com/package/@graphql-tools/delegate/v/9.0.3) (to `peerDependencies`)
+- Added dependency [`@graphql-tools/wrap@9.0.4` ↗︎](https://www.npmjs.com/package/@graphql-tools/wrap/v/9.0.4) (to `peerDependencies`)
+- Added dependency [`@graphql-tools/utils@8.10.0` ↗︎](https://www.npmjs.com/package/@graphql-tools/utils/v/8.10.0) (to `peerDependencies`)
diff --git a/.changeset/@graphprotocol_client-apollo-178-dependencies.md b/.changeset/@graphprotocol_client-apollo-178-dependencies.md
new file mode 100644
index 00000000..f997a45a
--- /dev/null
+++ b/.changeset/@graphprotocol_client-apollo-178-dependencies.md
@@ -0,0 +1,7 @@
+---
+"@graphprotocol/client-apollo": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
diff --git a/.changeset/@graphprotocol_client-auto-pagination-178-dependencies.md b/.changeset/@graphprotocol_client-auto-pagination-178-dependencies.md
new file mode 100644
index 00000000..b30f2371
--- /dev/null
+++ b/.changeset/@graphprotocol_client-auto-pagination-178-dependencies.md
@@ -0,0 +1,7 @@
+---
+"@graphprotocol/client-auto-pagination": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
diff --git a/.changeset/@graphprotocol_client-auto-type-merging-178-dependencies.md b/.changeset/@graphprotocol_client-auto-type-merging-178-dependencies.md
new file mode 100644
index 00000000..0a7b2fd1
--- /dev/null
+++ b/.changeset/@graphprotocol_client-auto-type-merging-178-dependencies.md
@@ -0,0 +1,7 @@
+---
+"@graphprotocol/client-auto-type-merging": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
diff --git a/.changeset/@graphprotocol_client-block-tracking-178-dependencies.md b/.changeset/@graphprotocol_client-block-tracking-178-dependencies.md
new file mode 100644
index 00000000..0b2d1da4
--- /dev/null
+++ b/.changeset/@graphprotocol_client-block-tracking-178-dependencies.md
@@ -0,0 +1,9 @@
+---
+"@graphprotocol/client-block-tracking": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
+- Added dependency [`@graphql-tools/delegate@^9.0.3` ↗︎](https://www.npmjs.com/package/@graphql-tools/delegate/v/null) (to `peerDependencies`)
+- Removed dependency [`@graphql-mesh/types@^0.78.0` ↗︎](https://www.npmjs.com/package/@graphql-mesh/types/v/null) (from `peerDependencies`)
diff --git a/.changeset/@graphprotocol_client-cli-178-dependencies.md b/.changeset/@graphprotocol_client-cli-178-dependencies.md
new file mode 100644
index 00000000..8a6c1cf3
--- /dev/null
+++ b/.changeset/@graphprotocol_client-cli-178-dependencies.md
@@ -0,0 +1,8 @@
+---
+"@graphprotocol/client-cli": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
+- Added dependency [`@graphprotocol/client-polling-live@0.0.0` ↗︎](https://www.npmjs.com/package/@graphprotocol/client-polling-live/v/0.0.0) (to `dependencies`)
diff --git a/.changeset/@graphprotocol_client-cli-dependencies.md b/.changeset/@graphprotocol_client-cli-dependencies.md
new file mode 100644
index 00000000..86b92e46
--- /dev/null
+++ b/.changeset/@graphprotocol_client-cli-dependencies.md
@@ -0,0 +1,7 @@
+---
+"@graphprotocol/client-cli": patch
+---
+
+### Dependencies Updates
+
+- Updated dependency ([`@graphql-mesh/cli@0.75.7` ↗︎](https://www.npmjs.com/package/@graphql-mesh/cli/v/0.75.7)) (was `0.75.6`, in `dependencies`)
diff --git a/.changeset/@graphprotocol_client-urql-178-dependencies.md b/.changeset/@graphprotocol_client-urql-178-dependencies.md
new file mode 100644
index 00000000..52fefa75
--- /dev/null
+++ b/.changeset/@graphprotocol_client-urql-178-dependencies.md
@@ -0,0 +1,7 @@
+---
+"@graphprotocol/client-urql": patch
+---
+
+dependencies updates:
+
+- Updated dependency [`tslib@^2.4.0` ↗︎](https://www.npmjs.com/package/tslib/v/null) (from `2.4.0`, in `dependencies`)
diff --git a/.changeset/config.json b/.changeset/config.json
index ebc8a0fe..5e2f173d 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -15,7 +15,10 @@
"react-query-example",
"nextjs-example",
"cross-chain-sdk",
- "cross-chain-extension"
+ "cross-chain-extension",
+ "live-queries-example",
+ "urql-live-example",
+ "*-example"
],
"access": "public",
"baseBranch": "main",
diff --git a/.changeset/metal-books-live.md b/.changeset/metal-books-live.md
new file mode 100644
index 00000000..91b78201
--- /dev/null
+++ b/.changeset/metal-books-live.md
@@ -0,0 +1,6 @@
+---
+'@graphprotocol/client-polling-live': major
+'@graphprotocol/client-cli': minor
+---
+
+New polling-based `@live` queries plugin.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3fa73def..194b565a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,14 +18,10 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v3
- - name: Use Node
- uses: actions/setup-node@master
+ - uses: the-guild-org/shared-config/setup@main
+ name: setup env
with:
- node-version: ${{ matrix.node-version }}
- cache: 'yarn'
-
- - name: Install Dependencies using Yarn
- run: yarn
+ nodeVersion: ${{ matrix.node-version }}
- name: Build Packages
run: yarn build
diff --git a/.gitignore b/.gitignore
index a6ded2d5..176466e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,6 @@ examples/**/node_modules
*.tsbuildinfo
coverage/
-.bob
\ No newline at end of file
+.bob
+.env
+.graphclient
diff --git a/README.md b/README.md
index bb3c6098..b07a3c5b 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ This library is intended to simplify the network aspect of data consumption for
| ✅ | Integration with `@apollo/client` | |
| ✅ | Integration with `urql` | |
| ✅ | TypeScript support | with built-in GraphQL Codegen and `TypedDocumentNode` |
+| ✅ | [`@live` queries](./docs/live.md) | Based on polling |
> You can find an [extended architecture design here](./docs/architecture.md)
diff --git a/docs/live.md b/docs/live.md
new file mode 100644
index 00000000..933fbc1c
--- /dev/null
+++ b/docs/live.md
@@ -0,0 +1,43 @@
+### `@live` queries in `graph-client`
+
+Graph-Client implements a custom `@live` directive that can make every GraphQL query work with real-time data.
+
+#### Getting Started
+
+Start by adding the following configuration to your `.graphclientrc.yml` file:
+
+```yml
+plugins:
+ - pollingLive:
+ defaultInterval: 1000
+```
+
+#### Usage
+
+Set the default update interval you wish to use, and then you can apply the following GraphQL `@directive` over your GraphQL queries:
+
+```graphql
+query ExampleQuery @live {
+ transactions(first: 2, orderBy: timestamp, orderDirection: desc) {
+ id
+ blockNumber
+ timestamp
+ }
+}
+```
+
+Or, you can specify a per-query interval:
+
+```graphql
+query ExampleQuery @live(interval: 5000) {
+ transactions(first: 2, orderBy: timestamp, orderDirection: desc) {
+ id
+ }
+}
+```
+
+#### Integrations
+
+Since the entire network layer (along with the `@live` mechanism) is implemented inside `graph-client` core, you can use Live queries with every GraphQL client (such as Urql or Apollo-Client), as long as it supports streame responses (`AsyncIterable`).
+
+No additional setup is required for GraphQL clients cache updates.
diff --git a/examples/live-queries/.graphclientrc.yml b/examples/live-queries/.graphclientrc.yml
new file mode 100644
index 00000000..22fd6c53
--- /dev/null
+++ b/examples/live-queries/.graphclientrc.yml
@@ -0,0 +1,12 @@
+sources:
+ - name: Sushi
+ handler:
+ graphql:
+ endpoint: https://api.thegraph.com/subgraphs/name/sushiswap/exchange
+
+plugins:
+ - pollingLive:
+ defaultInterval: 1000
+
+documents:
+ - ./example-query.graphql
diff --git a/examples/live-queries/example-query.graphql b/examples/live-queries/example-query.graphql
new file mode 100644
index 00000000..968c63cf
--- /dev/null
+++ b/examples/live-queries/example-query.graphql
@@ -0,0 +1,7 @@
+query ExampleQuery @live {
+ transactions(first: 2, orderBy: timestamp, orderDirection: desc) {
+ id
+ blockNumber
+ timestamp
+ }
+}
diff --git a/examples/live-queries/package.json b/examples/live-queries/package.json
new file mode 100644
index 00000000..a89ea1e5
--- /dev/null
+++ b/examples/live-queries/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "live-queries-example",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "build-client": "graphclient build",
+ "check": "tsc --pretty --noEmit",
+ "start": "graphclient serve-dev"
+ },
+ "dependencies": {
+ "@graphprotocol/client-cli": "2.1.3",
+ "@graphprotocol/client-polling-live": "0.0.0",
+ "graphql": "16.5.0"
+ }
+}
diff --git a/examples/urql-live-query/.gitignore b/examples/urql-live-query/.gitignore
new file mode 100644
index 00000000..61b5bf41
--- /dev/null
+++ b/examples/urql-live-query/.gitignore
@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.graphclient
\ No newline at end of file
diff --git a/examples/urql-live-query/.graphclientrc.yml b/examples/urql-live-query/.graphclientrc.yml
new file mode 100644
index 00000000..65540bb1
--- /dev/null
+++ b/examples/urql-live-query/.graphclientrc.yml
@@ -0,0 +1,12 @@
+sources:
+ - name: Sushi
+ handler:
+ graphql:
+ endpoint: https://api.thegraph.com/subgraphs/name/sushiswap/exchange
+
+plugins:
+ - pollingLive:
+ defaultInterval: 1000
+
+documents:
+ - ./src/example-query.graphql
diff --git a/examples/urql-live-query/README.md b/examples/urql-live-query/README.md
new file mode 100644
index 00000000..b7a46d0c
--- /dev/null
+++ b/examples/urql-live-query/README.md
@@ -0,0 +1,22 @@
+### The Graph Client / Urql / `@live`
+
+This example integrates The Graph Client with [Urql](https://formidable.com/open-source/urql/), with an example for using `@live` queries.
+
+By using `@live` over a `query`, we are able to get the Urql cache updating automatically when the actual data changes in the network layer.
+
+### Getting Started
+
+To run this example, make sure to install the dependencies in the root of the monorepo, build the client locally, and then run this example:
+
+```
+# In the root directory
+$ yarn install
+$ yarn build
+$ cd examples/urql-live-query/
+$ yarn build-client
+$ yarn start
+```
+
+### DevTools
+
+You can also run The Graph Client DevTools by running: `yarn graphiql`.
diff --git a/examples/urql-live-query/index.html b/examples/urql-live-query/index.html
new file mode 100644
index 00000000..38f38611
--- /dev/null
+++ b/examples/urql-live-query/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/examples/urql-live-query/package.json b/examples/urql-live-query/package.json
new file mode 100644
index 00000000..324eabb8
--- /dev/null
+++ b/examples/urql-live-query/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "urql-live-example",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "start": "vite",
+ "build": "vite build",
+ "preview": "vite preview",
+ "build-client": "graphclient build",
+ "graphiql": "graphclient serve-dev"
+ },
+ "dependencies": {
+ "@graphprotocol/client-urql": "1.0.4",
+ "graphql": "16.6.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "urql": "2.2.3"
+ },
+ "devDependencies": {
+ "@graphprotocol/client-cli": "2.1.3",
+ "@types/react": "18.0.17",
+ "@types/react-dom": "18.0.6",
+ "@vitejs/plugin-react": "2.0.1",
+ "typescript": "4.7.4",
+ "vite": "3.0.8"
+ }
+}
diff --git a/examples/urql-live-query/src/App.css b/examples/urql-live-query/src/App.css
new file mode 100644
index 00000000..271b115d
--- /dev/null
+++ b/examples/urql-live-query/src/App.css
@@ -0,0 +1,50 @@
+.App {
+ text-align: center;
+}
+
+.App-logo {
+ height: 20vmin;
+ pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ .App-logo {
+ animation: App-logo-spin infinite 20s linear;
+ }
+}
+
+.App-header {
+ background-color: #282c34;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ color: white;
+}
+
+.App-link {
+ color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+button {
+ font-size: 18px;
+}
+
+fieldset {
+ width: 50vw;
+}
+
+textarea {
+ width: 100%;
+}
diff --git a/examples/urql-live-query/src/App.tsx b/examples/urql-live-query/src/App.tsx
new file mode 100644
index 00000000..560f326a
--- /dev/null
+++ b/examples/urql-live-query/src/App.tsx
@@ -0,0 +1,44 @@
+import logo from './logo.svg'
+import './App.css'
+import { useQuery } from 'urql'
+import { ExampleQueryDocument } from '../.graphclient'
+
+function App() {
+ const [result, reexecuteQuery] = useQuery({
+ query: ExampleQueryDocument,
+ })
+
+ const { data, fetching, error } = result
+ return (
+
+
+
+ Graph Client with Urql Example with @live
+
+
+
+ {fetching ? 'Loading...' : 'You can find the result below...'}
+
+
+
+ )
+}
+
+export default App
diff --git a/examples/urql-live-query/src/example-query.graphql b/examples/urql-live-query/src/example-query.graphql
new file mode 100644
index 00000000..968c63cf
--- /dev/null
+++ b/examples/urql-live-query/src/example-query.graphql
@@ -0,0 +1,7 @@
+query ExampleQuery @live {
+ transactions(first: 2, orderBy: timestamp, orderDirection: desc) {
+ id
+ blockNumber
+ timestamp
+ }
+}
diff --git a/examples/urql-live-query/src/favicon.svg b/examples/urql-live-query/src/favicon.svg
new file mode 100644
index 00000000..de4aeddc
--- /dev/null
+++ b/examples/urql-live-query/src/favicon.svg
@@ -0,0 +1,15 @@
+
diff --git a/examples/urql-live-query/src/index.css b/examples/urql-live-query/src/index.css
new file mode 100644
index 00000000..7323ae85
--- /dev/null
+++ b/examples/urql-live-query/src/index.css
@@ -0,0 +1,11 @@
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
+ 'Droid Sans', 'Helvetica Neue', sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
+}
diff --git a/examples/urql-live-query/src/logo.svg b/examples/urql-live-query/src/logo.svg
new file mode 100644
index 00000000..714dc76e
--- /dev/null
+++ b/examples/urql-live-query/src/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/urql-live-query/src/main.tsx b/examples/urql-live-query/src/main.tsx
new file mode 100644
index 00000000..8b7db8c6
--- /dev/null
+++ b/examples/urql-live-query/src/main.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import './index.css'
+import App from './App'
+import './App.css'
+import { createClient, Provider } from 'urql'
+import { graphExchange } from '@graphprotocol/client-urql'
+import * as GraphClient from '../.graphclient'
+
+const client = createClient({
+ url: 'http://localhost:4000/graphql',
+ exchanges: [graphExchange(GraphClient)],
+})
+
+ReactDOM.render(
+
+
+
+
+ ,
+ document.getElementById('root'),
+)
diff --git a/examples/urql-live-query/src/vite-env.d.ts b/examples/urql-live-query/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/examples/urql-live-query/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/urql-live-query/tsconfig.json b/examples/urql-live-query/tsconfig.json
new file mode 100644
index 00000000..8a10db24
--- /dev/null
+++ b/examples/urql-live-query/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/examples/urql-live-query/tsconfig.node.json b/examples/urql-live-query/tsconfig.node.json
new file mode 100644
index 00000000..e993792c
--- /dev/null
+++ b/examples/urql-live-query/tsconfig.node.json
@@ -0,0 +1,8 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "esnext",
+ "moduleResolution": "node"
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/examples/urql-live-query/vite.config.ts b/examples/urql-live-query/vite.config.ts
new file mode 100644
index 00000000..5a33944a
--- /dev/null
+++ b/examples/urql-live-query/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/jest.config.js b/jest.config.js
index b89f316b..ee7a5ce2 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -21,9 +21,9 @@ module.exports = {
coverageThreshold: {
global: {
branches: 80,
- functions: 95,
- lines: 90,
- statements: 90,
+ functions: 80,
+ lines: 80,
+ statements: 80,
},
},
resolver: 'bob-the-bundler/jest-resolver.js',
diff --git a/packages/add-source-name/package.json b/packages/add-source-name/package.json
index ee34f062..499de14d 100644
--- a/packages/add-source-name/package.json
+++ b/packages/add-source-name/package.json
@@ -46,17 +46,17 @@
"access": "public"
},
"dependencies": {
- "@graphql-tools/delegate": "9.0.3",
- "@graphql-tools/wrap": "9.0.4",
- "@graphql-tools/utils": "8.10.0",
"lodash": "4.17.21",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"devDependencies": {
"@types/lodash": "4.14.183"
},
"peerDependencies": {
"graphql": "^15.2.0 || ^16.0.0",
+ "@graphql-tools/delegate": "9.0.3",
+ "@graphql-tools/wrap": "9.0.4",
+ "@graphql-tools/utils": "8.10.0",
"@graphql-mesh/types": "^0.78.0"
},
"type": "module"
diff --git a/packages/apollo/package.json b/packages/apollo/package.json
index 4dc7746f..6f3fd539 100644
--- a/packages/apollo/package.json
+++ b/packages/apollo/package.json
@@ -47,7 +47,7 @@
},
"dependencies": {
"@graphql-mesh/apollo-link": "5.0.2",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^15.2.0 || ^16.0.0",
diff --git a/packages/auto-pagination/package.json b/packages/auto-pagination/package.json
index fb645707..13cfb393 100644
--- a/packages/auto-pagination/package.json
+++ b/packages/auto-pagination/package.json
@@ -50,7 +50,7 @@
"@graphql-tools/wrap": "9.0.4",
"@graphql-tools/utils": "8.10.0",
"lodash": "4.17.21",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"devDependencies": {
"@types/lodash": "4.14.183",
diff --git a/packages/auto-type-merging/package.json b/packages/auto-type-merging/package.json
index d555303d..c2b11bd7 100644
--- a/packages/auto-type-merging/package.json
+++ b/packages/auto-type-merging/package.json
@@ -48,7 +48,7 @@
"dependencies": {
"@graphql-tools/delegate": "9.0.3",
"@graphql-mesh/transform-type-merging": "0.4.19",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^15.2.0 || ^16.0.0",
diff --git a/packages/block-tracking/package.json b/packages/block-tracking/package.json
index d7738827..c5799d46 100644
--- a/packages/block-tracking/package.json
+++ b/packages/block-tracking/package.json
@@ -47,11 +47,11 @@
},
"dependencies": {
"@graphql-tools/utils": "8.10.0",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^15.2.0 || ^16.0.0",
- "@graphql-mesh/types": "^0.78.0"
+ "@graphql-tools/delegate": "^9.0.3"
},
"type": "module"
}
diff --git a/packages/block-tracking/src/index.ts b/packages/block-tracking/src/index.ts
index ecb39df0..62ce37d4 100644
--- a/packages/block-tracking/src/index.ts
+++ b/packages/block-tracking/src/index.ts
@@ -1,5 +1,4 @@
-import type { MeshTransform } from '@graphql-mesh/types'
-import type { DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'
+import type { DelegationContext, SubschemaConfig, Transform } from '@graphql-tools/delegate'
import type { ExecutionRequest } from '@graphql-tools/utils'
import { memoize1, memoize2 } from '@graphql-tools/utils'
import {
@@ -163,7 +162,7 @@ function getRequestIdentifier(delegationContext: DelegationContext): any {
const schemaMinBlockMap = new WeakMap()
-export default class BlockTrackingTransform implements MeshTransform {
+export default class BlockTrackingTransform implements Transform {
public config: Required
constructor({ config }: { config?: BlockTrackingTransformConfig } = {}) {
this.config = {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 7d13ed07..dee0a8b0 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -51,11 +51,12 @@
"dependencies": {
"@graphql-mesh/cli": "0.75.9",
"@graphql-mesh/graphql": "0.29.9",
- "tslib": "2.4.0",
+ "tslib": "^2.4.0",
"@graphprotocol/client-auto-pagination": "1.1.2",
"@graphprotocol/client-auto-type-merging": "1.0.3",
"@graphprotocol/client-block-tracking": "1.0.5",
- "@graphprotocol/client-add-source-name": "1.0.4"
+ "@graphprotocol/client-add-source-name": "1.0.4",
+ "@graphprotocol/client-polling-live": "0.0.0"
},
"devDependencies": {
"graphql": "16.6.0"
diff --git a/packages/polling-live/package.json b/packages/polling-live/package.json
new file mode 100644
index 00000000..8bc1d9bb
--- /dev/null
+++ b/packages/polling-live/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "@graphprotocol/client-polling-live",
+ "version": "0.0.0",
+ "description": "",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/graphprotocol/graph-client.git",
+ "directory": "packages/polling-live"
+ },
+ "scripts": {
+ "prepack": "bob prepack",
+ "check": "tsc --pretty --noEmit"
+ },
+ "keywords": [
+ "thegraph",
+ "graphql",
+ "client"
+ ],
+ "license": "MIT",
+ "sideEffects": false,
+ "main": "dist/cjs/index.js",
+ "module": "dist/esm/index.js",
+ "typings": "dist/typings/index.d.ts",
+ "typescript": {
+ "definition": "dist/typings/index.d.ts"
+ },
+ "exports": {
+ ".": {
+ "require": {
+ "types": "./dist/typings/index.d.cts",
+ "default": "./dist/cjs/index.js"
+ },
+ "import": {
+ "types": "./dist/typings/index.d.ts",
+ "default": "./dist/esm/index.js"
+ },
+ "default": {
+ "types": "./dist/typings/index.d.ts",
+ "default": "./dist/esm/index.js"
+ }
+ },
+ "./package.json": "./package.json"
+ },
+ "publishConfig": {
+ "directory": "dist",
+ "access": "public"
+ },
+ "dependencies": {
+ "@repeaterjs/repeater": "^3.0.4",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "graphql": "^15.2.0 || ^16.0.0",
+ "@graphql-tools/merge": "^8.3.1",
+ "@envelop/core": "^2.4.2"
+ },
+ "type": "module"
+}
diff --git a/packages/polling-live/src/index.ts b/packages/polling-live/src/index.ts
new file mode 100644
index 00000000..5b257a92
--- /dev/null
+++ b/packages/polling-live/src/index.ts
@@ -0,0 +1,85 @@
+import { isAsyncIterable, Plugin } from '@envelop/core'
+import { DirectiveNode, GraphQLError, Kind, visit } from 'graphql'
+import { Repeater } from '@repeaterjs/repeater'
+import { mergeSchemas } from '@graphql-tools/schema'
+
+export default function usePollingLive({ config: { defaultInterval = 1000 } = {} } = {}): Plugin<{}> {
+ return {
+ onSchemaChange({ schema, replaceSchema }) {
+ if (!schema.getDirective('live')) {
+ replaceSchema(
+ mergeSchemas({
+ schemas: [schema],
+ typeDefs: /* GraphQL */ `
+ directive @live(interval: Int) on QUERY
+ `,
+ }),
+ )
+ }
+ },
+ onExecute({ args, executeFn, setExecuteFn }) {
+ let liveDirectiveNode: DirectiveNode | undefined
+ args.document = visit(args.document, {
+ OperationDefinition(node) {
+ if (args.operationName != null && node.name?.value !== args.operationName) {
+ return
+ }
+ const directives: DirectiveNode[] = []
+ if (node.directives && node.operation === 'query') {
+ for (const directive of node.directives) {
+ if (directive.name.value === 'live') {
+ liveDirectiveNode = directive
+ } else {
+ directives.push(directive)
+ }
+ }
+ return {
+ ...node,
+ directives,
+ }
+ }
+ return node
+ },
+ })
+ if (liveDirectiveNode) {
+ const intervalArgNode = liveDirectiveNode.arguments?.find((argNode) => argNode.name.value === 'interval')
+ let intervalMs = defaultInterval
+ if (intervalArgNode?.value?.kind === Kind.INT) {
+ intervalMs = parseInt(intervalArgNode.value.value)
+ }
+
+ setExecuteFn(
+ (args) =>
+ new Repeater((push, stop) => {
+ let finished = false
+ async function pump() {
+ if (finished) {
+ return
+ }
+ const result: any = await executeFn(args)
+ if (isAsyncIterable(result)) {
+ push({
+ data: null,
+ errors: [new GraphQLError('Execution returned AsyncIterable which is not supported!')],
+ isLive: true,
+ })
+ stop()
+ return
+ }
+ result.isLive = true
+ if (finished) {
+ return
+ }
+ push(result)
+ setTimeout(pump, intervalMs)
+ }
+ pump()
+ stop.then(() => {
+ finished = true
+ })
+ }) as any,
+ )
+ }
+ },
+ }
+}
diff --git a/packages/polling-live/test/polling-live.test.ts b/packages/polling-live/test/polling-live.test.ts
new file mode 100644
index 00000000..0a5e9e8f
--- /dev/null
+++ b/packages/polling-live/test/polling-live.test.ts
@@ -0,0 +1,142 @@
+import { envelop, useSchema } from '@envelop/core'
+import { makeExecutableSchema } from '@graphql-tools/schema'
+import { Repeater } from '@repeaterjs/repeater'
+import { parse } from 'graphql'
+import usePollingLive from '../src/index.js'
+
+describe('Polling Live Queries', () => {
+ const schema = makeExecutableSchema({
+ typeDefs: /* GraphQL */ `
+ type Query {
+ timestamp: String
+ }
+ `,
+ resolvers: {
+ Query: {
+ timestamp: () => new Date().toISOString(),
+ },
+ },
+ })
+ const getEnveloped = envelop({
+ plugins: [useSchema(schema), usePollingLive()],
+ })
+ afterEach(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 500))
+ })
+ it('should create a live stream with the given argument as an interval', async () => {
+ const enveloped = getEnveloped()
+
+ const result: any = await enveloped.execute({
+ schema: enveloped.schema,
+ document: parse(/* GraphQL */ `
+ query TestQuery @live(interval: 500) {
+ timestamp
+ }
+ `),
+ operationName: 'TestQuery',
+ })
+
+ expect(result[Symbol.asyncIterator]).toBeDefined()
+
+ let lastTimestamp
+ let i = 0
+ for await (const item of result) {
+ expect(item?.isLive).toBe(true)
+ expect(item?.data?.timestamp).toBeDefined()
+ if (lastTimestamp) {
+ expect(new Date(item?.data?.timestamp).getTime() - new Date(lastTimestamp).getTime()).toBeGreaterThan(0.49)
+ }
+ lastTimestamp = item?.data?.timestamp
+ if (i > 2) {
+ break
+ }
+ i++
+ }
+ expect.assertions(12)
+ })
+ it('should create a live stream with 1000 ms interval by default', async () => {
+ const enveloped = getEnveloped()
+
+ const result: any = await enveloped.execute({
+ schema: enveloped.schema,
+ document: parse(/* GraphQL */ `
+ query TestQuery @live {
+ timestamp
+ }
+ `),
+ operationName: 'TestQuery',
+ })
+
+ expect(result[Symbol.asyncIterator]).toBeDefined()
+
+ let lastTimestamp
+ let i = 0
+ for await (const item of result) {
+ expect(item?.isLive).toBe(true)
+ expect(item?.data?.timestamp).toBeDefined()
+ if (lastTimestamp) {
+ expect(new Date(item?.data?.timestamp).getTime() - new Date(lastTimestamp).getTime()).toBeGreaterThan(0.99)
+ }
+ lastTimestamp = item?.data?.timestamp
+ if (i > 2) {
+ break
+ }
+ i++
+ }
+ expect.assertions(12)
+ })
+ it('should return regular results if there is no @live', async () => {
+ const enveloped = getEnveloped()
+
+ const result = await enveloped.execute({
+ schema: enveloped.schema,
+ document: parse(/* GraphQL */ `
+ query TestQuery {
+ timestamp
+ }
+ `),
+ operationName: 'TestQuery',
+ })
+
+ expect(result?.data?.timestamp).toBeDefined()
+ })
+ it('should throw an error if the original executor returns AsyncIterable', async () => {
+ const getEnveloped = envelop({
+ plugins: [
+ useSchema(schema),
+ {
+ onExecute({ setExecuteFn }) {
+ return setExecuteFn(
+ () =>
+ new Repeater((push, stop) => {
+ push({ data: { timestamp: new Date().toISOString() } })
+ stop()
+ }) as any,
+ )
+ },
+ },
+ usePollingLive(),
+ ],
+ })
+
+ const enveloped = getEnveloped()
+
+ const result = await enveloped.execute({
+ schema: enveloped.schema,
+ document: parse(/* GraphQL */ `
+ query TestQuery @live {
+ timestamp
+ }
+ `),
+ operationName: 'TestQuery',
+ })
+
+ expect(result[Symbol.asyncIterator]).toBeDefined()
+ for await (const item of result as AsyncIterable) {
+ expect(item?.data).toBe(null)
+ expect(item?.errors?.[0]?.message).toBe('Execution returned AsyncIterable which is not supported!')
+ }
+
+ expect.assertions(3)
+ })
+})
diff --git a/packages/urql/package.json b/packages/urql/package.json
index 9a0de1c8..31b22056 100644
--- a/packages/urql/package.json
+++ b/packages/urql/package.json
@@ -39,20 +39,6 @@
"default": "./dist/esm/index.js"
}
},
- "./*": {
- "require": {
- "types": "./dist/typings/*.d.cts",
- "default": "./dist/cjs/*.js"
- },
- "import": {
- "types": "./dist/typings/*.d.ts",
- "default": "./dist/esm/*.js"
- },
- "default": {
- "types": "./dist/typings/*.d.ts",
- "default": "./dist/esm/*.js"
- }
- },
"./package.json": "./package.json"
},
"publishConfig": {
@@ -61,7 +47,7 @@
},
"dependencies": {
"@graphql-mesh/urql-exchange": "5.0.2",
- "tslib": "2.4.0"
+ "tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^15.2.0 || ^16.0.0",
diff --git a/yarn.lock b/yarn.lock
index ccf4f3e9..052ec07d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2948,7 +2948,7 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-array.prototype.flat@^1.2.3, array.prototype.flat@^1.2.5:
+array.prototype.flat@^1.2.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b"
integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==
@@ -2958,6 +2958,15 @@ array.prototype.flat@^1.2.3, array.prototype.flat@^1.2.5:
es-abstract "^1.19.2"
es-shim-unscopables "^1.0.0"
+array.prototype.flat@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13"
+ integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.0"
+
array.prototype.flatmap@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f"
@@ -4890,7 +4899,7 @@ graphql-ws@5.10.0, graphql-ws@^5.4.1:
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.10.0.tgz#3fb47a4e809e0d2e7c197f1bca754fa9f31b940e"
integrity sha512-ewbPzHQdRZgNCPDH9Yr6xccSeZfk3fmpO/AGGGg4KkM5gc6oAOJQ10Oui1EqprhVOyRbOll9bw2qAkOiOwfTag==
-graphql@16.6.0:
+graphql@16.5.0, graphql@16.6.0:
version "16.6.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb"
integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==
@@ -7737,16 +7746,16 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
-tslib@2.4.0, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@~2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
- integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
-
tslib@^1.8.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
+tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@~2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
+ integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
+
tsup@^5.11.6:
version "5.12.6"
resolved "https://registry.yarnpkg.com/tsup/-/tsup-5.12.6.tgz#29e057066bcc2cdd0238104e5a68e95d4f6f49a6"