From e5d70c3677a7667f5017c6ac30387d007d3eb86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Horv=C3=A1th?= Date: Tue, 16 Jul 2024 21:35:49 +0200 Subject: [PATCH] feat: add vike-react-apollo --- README.md | 4 + examples/apollo/.gitignore | 2 + examples/apollo/.test-dev.test.ts | 2 + examples/apollo/.test-preview.test.ts | 2 + examples/apollo/.testRun.ts | 41 +++ examples/apollo/assets/logo.svg | 36 ++ examples/apollo/layouts/HeadDefault.tsx | 14 + examples/apollo/layouts/LayoutDefault.tsx | 72 ++++ examples/apollo/layouts/style.css | 29 ++ examples/apollo/package.json | 23 ++ examples/apollo/pages/+ApolloClient.ts | 9 + examples/apollo/pages/+config.ts | 15 + examples/apollo/pages/_error/+Page.tsx | 23 ++ examples/apollo/pages/index/+Page.tsx | 26 ++ examples/apollo/pages/index/Counter.tsx | 9 + examples/apollo/pages/index/Countries.tsx | 24 ++ examples/apollo/readme.md | 8 + examples/apollo/tsconfig.json | 13 + examples/apollo/vite.config.ts | 7 + package.json | 3 +- packages/vike-react-apollo/.gitignore | 2 + packages/vike-react-apollo/CHANGELOG.md | 0 packages/vike-react-apollo/README.md | 219 ++++++++++++ packages/vike-react-apollo/package.json | 63 ++++ .../vike-react-apollo/renderer/+config.ts | 28 ++ .../vike-react-apollo/renderer/Transport.tsx | 22 ++ .../vike-react-apollo/renderer/Wrapper.tsx | 11 + packages/vike-react-apollo/src/index.ts | 1 + .../vike-react-apollo/src/withFallback.tsx | 134 ++++++++ packages/vike-react-apollo/tsconfig.json | 24 ++ packages/vike-react-apollo/utils/assert.ts | 6 + pnpm-lock.yaml | 313 +++++++++++++++++- 32 files changed, 1183 insertions(+), 2 deletions(-) create mode 100644 examples/apollo/.gitignore create mode 100644 examples/apollo/.test-dev.test.ts create mode 100644 examples/apollo/.test-preview.test.ts create mode 100644 examples/apollo/.testRun.ts create mode 100644 examples/apollo/assets/logo.svg create mode 100644 examples/apollo/layouts/HeadDefault.tsx create mode 100644 examples/apollo/layouts/LayoutDefault.tsx create mode 100644 examples/apollo/layouts/style.css create mode 100644 examples/apollo/package.json create mode 100644 examples/apollo/pages/+ApolloClient.ts create mode 100644 examples/apollo/pages/+config.ts create mode 100644 examples/apollo/pages/_error/+Page.tsx create mode 100644 examples/apollo/pages/index/+Page.tsx create mode 100644 examples/apollo/pages/index/Counter.tsx create mode 100644 examples/apollo/pages/index/Countries.tsx create mode 100644 examples/apollo/readme.md create mode 100644 examples/apollo/tsconfig.json create mode 100644 examples/apollo/vite.config.ts create mode 100644 packages/vike-react-apollo/.gitignore create mode 100644 packages/vike-react-apollo/CHANGELOG.md create mode 100644 packages/vike-react-apollo/README.md create mode 100644 packages/vike-react-apollo/package.json create mode 100644 packages/vike-react-apollo/renderer/+config.ts create mode 100644 packages/vike-react-apollo/renderer/Transport.tsx create mode 100644 packages/vike-react-apollo/renderer/Wrapper.tsx create mode 100644 packages/vike-react-apollo/src/index.ts create mode 100644 packages/vike-react-apollo/src/withFallback.tsx create mode 100644 packages/vike-react-apollo/tsconfig.json create mode 100644 packages/vike-react-apollo/utils/assert.ts diff --git a/README.md b/README.md index 35979c02..109ad1ff 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ - Docs: [README.md](packages/vike-react-query#readme) - Version history: [CHANGELOG.md](packages/vike-react-query/CHANGELOG.md) - Source code: [packages/vike-react-query/](packages/vike-react-query) +- `vike-react-apollo` ([Apollo GraphQL](https://www.apollographql.com/docs/react) integration) + - Docs: [README.md](packages/vike-react-apollo#readme) + - Version history: [CHANGELOG.md](packages/vike-react-apollo/CHANGELOG.md) + - Source code: [packages/vike-react-apollo/](packages/vike-react-apollo) > [!NOTE] > The source code is [small, simple, and highly polished](https://vike.dev/vike-react#under-the-hood). Contributing is easy and welcome, see [CONTRIBUTING.md](CONTRIBUTING.md) to get started. diff --git a/examples/apollo/.gitignore b/examples/apollo/.gitignore new file mode 100644 index 00000000..b0a5c349 --- /dev/null +++ b/examples/apollo/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/dist/ diff --git a/examples/apollo/.test-dev.test.ts b/examples/apollo/.test-dev.test.ts new file mode 100644 index 00000000..fbafdbbc --- /dev/null +++ b/examples/apollo/.test-dev.test.ts @@ -0,0 +1,2 @@ +import { testRun } from './.testRun' +testRun('pnpm run dev') diff --git a/examples/apollo/.test-preview.test.ts b/examples/apollo/.test-preview.test.ts new file mode 100644 index 00000000..6cd5bbe4 --- /dev/null +++ b/examples/apollo/.test-preview.test.ts @@ -0,0 +1,2 @@ +import { testRun } from './.testRun' +testRun('pnpm run preview') diff --git a/examples/apollo/.testRun.ts b/examples/apollo/.testRun.ts new file mode 100644 index 00000000..8fcaa9ca --- /dev/null +++ b/examples/apollo/.testRun.ts @@ -0,0 +1,41 @@ +export { testRun } + +import { test, expect, run, fetchHtml, page, getServerUrl, autoRetry } from '@brillout/test-e2e' + +function testRun(cmd: `pnpm run ${'dev' | 'preview'}`) { + run(cmd) + + const content = 'United States' + const loading = 'Loading contries...' + test('HTML', async () => { + const html = await fetchHtml('/') + expect(getTitle(html)).toBe('My Vike + React App') + // fetchHtml() awaits the stream + expect(html).toContain(content) + }) + test('DOM', async () => { + await page.goto(getServerUrl() + '/') + const body = await page.textContent('body') + // Playwright seems to await the HTML stream + expect(body).not.toContain(loading) + expect(body).toContain(content) + await testCounter() + }) +} + +function getTitle(html: string) { + const title = html.match(/(.*?)<\/title>/i)?.[1] + return title +} + +async function testCounter() { + // autoRetry() for awaiting client-side code loading & executing + await autoRetry( + async () => { + expect(await page.textContent('button')).toBe('Counter 0') + await page.click('button') + expect(await page.textContent('button')).toContain('Counter 1') + }, + { timeout: 5 * 1000 } + ) +} diff --git a/examples/apollo/assets/logo.svg b/examples/apollo/assets/logo.svg new file mode 100644 index 00000000..94d3caa0 --- /dev/null +++ b/examples/apollo/assets/logo.svg @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="175" height="175" fill="none" version="1.1" viewBox="0 0 175 175" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <metadata> + <rdf:RDF> + <cc:Work rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> + <dc:title/> + </cc:Work> + </rdf:RDF> + </metadata> + <defs> + <linearGradient id="linearGradient880" x1="108.64" x2="115.51" y1="88.726" y2="136.2" gradientTransform="matrix(1.0498 0 0 1.0498 -2.9171 -2.9658)" gradientUnits="userSpaceOnUse"> + <stop stop-color="#ffea83" offset="0"/> + <stop stop-color="#FFDD35" offset=".083333"/> + <stop stop-color="#FFA800" offset="1"/> + </linearGradient> + <linearGradient id="paint2_linear" x1="48.975" x2="61.299" y1="3.9232" y2="158.04" gradientTransform="translate(-2.832e-5)" gradientUnits="userSpaceOnUse"> + <stop stop-color="#FFEA83" offset="0"/> + <stop stop-color="#FFDD35" offset=".083333"/> + <stop stop-color="#FFA800" offset="1"/> + </linearGradient> + <linearGradient id="paint0_linear-6" x1="-1.4492" x2="116.62" y1="-5.8123" y2="137.08" gradientTransform="translate(-2.832e-5)" gradientUnits="userSpaceOnUse"> + <stop stop-color="#41D1FF" offset="0"/> + <stop stop-color="#BD34FE" offset="1"/> + </linearGradient> + </defs> + <circle cx="87.5" cy="87.5" r="87.5" fill="#c4c4c4"/> + <circle cx="87.5" cy="87.5" r="87.5" fill="url(#paint0_linear-6)"/> + <g transform="translate(632.92 54.355)" fill="#d38787" stroke-width="1.0614"> + <path d="m-549.75 68.457c-5.7533-3.1217-6.1166-5.2295-6.1166-35.489 0-30.458 0.35464-32.448 6.3339-35.54 3.9943-2.0655 24.279-2.2805 26.735-0.28333 0.89718 0.72974 6.7203 6.6637 12.94 13.187l11.309 11.86v19.575c0 18.473-0.12956 19.74-2.3011 22.5-4.0223 5.1136-7.558 5.8565-27.65 5.8099-14.15-0.03287-19.008-0.40294-21.25-1.6191zm42.473-6.3594c2.27-1.59 2.359-2.2909 2.359-18.575v-16.923h-6.9521c-12.443 0-16.4-4.0845-16.4-16.93v-7.4828h-8.9464c-6.7178 0-9.3619 0.41549-10.614 1.668-2.5031 2.5031-2.5031 55.724 0 58.228 2.4502 2.4502 37.058 2.4636 40.553 0.01609zm-1.8867-42.165c0-0.16422-2.8659-3.1346-6.3686-6.6008l-6.3686-6.3022v4.9328c0 6.3185 1.8955 8.2687 8.0366 8.2687 2.5854 0 4.7007-0.13434 4.7007-0.29859zm-57.57 44.279c-5.6185-3.0486-6.1166-5.593-6.1166-31.243 0-18.891 0.31331-24.063 1.6101-26.571 1.809-3.4981 6.5048-6.3339 10.489-6.3339 2.4847 0 2.5814 0.19984 1.541 3.1843-0.61054 1.7514-1.7457 3.1843-2.5226 3.1843-0.77686 0-2.1631 0.75059-3.0805 1.668-2.4923 2.4923-2.4923 47.244 0 49.736 0.91739 0.9174 2.3036 1.668 3.0805 1.668 0.77688 0 1.912 1.4329 2.5226 3.1843 1.0562 3.0298 0.97108 3.1822-1.7537 3.1418-1.575-0.02331-4.1713-0.75194-5.7694-1.6191zm-16.983-4.2458c-5.4392-2.9512-6.1166-5.9415-6.1166-26.997 0-15.096 0.345-19.878 1.6101-22.325 1.7476-3.3796 6.4758-6.3339 10.137-6.3339 1.8666 0 2.1789 0.44955 1.6594 2.3882-0.35184 1.3135-0.64655 2.7465-0.65453 3.1843-8e-3 0.43784-0.69682 0.79608-1.5308 0.79608-0.83399 0-2.2669 0.75059-3.1843 1.668-2.4767 2.4767-2.4767 38.768 0 41.244 0.91741 0.91739 2.2946 1.668 3.0605 1.668 1.196 0 2.6402 2.995 2.6871 5.5726 0.0241 1.3294-4.5804 0.80962-7.6676-0.8655z" style="mix-blend-mode:lighten"/> + <path d="m-552.2 68.911c-5.7533-3.1217-6.1166-5.2295-6.1166-35.489 0-30.458 0.35463-32.448 6.3339-35.54 3.9943-2.0655 24.279-2.2805 26.735-0.28333 0.89718 0.72974 6.7203 6.6637 12.94 13.187l11.309 11.86v19.575c0 18.473-0.12957 19.74-2.3011 22.5-4.0223 5.1136-7.558 5.8565-27.65 5.8099-14.15-0.03287-19.008-0.40294-21.25-1.6191zm42.473-6.3594c2.27-1.59 2.359-2.2909 2.359-18.575v-16.923h-6.952c-12.443 0-16.4-4.0845-16.4-16.93v-7.4828h-8.9464c-6.7179 0-9.3619 0.41549-10.614 1.668-2.5031 2.5031-2.5031 55.724 0 58.228 2.4502 2.4502 37.058 2.4636 40.553 0.01609zm-1.8867-42.165c0-0.16422-2.8659-3.1346-6.3686-6.6008l-6.3686-6.3022v4.9328c0 6.3185 1.8955 8.2688 8.0366 8.2688 2.5854 0 4.7007-0.13434 4.7007-0.29859zm-57.57 44.279c-5.6185-3.0486-6.1166-5.593-6.1166-31.243 0-18.891 0.31331-24.063 1.6101-26.571 1.809-3.4981 6.5048-6.3339 10.489-6.3339 2.4847 0 2.5814 0.19984 1.541 3.1843-0.61054 1.7514-1.7457 3.1843-2.5226 3.1843-0.77687 0-2.1631 0.75059-3.0805 1.668-2.4923 2.4923-2.4923 47.244 0 49.736 0.91741 0.91739 2.3036 1.668 3.0805 1.668 0.77686 0 1.912 1.4329 2.5226 3.1843 1.0562 3.0298 0.97107 3.1822-1.7537 3.1418-1.575-0.02331-4.1713-0.75194-5.7694-1.6191zm-16.983-4.2458c-5.4392-2.9512-6.1166-5.9415-6.1166-26.997 0-15.096 0.34502-19.878 1.6101-22.325 1.7476-3.3796 6.4758-6.3339 10.137-6.3339 1.8666 0 2.1789 0.44955 1.6594 2.3882-0.35182 1.3135-0.64653 2.7465-0.65452 3.1843-8e-3 0.43784-0.69683 0.79608-1.5308 0.79608-0.83397 0-2.2669 0.75059-3.1843 1.668-2.4767 2.4767-2.4767 38.768 0 41.245 0.9174 0.91739 2.2946 1.668 3.0605 1.668 1.196 0 2.6402 2.995 2.6871 5.5726 0.0241 1.3294-4.5804 0.80962-7.6676-0.8655z" fill-opacity=".47466" style="mix-blend-mode:lighten"/> + </g> + <path d="m128.48 88.913-24.027 4.6784c-0.39475 0.07685-0.68766 0.40944-0.71076 0.80849l-1.4782 24.805c-0.0347 0.58371 0.50497 1.0372 1.0792 0.90602l6.6886-1.5338c0.62676-0.14383 1.1916 0.40419 1.0635 1.0299l-1.9874 9.6702c-0.13438 0.65091 0.48084 1.2073 1.1202 1.0142l4.1322-1.2472c0.64041-0.19317 1.2556 0.36535 1.1202 1.0162l-3.158 15.191c-0.19842 0.95011 1.074 1.4677 1.6042 0.653l0.35485-0.54382 19.578-38.827c0.32755-0.64985-0.23727-1.391-0.95641-1.2535l-6.8849 1.3207c-0.6467 0.12389-1.1979-0.47453-1.0152-1.1034l4.4944-15.482c0.18266-0.63012-0.36955-1.2295-1.0173-1.1034z" fill="url(#linearGradient880)" stroke-width="1.0498"/> + <rect x="3" y="3" width="169" height="169" rx="84.5" stroke="url(#paint2_linear)" stroke-width="6" style="mix-blend-mode:soft-light"/> +</svg> diff --git a/examples/apollo/layouts/HeadDefault.tsx b/examples/apollo/layouts/HeadDefault.tsx new file mode 100644 index 00000000..ef374b88 --- /dev/null +++ b/examples/apollo/layouts/HeadDefault.tsx @@ -0,0 +1,14 @@ +export default HeadDefault + +import React from 'react' +import logoUrl from '../assets/logo.svg' + +function HeadDefault() { + return ( + <> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content="Demo showcasing Vike + React" /> + <link rel="icon" href={logoUrl} /> + </> + ) +} diff --git a/examples/apollo/layouts/LayoutDefault.tsx b/examples/apollo/layouts/LayoutDefault.tsx new file mode 100644 index 00000000..1e0de2b6 --- /dev/null +++ b/examples/apollo/layouts/LayoutDefault.tsx @@ -0,0 +1,72 @@ +export default LayoutDefault + +import './style.css' +import React from 'react' +import logoUrl from '../assets/logo.svg' + +function LayoutDefault({ children }: { children: React.ReactNode }) { + return ( + <div + style={{ + display: 'flex', + maxWidth: 900, + margin: 'auto' + }} + > + <Sidebar> + <Logo /> + </Sidebar> + <Content>{children}</Content> + </div> + ) +} + +function Sidebar({ children }: { children: React.ReactNode }) { + return ( + <div + id="sidebar" + style={{ + padding: 20, + flexShrink: 0, + display: 'flex', + flexDirection: 'column', + lineHeight: '1.8em', + borderRight: '2px solid #eee' + }} + > + {children} + </div> + ) +} + +function Content({ children }: { children: React.ReactNode }) { + return ( + <div id="page-container"> + <div + id="page-content" + style={{ + padding: 20, + paddingBottom: 50, + minHeight: '100vh' + }} + > + {children} + </div> + </div> + ) +} + +function Logo() { + return ( + <div + style={{ + marginTop: 20, + marginBottom: 10 + }} + > + <a href="/"> + <img src={logoUrl} height={64} width={64} /> + </a> + </div> + ) +} diff --git a/examples/apollo/layouts/style.css b/examples/apollo/layouts/style.css new file mode 100644 index 00000000..7afa4ca5 --- /dev/null +++ b/examples/apollo/layouts/style.css @@ -0,0 +1,29 @@ +/* Links */ +a { + text-decoration: none; +} +#sidebar a { + padding: 2px 10px; + margin-left: -10px; +} +#sidebar a.is-active { + background-color: #eee; +} + +/* Reset */ +body { + margin: 0; + font-family: sans-serif; +} +* { + box-sizing: border-box; +} + +/* Page Transition Anmiation */ +#page-content { + opacity: 1; + transition: opacity 0.3s ease-in-out; +} +body.page-is-transitioning #page-content { + opacity: 0; +} diff --git a/examples/apollo/package.json b/examples/apollo/package.json new file mode 100644 index 00000000..8da7f0bf --- /dev/null +++ b/examples/apollo/package.json @@ -0,0 +1,23 @@ +{ + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite build && vite preview" + }, + "dependencies": { + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "^5.3.3", + "vike": "^0.4.178", + "vike-react": "^0.4.17", + "vike-react-apollo": "^0.0.1", + "@apollo/client": "^3.10.8", + "@apollo/client-react-streaming": "^0.11.2", + "graphql": "^16.9.0", + "vite": "^5.1.1" + }, + "type": "module" +} diff --git a/examples/apollo/pages/+ApolloClient.ts b/examples/apollo/pages/+ApolloClient.ts new file mode 100644 index 00000000..4d1f5ce5 --- /dev/null +++ b/examples/apollo/pages/+ApolloClient.ts @@ -0,0 +1,9 @@ +import { ApolloClient, InMemoryCache } from '@apollo/client-react-streaming' +import type { PageContext } from 'vike/types' + +// Apollo GraphQL Client with artificial delay: https://gist.github.com/brillout/7d7db0fd6ce55b3b5e8f7ec893eeda01 +export default (pageContext: PageContext) => + new ApolloClient({ + uri: 'https://countries.trevorblades.com', + cache: new InMemoryCache() + }) diff --git a/examples/apollo/pages/+config.ts b/examples/apollo/pages/+config.ts new file mode 100644 index 00000000..4ddcbbb4 --- /dev/null +++ b/examples/apollo/pages/+config.ts @@ -0,0 +1,15 @@ +import type { Config } from 'vike/types' +import Layout from '../layouts/LayoutDefault' +import Head from '../layouts/HeadDefault' +import vikeReact from 'vike-react/config' +import vikeReactApollo from 'vike-react-apollo/config' + +// Default configs (can be overridden by pages) +export default { + Layout, + Head, + // <title> + title: 'My Vike + React App', + extends: [vikeReact, vikeReactApollo], + passToClient: ['routeParams'] +} satisfies Config diff --git a/examples/apollo/pages/_error/+Page.tsx b/examples/apollo/pages/_error/+Page.tsx new file mode 100644 index 00000000..2fa8b1cc --- /dev/null +++ b/examples/apollo/pages/_error/+Page.tsx @@ -0,0 +1,23 @@ +export default Page + +import React from 'react' +import { usePageContext } from 'vike-react/usePageContext' + +function Page() { + const { is404 } = usePageContext() + if (is404) { + return ( + <> + <h1>404 Page Not Found</h1> + <p>This page could not be found.</p> + </> + ) + } else { + return ( + <> + <h1>500 Internal Server Error</h1> + <p>Something went wrong.</p> + </> + ) + } +} diff --git a/examples/apollo/pages/index/+Page.tsx b/examples/apollo/pages/index/+Page.tsx new file mode 100644 index 00000000..56fa7307 --- /dev/null +++ b/examples/apollo/pages/index/+Page.tsx @@ -0,0 +1,26 @@ +export default Page + +import React from 'react' +import { Counter } from './Counter' +import { Countries } from './Countries' + +function Page() { + return ( + <> + <h1>My Vike + React app</h1> + This page is: + <ul> + <li>Rendered to HTML.</li> + <li> + Interactive while loading. <Counter /> + </li> + </ul> + <div> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ullamcorper neque magna, a dapibus turpis + volutpat eget. Praesent et aliquam nisi. Integer congue nec ligula et sollicitudin. + </div> + <br /> + <Countries /> + </> + ) +} diff --git a/examples/apollo/pages/index/Counter.tsx b/examples/apollo/pages/index/Counter.tsx new file mode 100644 index 00000000..70fe5fbb --- /dev/null +++ b/examples/apollo/pages/index/Counter.tsx @@ -0,0 +1,9 @@ +export { Counter } + +import React, { useState } from 'react' + +function Counter() { + const [count, setCount] = useState(0) + + return <button onClick={() => setCount((count) => count + 1)}>Counter {count}</button> +} diff --git a/examples/apollo/pages/index/Countries.tsx b/examples/apollo/pages/index/Countries.tsx new file mode 100644 index 00000000..55bd929a --- /dev/null +++ b/examples/apollo/pages/index/Countries.tsx @@ -0,0 +1,24 @@ +export { Countries } + +import { gql, useSuspenseQuery } from '@apollo/client/index.js' +import React from 'react' +import { withFallback } from 'vike-react-apollo' + +const Countries = withFallback(() => { + const { data } = useSuspenseQuery<{ countries: { code: string; name: string }[] }>(gql` + { + countries { + code + name + } + } + `) + + return ( + <ul> + {data.countries.map((country) => ( + <li key={country.code}>{country.name}</li> + ))} + </ul> + ) +}) diff --git a/examples/apollo/readme.md b/examples/apollo/readme.md new file mode 100644 index 00000000..847f5dd6 --- /dev/null +++ b/examples/apollo/readme.md @@ -0,0 +1,8 @@ +Example of using `vike-react-apollo`. + +```bash +git clone git@github.com:vikejs/vike-react +cd vike-react/examples/apollo/ +npm install +npm run dev +``` diff --git a/examples/apollo/tsconfig.json b/examples/apollo/tsconfig.json new file mode 100644 index 00000000..e0bb64ac --- /dev/null +++ b/examples/apollo/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2020", + "moduleResolution": "Node", + "target": "ES2020", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": ["vite/client"], + "jsx": "react", + "skipLibCheck": true, + "esModuleInterop": true + } +} diff --git a/examples/apollo/vite.config.ts b/examples/apollo/vite.config.ts new file mode 100644 index 00000000..e8b93550 --- /dev/null +++ b/examples/apollo/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import vike from 'vike/plugin' +import { UserConfig } from 'vite' + +export default { + plugins: [react(), vike()] +} satisfies UserConfig diff --git a/package.json b/package.json index d2b63f05..39886fcd 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "pnpm": { "overrides": { "vike-react": "link:./packages/vike-react/", - "vike-react-query": "link:./packages/vike-react-query/" + "vike-react-query": "link:./packages/vike-react-query/", + "vike-react-apollo": "link:./packages/vike-react-apollo/" } }, "devDependencies": { diff --git a/packages/vike-react-apollo/.gitignore b/packages/vike-react-apollo/.gitignore new file mode 100644 index 00000000..b0a5c349 --- /dev/null +++ b/packages/vike-react-apollo/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/dist/ diff --git a/packages/vike-react-apollo/CHANGELOG.md b/packages/vike-react-apollo/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/vike-react-apollo/README.md b/packages/vike-react-apollo/README.md new file mode 100644 index 00000000..853a754d --- /dev/null +++ b/packages/vike-react-apollo/README.md @@ -0,0 +1,219 @@ +<!-- WARNING: keep links absolute in this file so they work on NPM too --> + +[<img src="https://vike.dev/vike-readme.svg" align="right" height="90">](https://vike.dev) +[![npm version](https://img.shields.io/npm/v/vike-react-apollo)](https://www.npmjs.com/package/vike-react-apollo) + +# `vike-react-apollo` + +Enables your React components to fetch data using [Apollo GraphQL](https://www.apollographql.com/docs/react/). + +> [!NOTE] +> You also get [progressive rendering](https://vike.dev/streaming#progressive-rendering), fallback upon loading and/or error, and [caching](https://www.apollographql.com/docs/react/caching/cache-configuration). + +[Installation](#installation) +[Basic usage](#basic-usage) +[`withFallback()`](#withfallback) +[How it works](#how-it-works) +[See also](#see-also) + +<br/> + + +## Installation + +1. `npm install @apollo/client @apollo/client-react-streaming graphql vike-react-apollo` +2. Extend `+config.js`: + ```js + // pages/+config.js + + import vikeReact from 'vike-react/config' + import vikeReactApollo from 'vike-react-apollo/config' + + export default { + // ... + extends: [vikeReact, vikeReactApollo] + } + ``` +3. Create `+ApolloClient.js`: + ```js + // +ApolloClient.js + + import { ApolloClient, InMemoryCache } from '@apollo/client-react-streaming' + + export default (pageContext: PageContext) => + new ApolloClient({ + uri: 'https://countries.trevorblades.com', + cache: new InMemoryCache() + }) + ``` + +> [!NOTE] +> The `vike-react-apollo` extension requires [`vike-react`](https://vike.dev/vike-react). + +<br/> + + +## Basic usage + +```jsx +// Countries.jsx + +import { useSuspenseQuery, gql } from '@apollo/client/index.js' + +const Countries = () => { + const { data } = useSuspenseQuery(gql` + { + countries { + code + name + } + } + `) + + return ( + <ul> + {data.countries.map((country) => ( + <li key={country.code}>{country.name}</li> + ))} + </ul> + ) +} +``` + +> [!NOTE] +> Even though [`useSuspenseQuery()`](https://www.apollographql.com/docs/react/api/react/hooks/#usesuspensequery) is imported from `@apollo/client`, you still need to install `vike-react-apollo` for it to work. + +<br/> + + +## `withFallback()` + +```js +withFallback(Component) // Use default loading fallback (see +Loading) +withFallback(Component, Loading) // Define loading fallback +withFallback(Component, Loading, Error) // Define loading and error fallback +withFallback(Component, undefined, Error) // Define error fallback +``` + +```jsx +// Country.jsx + +import { useSuspenseQuery, gql } from '@apollo/client/index.js' +import { withFallback } from 'vike-react-apollo' + +const Country = withFallback( + ({ code }) => { + const { data } = useSuspenseQuery( + gql` + query Country($code: String!) { + country(code: $code) { + name + } + } + `, + { + variables: { + code + } + } + ) + + return ( + <div> + Name: <b>{data.country.name}</b> + </div> + ) + }, + ({ code }) => <div>Loading country {code}</div>, + // The props `retry` and `error` are provided by vike-react-apollo + // Other props, such as `code`, are provied by the parent component + ({ code, retry, error }) => ( + <div> + Failed to load country {code} + <button onClick={() => retry()}>Retry</button> + </div> + ) +) +``` + +**`+Loading`** + +If you skip the `Loading` parameter, then a default loading component (provided by `vike-react`) is used. You can create a custom default loading component: + +```jsx +// pages/+Loading.jsx + +export default { component: LoadingComponent } + +function LoadingComponent() { + // Applies on a component-level + return <div>Loading...</div> +} +``` + +Instead of adding a loading fallback to the component, you can set a loading fallback to the page and layouts: + +```jsx +// pages/+Loading.jsx + +export default { layout: LoadingLayout } + +function LoadingLayout() { + // Applies to the page and all layouts + return <div>Loading...</div> +} +``` + +> [!NOTE] +> The `+Loading.layout` setting is optional and only relevant when using `useSuspenseQuery()` without `withFallback()` or `withFallback(Component, false)`. +> ```js +> withFallback(Component, false) // Don't set any loading fallback +> withFallback(Component, undefined) // Use default loading fallback +> ``` + +**Manual `<Suspense>` boundary** + +Technically speaking: +- `withFallback()` wraps the component inside a [`<Suspense>` boundary](https://react.dev/reference/react/Suspense). +- `+Loading.layout` adds a `<Suspense>` boundary to the [`<Page>` component](https://vike.dev/Page) as well as to all [`<Layout>` components](https://vike.dev/Layout). + +You can also manually add a `<Suspense>` boundary at any arbitrary position: + +```js +import { Suspense } from 'react' + +function SomePageSection() { + return ( + <Suspense fallback={<div>Loading...</div>}> + <SomeDataFetchingComponent /> + <SomeOtherDataFetchingComponent /> + </Suspense> + ) +} +``` + +<br/> + + +## How it works + +Upon SSR, the component is rendered to HTML and its data loaded on the server-side. On the client side, the component is merely [hydrated](https://vike.dev/hydration). + +Upon page navigation (and rendering the first page if [SSR is disabled](https://vike.dev/ssr)), the component is rendered and its data loaded on the client-side. + +> [!NOTE] +> With `vike-react-apollo` you fetch data on a component-level instead of using Vike's [`data()` hook](https://vike.dev/data) which fetches data on a page-level. + +> [!NOTE] +> Behind the scenes `vike-react-apollo` integrates Apollo GraphQL into [the HTML stream](https://github.com/brillout/react-streaming#readme). + +<br/> + + +## See also + +- [Example](https://github.com/vikejs/vike-react/tree/main/examples/apollo) +- [Vike > Data Fetching](https://vike.dev/data-fetching) +- [Apollo GraphQL > useSuspenseQuery](https://www.apollographql.com/docs/react/api/react/hooks/#usesuspensequery) +- [Apollo GraphQL > Suspense](https://www.apollographql.com/docs/react/data/suspense/) +- [React > `<Suspense>`](https://react.dev/reference/react/Suspense) diff --git a/packages/vike-react-apollo/package.json b/packages/vike-react-apollo/package.json new file mode 100644 index 00000000..e0bd98ae --- /dev/null +++ b/packages/vike-react-apollo/package.json @@ -0,0 +1,63 @@ +{ + "name": "vike-react-apollo", + "version": "0.1.0", + "type": "module", + "main": "dist/src/index.js", + "typings": "dist/src/index.js", + "exports": { + ".": "./dist/src/index.js", + "./config": "./dist/renderer/+config.js", + "./renderer/Wrapper": "./dist/renderer/Wrapper.js" + }, + "scripts": { + "dev": "tsc --watch", + "build": "rimraf dist/ && tsc", + "release": "release-me patch", + "release:minor": "release-me minor", + "release:commit": "release-me commit" + }, + "peerDependencies": { + "@apollo/client": ">=3.0.0", + "graphql": ">=16.0.0", + "@apollo/client-react-streaming": ">=0.11.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0", + "react-streaming": ">=0.3.41", + "vike-react": ">=0.4.13" + }, + "devDependencies": { + "@brillout/release-me": "^0.3.8", + "@apollo/client": "^3.10.8", + "@apollo/client-react-streaming": "^0.11.2", + "graphql": "^16.9.0", + "@types/node": "^20.11.17", + "@types/react": "^18.2.55", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "@types/react-dom": "^18.3.0", + "react-streaming": "^0.3.41", + "rimraf": "^5.0.5", + "typescript": "^5.3.3", + "vike": "^0.4.178", + "vike-react": "^0.4.17", + "vite": "^5.1.1" + }, + "dependencies": { + "react-error-boundary": "^4.0.12" + }, + "typesVersions": { + "*": { + "config": [ + "dist/renderer/+config.d.ts" + ], + "renderer/Wrapper": [ + "dist/renderer/Wrapper.d.ts" + ] + } + }, + "files": [ + "dist" + ], + "repository": "github:vikejs/vike-react", + "license": "MIT" +} diff --git a/packages/vike-react-apollo/renderer/+config.ts b/packages/vike-react-apollo/renderer/+config.ts new file mode 100644 index 00000000..10f2f9f3 --- /dev/null +++ b/packages/vike-react-apollo/renderer/+config.ts @@ -0,0 +1,28 @@ +import type { Config } from 'vike/types' +import type _ from 'vike-react/config' // Needed for declaration merging of Config +import type { ApolloClient } from '@apollo/client-react-streaming' + +export default { + name: 'vike-react-apollo', + require: { + 'vike-react': '>=0.4.13' + }, + Wrapper: 'import:vike-react-apollo/renderer/Wrapper:default', + streamIsRequired: true, + meta: { + ApolloClient: { + env: { + server: true, + client: true + } + } + } +} satisfies Config + +declare global { + namespace Vike { + interface Config { + ApolloClient?: (pageContext: PageContext) => ApolloClient + } + } +} diff --git a/packages/vike-react-apollo/renderer/Transport.tsx b/packages/vike-react-apollo/renderer/Transport.tsx new file mode 100644 index 00000000..76d67ed8 --- /dev/null +++ b/packages/vike-react-apollo/renderer/Transport.tsx @@ -0,0 +1,22 @@ +import { WrapApolloProvider } from '@apollo/client-react-streaming' +import { buildManualDataTransport } from '@apollo/client-react-streaming/manual-transport' +import { useStream } from 'react-streaming' +import { renderToString } from 'react-dom/server' +import React from 'react' + +export const WrappedApolloProvider = WrapApolloProvider( + buildManualDataTransport({ + useInsertHtml() { + const stream = useStream() + if (!stream) { + return () => {} + } + return (callback: () => React.ReactNode) => { + stream.injectToStream( + // https://github.com/apollographql/apollo-client-nextjs/issues/325 + (async () => renderToString(await callback()))() + ) + } + } + }) +) diff --git a/packages/vike-react-apollo/renderer/Wrapper.tsx b/packages/vike-react-apollo/renderer/Wrapper.tsx new file mode 100644 index 00000000..1a017c31 --- /dev/null +++ b/packages/vike-react-apollo/renderer/Wrapper.tsx @@ -0,0 +1,11 @@ +import React, { type ReactNode } from 'react' +import { usePageContext } from 'vike-react/usePageContext' +import { assertUsage } from '../utils/assert.js' +import { WrappedApolloProvider } from './Transport.js' + +export default function Wrapper({ children }: { children: ReactNode }) { + const pageContext = usePageContext() + const { ApolloClient: getApolloClient } = pageContext.config + assertUsage(getApolloClient, 'Setting +ApolloClient is required') + return <WrappedApolloProvider makeClient={() => getApolloClient(pageContext)}>{children}</WrappedApolloProvider> +} diff --git a/packages/vike-react-apollo/src/index.ts b/packages/vike-react-apollo/src/index.ts new file mode 100644 index 00000000..5ce99bc8 --- /dev/null +++ b/packages/vike-react-apollo/src/index.ts @@ -0,0 +1 @@ +export { withFallback } from './withFallback.js' diff --git a/packages/vike-react-apollo/src/withFallback.tsx b/packages/vike-react-apollo/src/withFallback.tsx new file mode 100644 index 00000000..3dec2178 --- /dev/null +++ b/packages/vike-react-apollo/src/withFallback.tsx @@ -0,0 +1,134 @@ +export { withFallback } + +import React, { type ComponentType, type ReactNode, Suspense } from 'react' +import { ErrorBoundary, FallbackProps } from 'react-error-boundary' +import { ApolloConsumer } from '@apollo/client/index.js' +import { usePageContext } from 'vike-react/usePageContext' +type RetryOptions = { retryQuery?: boolean } +type RetryFn = (options?: RetryOptions) => void + +type ErrorFallbackProps = { + error: { message: string } & Record<string, unknown> + retry: RetryFn +} + +type Loading<T> = ComponentType<T> | ReactNode | false +type Error<T> = ComponentType<T & ErrorFallbackProps> | ReactNode + +type WithFallbackOptions<T> = { + Loading?: Loading<T> + Error?: Error<T> +} + +function withFallback<T extends object = Record<string, never>>( + Component: ComponentType<T>, + options?: WithFallbackOptions<T> +): ComponentType<T> +function withFallback<T extends object = Record<string, never>>( + Component: ComponentType<T>, + Loading?: Loading<T>, + Error?: Error<T> +): ComponentType<T> +function withFallback<T extends object = Record<string, never>>( + Component: ComponentType<T>, + options?: Loading<T> | WithFallbackOptions<T>, + Error_?: Error<T> +): ComponentType<T> { + let Loading: Loading<T> + let Error: Error<T> + + if (options && typeof options === 'object' && ('Loading' in options || 'Error' in options)) { + Loading = options.Loading + Error = options.Error + } else if (typeof options !== 'object') { + Loading = options + Error = Error_ + } + + const ComponentWithFallback = (componentProps: T) => { + const pageContext = usePageContext() + + let element = <Component {...componentProps} /> + + if (Error) { + element = ( + <ApolloConsumer> + {({ reFetchObservableQueries }) => { + const createRetry = + (resetErrorBoundary: FallbackProps['resetErrorBoundary']): RetryFn => + (options = {}) => { + const { retryQuery = true } = options + if (retryQuery) { + reFetchObservableQueries() + } + resetErrorBoundary() + } + const createError = (originalError: FallbackProps['error']) => { + const message = getErrorMessage(originalError) + const error = { message } + if (typeof originalError === 'object') { + Object.assign(error, originalError) + for (const key of ['name', 'stack', 'cause']) { + if (key in originalError) { + Object.assign(error, { [key]: originalError[key] }) + } + } + } + return error + } + + return ( + <ErrorBoundary + fallbackRender={({ error: originalError, resetErrorBoundary }) => + typeof Error === 'function' ? ( + <Error + {...componentProps} + retry={createRetry(resetErrorBoundary)} + error={createError(originalError)} + /> + ) : ( + Error + ) + } + > + {element} + </ErrorBoundary> + ) + }} + </ApolloConsumer> + ) + } + + if (Loading === undefined && pageContext.config.Loading?.component) { + Loading = pageContext.config.Loading.component + } + if (Loading !== false) { + element = ( + <Suspense fallback={typeof Loading === 'function' ? <Loading {...componentProps} /> : Loading}> + {element} + </Suspense> + ) + } + + return element + } + + ComponentWithFallback.displayName = `withFallback(${Component.displayName || Component.name})` + return ComponentWithFallback +} + +function getErrorMessage(error: unknown) { + if (error && error instanceof Error) { + return error.message + } + + if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') { + return error.message + } + + if (typeof error === 'string') { + return error + } + + return 'Unknown error' +} diff --git a/packages/vike-react-apollo/tsconfig.json b/packages/vike-react-apollo/tsconfig.json new file mode 100644 index 00000000..714277ff --- /dev/null +++ b/packages/vike-react-apollo/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + // Resolution + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + // Libs + "lib": ["ES2021", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + // Strictness + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitAny": true, + // Output + "declaration": true, + "noEmitOnError": false, + "rootDir": "./", + // Misc + "esModuleInterop": true, + "skipLibCheck": true, + "jsx": "react" + } +} diff --git a/packages/vike-react-apollo/utils/assert.ts b/packages/vike-react-apollo/utils/assert.ts new file mode 100644 index 00000000..18647dc9 --- /dev/null +++ b/packages/vike-react-apollo/utils/assert.ts @@ -0,0 +1,6 @@ +export { assertUsage } + +function assertUsage(condition: unknown, message: string): asserts condition { + if (condition) return + throw new Error('Wrong usage: ' + message) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f66900a..c06ad681 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ settings: overrides: vike-react: link:./packages/vike-react/ vike-react-query: link:./packages/vike-react-query/ + vike-react-apollo: link:./packages/vike-react-apollo/ importers: @@ -28,6 +29,48 @@ importers: specifier: ^3.2.5 version: 3.2.5 + examples/apollo: + dependencies: + '@apollo/client': + specifier: ^3.10.8 + version: 3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@apollo/client-react-streaming': + specifier: ^0.11.2 + version: 0.11.2(@apollo/client@3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) + '@types/react': + specifier: ^18.2.55 + version: 18.2.55 + '@types/react-dom': + specifier: ^18.2.19 + version: 18.3.0 + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.2.1(vite@5.1.1(@types/node@20.11.17)) + graphql: + specifier: ^16.9.0 + version: 16.9.0 + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + typescript: + specifier: ^5.3.3 + version: 5.5.3 + vike: + specifier: ^0.4.178 + version: 0.4.178(react-streaming@0.3.42(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(vite@5.1.1(@types/node@20.11.17)) + vike-react: + specifier: link:../../packages/vike-react + version: link:../../packages/vike-react + vike-react-apollo: + specifier: link:../../packages/vike-react-apollo + version: link:../../packages/vike-react-apollo + vite: + specifier: ^5.1.1 + version: 5.1.1(@types/node@20.11.17) + examples/full: dependencies: '@types/react': @@ -161,6 +204,58 @@ importers: specifier: ^0.4.178 version: 0.4.178(react-streaming@0.3.42(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(vite@5.1.1(@types/node@20.11.17)) + packages/vike-react-apollo: + dependencies: + react-error-boundary: + specifier: ^4.0.12 + version: 4.0.12(react@18.2.0) + devDependencies: + '@apollo/client': + specifier: ^3.10.8 + version: 3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@apollo/client-react-streaming': + specifier: ^0.11.2 + version: 0.11.2(@apollo/client@3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) + '@brillout/release-me': + specifier: ^0.3.8 + version: 0.3.8 + '@types/node': + specifier: ^20.11.17 + version: 20.11.17 + '@types/react': + specifier: ^18.2.55 + version: 18.2.55 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.0 + graphql: + specifier: ^16.9.0 + version: 16.9.0 + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-streaming: + specifier: ^0.3.41 + version: 0.3.42(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + rimraf: + specifier: ^5.0.5 + version: 5.0.5 + typescript: + specifier: ^5.3.3 + version: 5.5.3 + vike: + specifier: ^0.4.178 + version: 0.4.178(react-streaming@0.3.42(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(vite@5.1.1(@types/node@20.11.17)) + vike-react: + specifier: link:../vike-react + version: link:../vike-react + vite: + specifier: ^5.1.1 + version: 5.1.1(@types/node@20.11.17) + packages/vike-react-query: dependencies: devalue: @@ -219,6 +314,30 @@ packages: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} + '@apollo/client-react-streaming@0.11.2': + resolution: {integrity: sha512-rRA/dIA09/Y6+jtGGBnXHQfPOv6BYYVZwQP8OzQtWrWbSgDEI6uAhqULssU5f0ZhQJVzKDuslqGE9QAX0gdfRQ==} + peerDependencies: + '@apollo/client': ^3.10.4 + react: ^18 + + '@apollo/client@3.10.8': + resolution: {integrity: sha512-UaaFEitRrPRWV836wY2L7bd3HRCfbMie1jlYMcmazFAK23MVhz/Uq7VG1nwbotPb5xzFsw5RF4Wnp2G3dWPM3g==} + peerDependencies: + graphql: ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + '@babel/code-frame@7.23.5': resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} @@ -700,6 +819,11 @@ packages: cpu: [x64] os: [win32] + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@hutson/parse-repository-url@5.0.0': resolution: {integrity: sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==} engines: {node: '>=10.13.0'} @@ -866,6 +990,9 @@ packages: '@types/react-dom@18.2.19': resolution: {integrity: sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==} + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react@18.2.55': resolution: {integrity: sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==} @@ -893,6 +1020,26 @@ packages: '@vitest/utils@1.2.2': resolution: {integrity: sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==} + '@wry/caches@1.0.1': + resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} + engines: {node: '>=8'} + + '@wry/context@0.7.4': + resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} + engines: {node: '>=8'} + + '@wry/equality@0.5.7': + resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} + engines: {node: '>=8'} + + '@wry/trie@0.4.3': + resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==} + engines: {node: '>=8'} + + '@wry/trie@0.5.0': + resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} + engines: {node: '>=8'} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -1305,6 +1452,16 @@ packages: gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + graphql-tag@2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -1340,6 +1497,9 @@ packages: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} engines: {node: '>= 0.4'} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hosted-git-info@7.0.1: resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==} engines: {node: ^16.14.0 || >=18.0.0} @@ -1653,6 +1813,10 @@ packages: nwsapi@2.2.7: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} @@ -1676,6 +1840,9 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + optimism@0.18.0: + resolution: {integrity: sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==} + p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1772,6 +1939,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -1795,6 +1965,9 @@ packages: peerDependencies: react: '>=16.13.1' + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -1830,9 +2003,24 @@ packages: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} + rehackt@0.1.0: + resolution: {integrity: sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==} + peerDependencies: + '@types/react': '*' + react: '*' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + response-iterator@0.2.6: + resolution: {integrity: sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==} + engines: {node: '>=0.8'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1982,6 +2170,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -2026,6 +2218,13 @@ packages: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} + ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -2229,6 +2428,12 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + zen-observable-ts@1.2.5: + resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} + + zen-observable@0.8.15: + resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + snapshots: '@ampproject/remapping@2.2.1': @@ -2236,6 +2441,35 @@ snapshots: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 + '@apollo/client-react-streaming@0.11.2(@apollo/client@3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': + dependencies: + '@apollo/client': 3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + ts-invariant: 0.10.3 + + '@apollo/client@3.10.8(@types/react@18.2.55)(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@wry/caches': 1.0.1 + '@wry/equality': 0.5.7 + '@wry/trie': 0.5.0 + graphql: 16.9.0 + graphql-tag: 2.12.6(graphql@16.9.0) + hoist-non-react-statics: 3.3.2 + optimism: 0.18.0 + prop-types: 15.8.1 + rehackt: 0.1.0(@types/react@18.2.55)(react@18.2.0) + response-iterator: 0.2.6 + symbol-observable: 4.0.0 + ts-invariant: 0.10.3 + tslib: 2.6.3 + zen-observable-ts: 1.2.5 + optionalDependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + '@babel/code-frame@7.23.5': dependencies: '@babel/highlight': 7.23.4 @@ -2608,6 +2842,10 @@ snapshots: '@esbuild/win32-x64@0.19.10': optional: true + '@graphql-typed-document-node/core@3.2.0(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + '@hutson/parse-repository-url@5.0.0': {} '@isaacs/cliui@8.0.2': @@ -2720,7 +2958,7 @@ snapshots: dependencies: '@babel/runtime': 7.23.5 '@testing-library/dom': 9.3.3 - '@types/react-dom': 18.2.19 + '@types/react-dom': 18.3.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -2761,6 +2999,10 @@ snapshots: dependencies: '@types/react': 18.2.55 + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 18.2.55 + '@types/react@18.2.55': dependencies: '@types/prop-types': 15.7.10 @@ -2809,6 +3051,26 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 + '@wry/caches@1.0.1': + dependencies: + tslib: 2.6.3 + + '@wry/context@0.7.4': + dependencies: + tslib: 2.6.3 + + '@wry/equality@0.5.7': + dependencies: + tslib: 2.6.3 + + '@wry/trie@0.4.3': + dependencies: + tslib: 2.6.3 + + '@wry/trie@0.5.0': + dependencies: + tslib: 2.6.3 + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -3295,6 +3557,13 @@ snapshots: dependencies: get-intrinsic: 1.2.2 + graphql-tag@2.12.6(graphql@16.9.0): + dependencies: + graphql: 16.9.0 + tslib: 2.6.3 + + graphql@16.9.0: {} + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -3326,6 +3595,10 @@ snapshots: dependencies: function-bind: 1.1.2 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hosted-git-info@7.0.1: dependencies: lru-cache: 10.2.2 @@ -3616,6 +3889,8 @@ snapshots: nwsapi@2.2.7: {} + object-assign@4.1.1: {} + object-inspect@1.13.1: {} object-is@1.1.5: @@ -3640,6 +3915,13 @@ snapshots: dependencies: mimic-fn: 4.0.0 + optimism@0.18.0: + dependencies: + '@wry/caches': 1.0.1 + '@wry/context': 0.7.4 + '@wry/trie': 0.4.3 + tslib: 2.6.3 + p-limit@4.0.0: dependencies: yocto-queue: 1.0.0 @@ -3729,6 +4011,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.2.0 + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + psl@1.9.0: {} punycode@2.3.1: {} @@ -3748,6 +4036,8 @@ snapshots: '@babel/runtime': 7.23.5 react: 18.2.0 + react-is@16.13.1: {} + react-is@17.0.2: {} react-is@18.2.0: {} @@ -3788,8 +4078,15 @@ snapshots: define-properties: 1.2.1 set-function-name: 2.0.1 + rehackt@0.1.0(@types/react@18.2.55)(react@18.2.0): + optionalDependencies: + '@types/react': 18.2.55 + react: 18.2.0 + requires-port@1.0.0: {} + response-iterator@0.2.6: {} + reusify@1.0.4: {} rimraf@5.0.5: @@ -3943,6 +4240,8 @@ snapshots: dependencies: has-flag: 4.0.0 + symbol-observable@4.0.0: {} + symbol-tree@3.2.4: {} text-extensions@2.4.0: {} @@ -3976,6 +4275,12 @@ snapshots: dependencies: punycode: 2.3.1 + ts-invariant@0.10.3: + dependencies: + tslib: 2.6.3 + + tslib@2.6.3: {} + type-detect@4.0.8: {} type-fest@3.13.1: {} @@ -4172,3 +4477,9 @@ snapshots: yallist@4.0.0: {} yocto-queue@1.0.0: {} + + zen-observable-ts@1.2.5: + dependencies: + zen-observable: 0.8.15 + + zen-observable@0.8.15: {}