```
## Parameters
@@ -39,7 +39,7 @@ args
-BuildArgs
+ManualDataTransportOptions
|
diff --git a/docs/client-react-streaming.queryevent.md b/docs/client-react-streaming.queryevent.md
index a8160b0a..087ef7a4 100644
--- a/docs/client-react-streaming.queryevent.md
+++ b/docs/client-react-streaming.queryevent.md
@@ -11,7 +11,9 @@ Events that will be emitted by a wrapped ApolloClient instance during SSR on `Da
```typescript
type QueryEvent = {
type: "started";
- options: WatchQueryOptions;
+ options: {
+ query: string;
+ } & Omit;
id: TransportIdentifier;
} | {
type: "data";
diff --git a/docs/experimental-nextjs-app-support.apolloclient.md b/docs/experimental-nextjs-app-support.apolloclient.md
new file mode 100644
index 00000000..eba6300f
--- /dev/null
+++ b/docs/experimental-nextjs-app-support.apolloclient.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [ApolloClient](./experimental-nextjs-app-support.apolloclient.md)
+
+## ApolloClient class
+
+A version of `ApolloClient` to be used with streaming SSR or in React Server Components.
+
+For more documentation, please see [the Apollo Client API documentation](https://www.apollographql.com/docs/react/api/core/ApolloClient).
+
+**Signature:**
+
+```typescript
+declare class ApolloClient extends ApolloClient$1
+```
+**Extends:** ApolloClient$1<TCacheShape>
+
diff --git a/docs/experimental-nextjs-app-support.apollonextappprovider.md b/docs/experimental-nextjs-app-support.apollonextappprovider.md
index 742266ee..5ff06781 100644
--- a/docs/experimental-nextjs-app-support.apollonextappprovider.md
+++ b/docs/experimental-nextjs-app-support.apollonextappprovider.md
@@ -10,7 +10,7 @@ A version of `ApolloProvider` to be used with the Next.js App Router.
As opposed to the normal `ApolloProvider`, this version does not require a `client` prop, but requires a `makeClient` prop instead.
-Use this component together with `NextSSRApolloClient` and `NextSSRInMemoryCache` to make an ApolloClient instance available to your Client Component hooks in the Next.js App Router.
+Use this component together with `ApolloClient` and `InMemoryCache` from the `@apollo/experimental-nextjs-app-support` package to make an ApolloClient instance available to your Client Component hooks in the Next.js App Router.
**Signature:**
@@ -23,13 +23,16 @@ ApolloNextAppProvider: _apollo_client_react_streaming.WrappedApolloProvider<_apo
`app/ApolloWrapper.jsx`
```tsx
+import { HttpLink } from "@apollo/client";
+import { ApolloNextAppProvider, ApolloClient, InMemoryCache } from "@apollo/experimental-nextjs-app-support";
+
function makeClient() {
const httpLink = new HttpLink({
uri: "https://example.com/api/graphql",
});
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link: httpLink,
});
}
diff --git a/docs/experimental-nextjs-app-support.nextssrinmemorycache.md b/docs/experimental-nextjs-app-support.inmemorycache.md
similarity index 73%
rename from docs/experimental-nextjs-app-support.nextssrinmemorycache.md
rename to docs/experimental-nextjs-app-support.inmemorycache.md
index c28416f3..92e4ab51 100644
--- a/docs/experimental-nextjs-app-support.nextssrinmemorycache.md
+++ b/docs/experimental-nextjs-app-support.inmemorycache.md
@@ -1,8 +1,8 @@
-[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [NextSSRInMemoryCache](./experimental-nextjs-app-support.nextssrinmemorycache.md)
+[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [InMemoryCache](./experimental-nextjs-app-support.inmemorycache.md)
-## NextSSRInMemoryCache class
+## InMemoryCache class
A version of `InMemoryCache` to be used with streaming SSR.
diff --git a/docs/experimental-nextjs-app-support.md b/docs/experimental-nextjs-app-support.md
index 2436c8d2..9a5df2fa 100644
--- a/docs/experimental-nextjs-app-support.md
+++ b/docs/experimental-nextjs-app-support.md
@@ -20,35 +20,35 @@ Description
-[DebounceMultipartResponsesLink](./experimental-nextjs-app-support.debouncemultipartresponseslink.md)
+[ApolloClient](./experimental-nextjs-app-support.apolloclient.md)
|
-This link can be used to "debounce" the initial response of a multipart request. Any incremental data received during the `cutoffDelay` time will be merged into the initial response.
-
-After `cutoffDelay`, the link will return the initial response, even if there is still incremental data pending, and close the network connection.
+A version of `ApolloClient` to be used with streaming SSR or in React Server Components.
-If `cutoffDelay` is `0`, the link will immediately return data as soon as it is received, without waiting for incremental data, and immediately close the network connection.
+For more documentation, please see [the Apollo Client API documentation](https://www.apollographql.com/docs/react/api/core/ApolloClient).
|
-[NextSSRApolloClient](./experimental-nextjs-app-support.nextssrapolloclient.md)
+[DebounceMultipartResponsesLink](./experimental-nextjs-app-support.debouncemultipartresponseslink.md)
|
-A version of `ApolloClient` to be used with streaming SSR.
+This link can be used to "debounce" the initial response of a multipart request. Any incremental data received during the `cutoffDelay` time will be merged into the initial response.
-For more documentation, please see [the Apollo Client API documentation](https://www.apollographql.com/docs/react/api/core/ApolloClient).
+After `cutoffDelay`, the link will return the initial response, even if there is still incremental data pending, and close the network connection.
+
+If `cutoffDelay` is `0`, the link will immediately return data as soon as it is received, without waiting for incremental data, and immediately close the network connection.
|
-[NextSSRInMemoryCache](./experimental-nextjs-app-support.nextssrinmemorycache.md)
+[InMemoryCache](./experimental-nextjs-app-support.inmemorycache.md)
|
@@ -139,7 +139,7 @@ Ensures that you can always access the same instance of ApolloClient during RSC
|
-[resetNextSSRApolloSingletons()](./experimental-nextjs-app-support.resetnextssrapollosingletons.md)
+[resetApolloClientSingletons()](./experimental-nextjs-app-support.resetapolloclientsingletons.md)
|
@@ -184,7 +184,7 @@ A version of `ApolloProvider` to be used with the Next.js App Router.
As opposed to the normal `ApolloProvider`, this version does not require a `client` prop, but requires a `makeClient` prop instead.
-Use this component together with `NextSSRApolloClient` and `NextSSRInMemoryCache` to make an ApolloClient instance available to your Client Component hooks in the Next.js App Router.
+Use this component together with `ApolloClient` and `InMemoryCache` from the `@apollo/experimental-nextjs-app-support` package to make an ApolloClient instance available to your Client Component hooks in the Next.js App Router.
|
diff --git a/docs/experimental-nextjs-app-support.nextssrapolloclient.md b/docs/experimental-nextjs-app-support.nextssrapolloclient.md
deleted file mode 100644
index 945aad84..00000000
--- a/docs/experimental-nextjs-app-support.nextssrapolloclient.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [NextSSRApolloClient](./experimental-nextjs-app-support.nextssrapolloclient.md)
-
-## NextSSRApolloClient class
-
-A version of `ApolloClient` to be used with streaming SSR.
-
-For more documentation, please see [the Apollo Client API documentation](https://www.apollographql.com/docs/react/api/core/ApolloClient).
-
-**Signature:**
-
-```typescript
-declare class NextSSRApolloClient extends ApolloClient
-```
-**Extends:** ApolloClient<TCacheShape>
-
diff --git a/docs/experimental-nextjs-app-support.resetnextssrapollosingletons.md b/docs/experimental-nextjs-app-support.resetapolloclientsingletons.md
similarity index 70%
rename from docs/experimental-nextjs-app-support.resetnextssrapollosingletons.md
rename to docs/experimental-nextjs-app-support.resetapolloclientsingletons.md
index d896cf53..3ac3176e 100644
--- a/docs/experimental-nextjs-app-support.resetnextssrapollosingletons.md
+++ b/docs/experimental-nextjs-app-support.resetapolloclientsingletons.md
@@ -1,8 +1,8 @@
-[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [resetNextSSRApolloSingletons](./experimental-nextjs-app-support.resetnextssrapollosingletons.md)
+[Home](./index.md) > [@apollo/experimental-nextjs-app-support](./experimental-nextjs-app-support.md) > [resetApolloClientSingletons](./experimental-nextjs-app-support.resetapolloclientsingletons.md)
-## resetNextSSRApolloSingletons() function
+## resetApolloClientSingletons() function
> This export is only available in React Client Components
diff --git a/examples/app-dir-experiments/app/ssr/ApolloWrapper.tsx b/examples/app-dir-experiments/app/ssr/ApolloWrapper.tsx
index 204037a0..e7003ecf 100644
--- a/examples/app-dir-experiments/app/ssr/ApolloWrapper.tsx
+++ b/examples/app-dir-experiments/app/ssr/ApolloWrapper.tsx
@@ -3,10 +3,10 @@
import { ApolloLink, HttpLink } from "@apollo/client";
import {
ApolloNextAppProvider,
- NextSSRInMemoryCache,
- NextSSRApolloClient,
+ InMemoryCache,
+ ApolloClient,
SSRMultipartLink,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+} from "@apollo/experimental-nextjs-app-support";
import { setVerbosity } from "ts-invariant";
setVerbosity("debug");
@@ -17,8 +17,8 @@ function makeClient() {
fetchOptions: { cache: "no-store" },
});
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link:
typeof window === "undefined"
? ApolloLink.from([
diff --git a/examples/app-dir-experiments/app/ssr/page.tsx b/examples/app-dir-experiments/app/ssr/page.tsx
index 372aec94..5b3ef26f 100644
--- a/examples/app-dir-experiments/app/ssr/page.tsx
+++ b/examples/app-dir-experiments/app/ssr/page.tsx
@@ -1,10 +1,6 @@
"use client";
import React, { Suspense } from "react";
-import {
- useFragment,
- useQuery,
- useSuspenseQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+import { useFragment, useQuery, useSuspenseQuery } from "@apollo/client";
import { gql } from "@apollo/client";
import { HtmlChangesObserver } from "@/components/HtmlChangesObserver";
diff --git a/examples/hack-the-supergraph-ssr/app/ApolloWrapper.tsx b/examples/hack-the-supergraph-ssr/app/ApolloWrapper.tsx
index 7f115ba9..4167895e 100644
--- a/examples/hack-the-supergraph-ssr/app/ApolloWrapper.tsx
+++ b/examples/hack-the-supergraph-ssr/app/ApolloWrapper.tsx
@@ -4,10 +4,10 @@ import { ApolloLink, HttpLink } from "@apollo/client";
import clientCookies from "js-cookie";
import {
ApolloNextAppProvider,
- NextSSRInMemoryCache,
- NextSSRApolloClient,
+ InMemoryCache,
+ ApolloClient,
SSRMultipartLink,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+} from "@apollo/experimental-nextjs-app-support";
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
import { setVerbosity } from "ts-invariant";
@@ -68,8 +68,8 @@ export function ApolloWrapper({
])
: ApolloLink.from([delayLink, httpLink]);
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link,
});
}
diff --git a/examples/hack-the-supergraph-ssr/app/page.tsx b/examples/hack-the-supergraph-ssr/app/page.tsx
index 1b3ede29..0a4dd36f 100644
--- a/examples/hack-the-supergraph-ssr/app/page.tsx
+++ b/examples/hack-the-supergraph-ssr/app/page.tsx
@@ -2,8 +2,7 @@
import ProductCard from "../components/ProductCard";
import { Heading, SimpleGrid, Stack, Text, VStack } from "@chakra-ui/react";
-import { gql, TypedDocumentNode } from "@apollo/client";
-import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
+import { useSuspenseQuery, gql, TypedDocumentNode } from "@apollo/client";
const GET_LATEST_PRODUCTS: TypedDocumentNode<{
products: { id: string }[];
diff --git a/examples/hack-the-supergraph-ssr/app/product/[id]/page.tsx b/examples/hack-the-supergraph-ssr/app/product/[id]/page.tsx
index 81971bd4..516c9077 100644
--- a/examples/hack-the-supergraph-ssr/app/product/[id]/page.tsx
+++ b/examples/hack-the-supergraph-ssr/app/product/[id]/page.tsx
@@ -15,7 +15,7 @@ import {
Text,
} from "@chakra-ui/react";
import { gql, TypedDocumentNode } from "@apollo/client";
-import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
+import { useSuspenseQuery } from "@apollo/client";
const GET_PRODUCT_DETAILS: TypedDocumentNode<{
product: {
diff --git a/examples/hack-the-supergraph-ssr/components/ProductCard.tsx b/examples/hack-the-supergraph-ssr/components/ProductCard.tsx
index a5f002b7..06650aea 100644
--- a/examples/hack-the-supergraph-ssr/components/ProductCard.tsx
+++ b/examples/hack-the-supergraph-ssr/components/ProductCard.tsx
@@ -9,8 +9,7 @@ import {
usePrefersReducedMotion,
} from "@chakra-ui/react";
import Link from "next/link";
-import { TypedDocumentNode, gql } from "@apollo/client";
-import { useFragment } from "@apollo/experimental-nextjs-app-support/ssr";
+import { useFragment, TypedDocumentNode, gql } from "@apollo/client";
const ProductCardProductFragment: TypedDocumentNode<{
id: string;
diff --git a/examples/polls-demo/app/cc/apollo-wrapper.tsx b/examples/polls-demo/app/cc/apollo-wrapper.tsx
index b92ac869..f9b3e39a 100644
--- a/examples/polls-demo/app/cc/apollo-wrapper.tsx
+++ b/examples/polls-demo/app/cc/apollo-wrapper.tsx
@@ -2,11 +2,11 @@
import { ApolloLink, HttpLink } from "@apollo/client";
import {
- NextSSRApolloClient,
+ ApolloClient,
ApolloNextAppProvider,
- NextSSRInMemoryCache,
+ InMemoryCache,
SSRMultipartLink,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+} from "@apollo/experimental-nextjs-app-support";
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
import { setVerbosity } from "ts-invariant";
@@ -21,8 +21,8 @@ function makeClient() {
uri: "https://apollo-next-poll.up.railway.app/",
});
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link:
typeof window === "undefined"
? ApolloLink.from([
diff --git a/examples/polls-demo/app/cc/poll-cc.tsx b/examples/polls-demo/app/cc/poll-cc.tsx
index 73f6a4bc..d5897e4a 100644
--- a/examples/polls-demo/app/cc/poll-cc.tsx
+++ b/examples/polls-demo/app/cc/poll-cc.tsx
@@ -1,10 +1,6 @@
"use client";
import { Suspense } from "react";
-import {
- useReadQuery,
- useBackgroundQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
-import { useMutation } from "@apollo/client";
+import { useReadQuery, useBackgroundQuery, useMutation } from "@apollo/client";
import { QueryReference } from "@apollo/client/react";
import { Poll as PollInner } from "@/components/poll";
diff --git a/integration-test/jest/src/App.jsx b/integration-test/jest/src/App.jsx
index 94e116a4..390f473b 100644
--- a/integration-test/jest/src/App.jsx
+++ b/integration-test/jest/src/App.jsx
@@ -1,12 +1,16 @@
import { Suspense, useState } from "react";
import {
ApolloNextAppProvider,
- NextSSRApolloClient,
- NextSSRInMemoryCache,
- useSuspenseQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+ ApolloClient,
+ InMemoryCache,
+} from "@apollo/experimental-nextjs-app-support";
import { SchemaLink } from "@apollo/client/link/schema/index.js";
-import { gql, ApolloLink, Observable } from "@apollo/client/index.js";
+import {
+ useSuspenseQuery,
+ gql,
+ ApolloLink,
+ Observable,
+} from "@apollo/client/index.js";
import { schema } from "./schema";
const delayLink = new ApolloLink((operation, forward) => {
@@ -22,8 +26,8 @@ const delayLink = new ApolloLink((operation, forward) => {
});
export const makeClient = () => {
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link: delayLink.concat(new SchemaLink({ schema })),
});
};
diff --git a/integration-test/jest/src/App.test.jsx b/integration-test/jest/src/App.test.jsx
index c99bb53e..fd73b4bf 100644
--- a/integration-test/jest/src/App.test.jsx
+++ b/integration-test/jest/src/App.test.jsx
@@ -3,9 +3,9 @@ import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import App from "./App";
-import { resetNextSSRApolloSingletons } from "@apollo/experimental-nextjs-app-support/ssr";
+import { resetApolloClientSingletons } from "@apollo/experimental-nextjs-app-support";
-afterEach(resetNextSSRApolloSingletons);
+afterEach(resetApolloClientSingletons);
test("loads data", async () => {
render();
diff --git a/integration-test/jest/src/hooks.test.jsx b/integration-test/jest/src/hooks.test.jsx
index c16a8f98..11947f5a 100644
--- a/integration-test/jest/src/hooks.test.jsx
+++ b/integration-test/jest/src/hooks.test.jsx
@@ -4,11 +4,11 @@ import "@testing-library/jest-dom";
import { makeClient, QUERY } from "./App";
import {
ApolloNextAppProvider,
- NextSSRApolloClient,
- useQuery,
- resetNextSSRApolloSingletons,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+ ApolloClient,
+ resetApolloClientSingletons,
+} from "@apollo/experimental-nextjs-app-support";
import { Suspense } from "react";
+import { useQuery } from "@apollo/client";
const wrapper = ({ children }) => (
@@ -16,7 +16,7 @@ const wrapper = ({ children }) => (
);
-afterEach(resetNextSSRApolloSingletons);
+afterEach(resetApolloClientSingletons);
/**
* We test that jest is using the "browser" build.
@@ -25,7 +25,7 @@ afterEach(resetNextSSRApolloSingletons);
*/
test("uses the browser build", () => {
let foundPrototype = false;
- let proto = NextSSRApolloClient;
+ let proto = ApolloClient;
while (proto) {
if (proto.name === "ApolloClientBrowserImpl") {
foundPrototype = true;
@@ -66,11 +66,11 @@ test("will set up the data transport", () => {
expect(globalThis[Symbol.for("ApolloClientSingleton")]).toBeDefined();
});
-test("resetNextSSRApolloSingletons tears down global singletons", () => {
+test("resetApolloClientSingletons tears down global singletons", () => {
render(<>>, { wrapper });
// wrappers are now set up, see last test
// usually, we do this in `afterEach`
- resetNextSSRApolloSingletons();
+ resetApolloClientSingletons();
expect(globalThis[Symbol.for("ApolloSSRDataTransport")]).not.toBeDefined();
expect(globalThis[Symbol.for("ApolloClientSingleton")]).not.toBeDefined();
});
diff --git a/integration-test/nextjs/src/app/cc/ApolloWrapper.tsx b/integration-test/nextjs/src/app/cc/ApolloWrapper.tsx
index f3022dcd..76f61b17 100644
--- a/integration-test/nextjs/src/app/cc/ApolloWrapper.tsx
+++ b/integration-test/nextjs/src/app/cc/ApolloWrapper.tsx
@@ -3,9 +3,9 @@ import React from "react";
import { HttpLink } from "@apollo/client";
import {
ApolloNextAppProvider,
- NextSSRInMemoryCache,
- NextSSRApolloClient,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+ InMemoryCache,
+ ApolloClient,
+} from "@apollo/experimental-nextjs-app-support";
import { SchemaLink } from "@apollo/client/link/schema";
@@ -42,8 +42,8 @@ export function ApolloWrapper({
uri: "/graphql",
});
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link: delayLink
.concat(errorLink)
.concat(
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQuery/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQuery/page.tsx
index ea1d336e..74f8ed5d 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQuery/page.tsx
@@ -1,11 +1,12 @@
"use client";
+import type { TypedDocumentNode } from "@apollo/client";
import {
useBackgroundQuery,
useReadQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
-import type { TypedDocumentNode } from "@apollo/client";
-import { gql, QueryReference } from "@apollo/client";
+ gql,
+ QueryReference,
+} from "@apollo/client";
import { Suspense } from "react";
interface Data {
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQueryWithoutSsrReadQuery/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQueryWithoutSsrReadQuery/page.tsx
index 76c4326e..a2cbb020 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQueryWithoutSsrReadQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useBackgroundQueryWithoutSsrReadQuery/page.tsx
@@ -1,9 +1,6 @@
"use client";
-import {
- useBackgroundQuery,
- useReadQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+import { useBackgroundQuery, useReadQuery } from "@apollo/client";
import type { TypedDocumentNode } from "@apollo/client";
import { gql, QueryReference } from "@apollo/client";
import { Suspense, useState, useEffect } from "react";
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useQuery/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useQuery/page.tsx
index d3f34c52..f150e009 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useQuery/page.tsx
@@ -1,8 +1,7 @@
"use client";
-import { useQuery } from "@apollo/experimental-nextjs-app-support/ssr";
import type { TypedDocumentNode } from "@apollo/client";
-import { gql } from "@apollo/client";
+import { useQuery, gql } from "@apollo/client";
const QUERY: TypedDocumentNode<{
products: {
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useQueryWithCache/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useQueryWithCache/page.tsx
index 455d44f7..8df4b7ce 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useQueryWithCache/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useQueryWithCache/page.tsx
@@ -1,11 +1,7 @@
"use client";
-import {
- useQuery,
- useSuspenseQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
import type { TypedDocumentNode } from "@apollo/client";
-import { gql } from "@apollo/client";
+import { useQuery, useSuspenseQuery, gql } from "@apollo/client";
const QUERY: TypedDocumentNode<{
products: {
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQuery/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQuery/page.tsx
index f5e8553f..b154708f 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQuery/page.tsx
@@ -1,8 +1,7 @@
"use client";
-import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
import type { TypedDocumentNode } from "@apollo/client";
-import { gql } from "@apollo/client";
+import { useSuspenseQuery, gql } from "@apollo/client";
const QUERY: TypedDocumentNode<{
products: {
diff --git a/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQueryWithError/page.tsx b/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQueryWithError/page.tsx
index e1a5b5c0..0862ddad 100644
--- a/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQueryWithError/page.tsx
+++ b/integration-test/nextjs/src/app/cc/dynamic/useSuspenseQueryWithError/page.tsx
@@ -1,10 +1,9 @@
"use client";
-import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
import type { TypedDocumentNode } from "@apollo/client";
-import { gql } from "@apollo/client";
+import { useSuspenseQuery, gql } from "@apollo/client";
import { ErrorBoundary, FallbackProps } from "react-error-boundary";
-import { Suspense, startTransition, useState, useTransition } from "react";
+import { Suspense } from "react";
const QUERY: TypedDocumentNode<{
products: {
diff --git a/integration-test/nextjs/src/app/cc/static/useBackgroundQueryWithoutSsrReadQuery/page.tsx b/integration-test/nextjs/src/app/cc/static/useBackgroundQueryWithoutSsrReadQuery/page.tsx
index 5ec41b4e..18a46960 100644
--- a/integration-test/nextjs/src/app/cc/static/useBackgroundQueryWithoutSsrReadQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/static/useBackgroundQueryWithoutSsrReadQuery/page.tsx
@@ -1,11 +1,12 @@
"use client";
+import type { TypedDocumentNode } from "@apollo/client";
import {
useBackgroundQuery,
useReadQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
-import type { TypedDocumentNode } from "@apollo/client";
-import { gql, QueryReference } from "@apollo/client";
+ gql,
+ QueryReference,
+} from "@apollo/client";
import { Suspense, useState, useEffect } from "react";
interface Data {
diff --git a/integration-test/nextjs/src/app/cc/static/useSuspenseQuery/page.tsx b/integration-test/nextjs/src/app/cc/static/useSuspenseQuery/page.tsx
index 65449c48..16f631c0 100644
--- a/integration-test/nextjs/src/app/cc/static/useSuspenseQuery/page.tsx
+++ b/integration-test/nextjs/src/app/cc/static/useSuspenseQuery/page.tsx
@@ -1,8 +1,7 @@
"use client";
-import { useSuspenseQuery } from "@apollo/experimental-nextjs-app-support/ssr";
import type { TypedDocumentNode } from "@apollo/client";
-import { gql, useApolloClient } from "@apollo/client";
+import { gql, useSuspenseQuery } from "@apollo/client";
const QUERY: TypedDocumentNode<{
products: {
diff --git a/integration-test/vitest/src/App.jsx b/integration-test/vitest/src/App.jsx
index 12ccf395..2f5813b1 100644
--- a/integration-test/vitest/src/App.jsx
+++ b/integration-test/vitest/src/App.jsx
@@ -1,12 +1,16 @@
import { Suspense, useState } from "react";
import {
ApolloNextAppProvider,
- NextSSRApolloClient,
- NextSSRInMemoryCache,
- useSuspenseQuery,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+ ApolloClient,
+ InMemoryCache,
+} from "@apollo/experimental-nextjs-app-support";
import { SchemaLink } from "@apollo/client/link/schema/index.js";
-import { gql, ApolloLink, Observable } from "@apollo/client/index.js";
+import {
+ useSuspenseQuery,
+ gql,
+ ApolloLink,
+ Observable,
+} from "@apollo/client/index.js";
import { schema } from "./schema";
const delayLink = new ApolloLink((operation, forward) => {
@@ -22,8 +26,8 @@ const delayLink = new ApolloLink((operation, forward) => {
});
export const makeClient = () => {
- return new NextSSRApolloClient({
- cache: new NextSSRInMemoryCache(),
+ return new ApolloClient({
+ cache: new InMemoryCache(),
link: delayLink.concat(new SchemaLink({ schema })),
});
};
diff --git a/integration-test/vitest/src/App.test.jsx b/integration-test/vitest/src/App.test.jsx
index 68142a18..e42304f1 100644
--- a/integration-test/vitest/src/App.test.jsx
+++ b/integration-test/vitest/src/App.test.jsx
@@ -2,9 +2,9 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import App from "./App";
-import { resetNextSSRApolloSingletons } from "@apollo/experimental-nextjs-app-support/ssr";
+import { resetApolloClientSingletons } from "@apollo/experimental-nextjs-app-support";
-afterEach(resetNextSSRApolloSingletons);
+afterEach(resetApolloClientSingletons);
test("loads data", async () => {
render();
diff --git a/integration-test/vitest/src/hooks.test.jsx b/integration-test/vitest/src/hooks.test.jsx
index c16a8f98..6582106d 100644
--- a/integration-test/vitest/src/hooks.test.jsx
+++ b/integration-test/vitest/src/hooks.test.jsx
@@ -4,11 +4,11 @@ import "@testing-library/jest-dom";
import { makeClient, QUERY } from "./App";
import {
ApolloNextAppProvider,
- NextSSRApolloClient,
- useQuery,
- resetNextSSRApolloSingletons,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+ ApolloClient,
+ resetApolloClientSingletons,
+} from "@apollo/experimental-nextjs-app-support";
import { Suspense } from "react";
+import { useQuery } from "@apollo/client";
const wrapper = ({ children }) => (
@@ -16,7 +16,7 @@ const wrapper = ({ children }) => (
);
-afterEach(resetNextSSRApolloSingletons);
+afterEach(resetApolloClientSingletons);
/**
* We test that jest is using the "browser" build.
@@ -25,7 +25,7 @@ afterEach(resetNextSSRApolloSingletons);
*/
test("uses the browser build", () => {
let foundPrototype = false;
- let proto = NextSSRApolloClient;
+ let proto = ApolloClient;
while (proto) {
if (proto.name === "ApolloClientBrowserImpl") {
foundPrototype = true;
diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
index 6779f90d..735db7b7 100644
--- a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
+++ b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
@@ -361,7 +361,7 @@ const ApolloClientImplementation =
: ApolloClientBase;
/**
- * A version of `ApolloClient` to be used with streaming SSR.
+ * A version of `ApolloClient` to be used with streaming SSR or in React Server Components.
*
* For more documentation, please see {@link https://www.apollographql.com/docs/react/api/core/ApolloClient | the Apollo Client API documentation}.
*
diff --git a/packages/experimental-nextjs-app-support/README.md b/packages/experimental-nextjs-app-support/README.md
index 24d4756c..1eadf21a 100644
--- a/packages/experimental-nextjs-app-support/README.md
+++ b/packages/experimental-nextjs-app-support/README.md
@@ -50,8 +50,12 @@ npm install @apollo/client@latest @apollo/experimental-nextjs-app-support
Create an `ApolloClient.js` file:
```js
-import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
-import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";
+import { HttpLink } from "@apollo/client";
+import {
+ registerApolloClient,
+ ApolloClient,
+ InMemoryCache,
+} from "@apollo/experimental-nextjs-app-support";
export const { getClient } = registerApolloClient(() => {
return new ApolloClient({
@@ -86,10 +90,10 @@ First, create a new file `app/ApolloWrapper.jsx`:
import { ApolloLink, HttpLink } from "@apollo/client";
import {
ApolloNextAppProvider,
- NextSSRInMemoryCache,
- NextSSRApolloClient,
+ ApolloClient,
+ InMemoryCache,
SSRMultipartLink,
-} from "@apollo/experimental-nextjs-app-support/ssr";
+} from "@apollo/experimental-nextjs-app-support";
// have a function to create a client for you
function makeClient() {
@@ -105,21 +109,11 @@ function makeClient() {
// const { data } = useSuspenseQuery(MY_QUERY, { context: { fetchOptions: { cache: "force-cache" }}});
});
- return new NextSSRApolloClient({
- // use the `NextSSRInMemoryCache`, not the normal `InMemoryCache`
- cache: new NextSSRInMemoryCache(),
- link:
- typeof window === "undefined"
- ? ApolloLink.from([
- // in a SSR environment, if you use multipart features like
- // @defer, you need to decide how to handle these.
- // This strips all interfaces with a `@defer` directive from your queries.
- new SSRMultipartLink({
- stripDefer: true,
- }),
- httpLink,
- ])
- : httpLink,
+ // use the `ApolloClient` from "@apollo/experimental-nextjs-app-support"
+ return new ApolloClient({
+ // use the `InMemoryCache` from "@apollo/experimental-nextjs-app-support"
+ cache: new InMemoryCache(),
+ link: httpLink,
});
}
@@ -164,12 +158,12 @@ If you want to make the most of the streaming SSR features offered by React & th
This package uses some singleton instances on the Browser side - if you are writing tests, you must reset them between tests.
-For that, you can use the `resetNextSSRApolloSingletons` helper:
+For that, you can use the `resetApolloClientSingletons ` helper:
```ts
-import { resetNextSSRApolloSingletons } from "@apollo/experimental-nextjs-app-support/ssr";
+import { resetApolloClientSingletons } from "@apollo/experimental-nextjs-app-support";
-afterEach(resetNextSSRApolloSingletons);
+afterEach(resetApolloClientSingletons);
```
## Handling Multipart responses in SSR
diff --git a/packages/experimental-nextjs-app-support/api-extractor.d.ts b/packages/experimental-nextjs-app-support/api-extractor.d.ts
index 28f02f54..d8c9ad8a 100644
--- a/packages/experimental-nextjs-app-support/api-extractor.d.ts
+++ b/packages/experimental-nextjs-app-support/api-extractor.d.ts
@@ -2,5 +2,4 @@
* @packageDocumentation
*/
-export * from "./dist/rsc/index.d.ts";
-export * from "./dist/ssr/index.ssr.d.ts";
+export * from "./dist/combined.d.ts";
diff --git a/packages/experimental-nextjs-app-support/package-shape.json b/packages/experimental-nextjs-app-support/package-shape.json
index 0e979872..1347707c 100644
--- a/packages/experimental-nextjs-app-support/package-shape.json
+++ b/packages/experimental-nextjs-app-support/package-shape.json
@@ -1,4 +1,45 @@
{
+ "@apollo/experimental-nextjs-app-support": {
+ "react-server": [
+ "registerApolloClient",
+ "DebounceMultipartResponsesLink",
+ "RemoveMultipartDirectivesLink",
+ "SSRMultipartLink",
+ "ApolloClient",
+ "InMemoryCache",
+ "built_for_rsc"
+ ],
+ "browser": [
+ "ApolloNextAppProvider",
+ "DebounceMultipartResponsesLink",
+ "ApolloClient",
+ "InMemoryCache",
+ "RemoveMultipartDirectivesLink",
+ "SSRMultipartLink",
+ "resetApolloClientSingletons",
+ "built_for_browser"
+ ],
+ "node": [
+ "ApolloNextAppProvider",
+ "DebounceMultipartResponsesLink",
+ "ApolloClient",
+ "InMemoryCache",
+ "RemoveMultipartDirectivesLink",
+ "SSRMultipartLink",
+ "resetApolloClientSingletons",
+ "built_for_ssr"
+ ],
+ "edge-light,worker,browser": [
+ "ApolloNextAppProvider",
+ "DebounceMultipartResponsesLink",
+ "ApolloClient",
+ "InMemoryCache",
+ "RemoveMultipartDirectivesLink",
+ "SSRMultipartLink",
+ "resetApolloClientSingletons",
+ "built_for_ssr"
+ ]
+ },
"@apollo/experimental-nextjs-app-support/ssr": {
"react-server": [
"DebounceMultipartResponsesLink",
diff --git a/packages/experimental-nextjs-app-support/package.json b/packages/experimental-nextjs-app-support/package.json
index 0ef4a51d..092904d6 100644
--- a/packages/experimental-nextjs-app-support/package.json
+++ b/packages/experimental-nextjs-app-support/package.json
@@ -16,6 +16,22 @@
],
"type": "module",
"exports": {
+ ".": {
+ "require": {
+ "types": "./dist/combined.d.cts",
+ "react-server": "./dist/index.rsc.cjs",
+ "edge-light": "./dist/index.ssr.cjs",
+ "browser": "./dist/index.browser.cjs",
+ "node": "./dist/index.ssr.cjs"
+ },
+ "import": {
+ "types": "./dist/combined.d.ts",
+ "react-server": "./dist/index.rsc.js",
+ "edge-light": "./dist/index.ssr.js",
+ "browser": "./dist/index.browser.js",
+ "node": "./dist/index.ssr.js"
+ }
+ },
"./rsc": {
"require": {
"types": "./dist/rsc/index.d.cts",
@@ -56,7 +72,7 @@
]
}
},
- "typings": "./dist/empty.d.ts",
+ "typings": "./dist/combined.d.ts",
"author": "packages@apollographql.com",
"license": "MIT",
"files": [
diff --git a/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts b/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
index 1679ed87..b1d3c9c1 100644
--- a/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
+++ b/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
@@ -12,20 +12,24 @@ import { bundle } from "./bundleInfo.js";
* As opposed to the normal `ApolloProvider`, this version does not require a `client` prop,
* but requires a `makeClient` prop instead.
*
- * Use this component together with `NextSSRApolloClient` and `NextSSRInMemoryCache`
+ * Use this component together with `ApolloClient` and `InMemoryCache`
+ * from the "@apollo/experimental-nextjs-app-support" package
* to make an ApolloClient instance available to your Client Component hooks in the
* Next.js App Router.
*
* @example
* `app/ApolloWrapper.jsx`
* ```tsx
+ * import { HttpLink } from "@apollo/client";
+ * import { ApolloNextAppProvider, ApolloClient, InMemoryCache } from "@apollo/experimental-nextjs-app-support";
+ *
* function makeClient() {
* const httpLink = new HttpLink({
* uri: "https://example.com/api/graphql",
* });
*
- * return new NextSSRApolloClient({
- * cache: new NextSSRInMemoryCache(),
+ * return new ApolloClient({
+ * cache: new InMemoryCache(),
* link: httpLink,
* });
* }
diff --git a/packages/experimental-nextjs-app-support/src/bundleInfo.ts b/packages/experimental-nextjs-app-support/src/bundleInfo.ts
index 58fc2bdf..5f44d9e2 100644
--- a/packages/experimental-nextjs-app-support/src/bundleInfo.ts
+++ b/packages/experimental-nextjs-app-support/src/bundleInfo.ts
@@ -1,5 +1,5 @@
export const bundle = {
- pkg: "@apollo/experimental-nextjs-app-support/ssr",
- client: "NextSSRApolloClient",
- cache: "NextSSRInMemoryCache",
+ pkg: "@apollo/experimental-nextjs-app-support",
+ client: "ApolloClient",
+ cache: "InMemoryCache",
};
diff --git a/packages/experimental-nextjs-app-support/src/combined.ts b/packages/experimental-nextjs-app-support/src/combined.ts
new file mode 100644
index 00000000..086e6ca7
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/src/combined.ts
@@ -0,0 +1,17 @@
+/**
+ * TypeScript does not have the concept of these environments,
+ * so we need to create a single entry point that combines all
+ * possible exports.
+ * That means that users will be offered "RSC" exports in a
+ * "SSR/Browser" code file, but those will error in a compilation
+ * step.
+ *
+ * This is a limitation of TypeScript, and we can't do anything
+ * about it.
+ *
+ * The build process will only create `.d.ts`/`d.cts` files from
+ * this, and not actual runtime code.
+ */
+
+export * from "./index.rsc.js";
+export * from "./index.js";
diff --git a/packages/experimental-nextjs-app-support/src/index.rsc.ts b/packages/experimental-nextjs-app-support/src/index.rsc.ts
new file mode 100644
index 00000000..a7649d95
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/src/index.rsc.ts
@@ -0,0 +1,2 @@
+export * from "./index.shared.js";
+export { registerApolloClient } from "@apollo/client-react-streaming";
diff --git a/packages/experimental-nextjs-app-support/src/index.shared.ts b/packages/experimental-nextjs-app-support/src/index.shared.ts
new file mode 100644
index 00000000..9f55f932
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/src/index.shared.ts
@@ -0,0 +1,27 @@
+export {
+ SSRMultipartLink,
+ DebounceMultipartResponsesLink,
+ RemoveMultipartDirectivesLink,
+ InMemoryCache,
+ type TransportedQueryRef,
+} from "@apollo/client-react-streaming";
+import { bundle } from "./bundleInfo.js";
+import { ApolloClient as UpstreamApolloClient } from "@apollo/client-react-streaming";
+
+/**
+ * A version of `ApolloClient` to be used with streaming SSR or in React Server Components.
+ *
+ * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/core/ApolloClient | the Apollo Client API documentation}.
+ *
+ * @public
+ */
+export class ApolloClient<
+ TCacheShape,
+> extends UpstreamApolloClient {
+ /**
+ * Information about the current package and it's export names, for use in error messages.
+ *
+ * @internal
+ */
+ static readonly info = bundle;
+}
diff --git a/packages/experimental-nextjs-app-support/src/index.ts b/packages/experimental-nextjs-app-support/src/index.ts
new file mode 100644
index 00000000..bfb5f80a
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/src/index.ts
@@ -0,0 +1,16 @@
+export * from "./index.shared.js";
+export { ApolloNextAppProvider } from "./ApolloNextAppProvider.js";
+import { resetManualSSRApolloSingletons } from "@apollo/client-react-streaming/manual-transport";
+/**
+ * > This export is only available in React Client Components
+ *
+ * Resets the singleton instances created for the Apollo SSR data transport and caches.
+ *
+ * To be used in testing only, like
+ * ```ts
+ * afterEach(resetApolloClientSingletons);
+ * ```
+ *
+ * @public
+ */
+export const resetApolloClientSingletons = resetManualSSRApolloSingletons;
diff --git a/packages/experimental-nextjs-app-support/src/rsc/index.ts b/packages/experimental-nextjs-app-support/src/rsc/index.ts
index acd8769e..5d684d3b 100644
--- a/packages/experimental-nextjs-app-support/src/rsc/index.ts
+++ b/packages/experimental-nextjs-app-support/src/rsc/index.ts
@@ -1,4 +1,4 @@
export {
registerApolloClient,
type TransportedQueryRef,
-} from "@apollo/client-react-streaming";
+} from "@apollo/experimental-nextjs-app-support";
diff --git a/packages/experimental-nextjs-app-support/src/ssr/index.ts b/packages/experimental-nextjs-app-support/src/ssr/index.ts
index 49f8da9d..c1c625d2 100644
--- a/packages/experimental-nextjs-app-support/src/ssr/index.ts
+++ b/packages/experimental-nextjs-app-support/src/ssr/index.ts
@@ -1,14 +1,13 @@
-export { ApolloNextAppProvider } from "../ApolloNextAppProvider.js";
-export { resetManualSSRApolloSingletons as resetNextSSRApolloSingletons } from "@apollo/client-react-streaming/manual-transport";
-import { ApolloClient } from "@apollo/client-react-streaming";
-import { bundle } from "../bundleInfo.js";
export {
InMemoryCache as NextSSRInMemoryCache,
+ ApolloClient as NextSSRApolloClient,
SSRMultipartLink,
DebounceMultipartResponsesLink,
RemoveMultipartDirectivesLink,
+ ApolloNextAppProvider,
+ resetApolloClientSingletons as resetNextSSRApolloSingletons,
type TransportedQueryRef,
-} from "@apollo/client-react-streaming";
+} from "@apollo/experimental-nextjs-app-support";
export {
useBackgroundQuery,
useFragment,
@@ -16,20 +15,3 @@ export {
useReadQuery,
useSuspenseQuery,
} from "@apollo/client/index.js";
-/**
- * A version of `ApolloClient` to be used with streaming SSR.
- *
- * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/core/ApolloClient | the Apollo Client API documentation}.
- *
- * @public
- */
-export class NextSSRApolloClient<
- TCacheShape,
-> extends ApolloClient {
- /**
- * Information about the current package and it's export names, for use in error messages.
- *
- * @internal
- */
- static readonly info = bundle;
-}
diff --git a/packages/experimental-nextjs-app-support/tsconfig.json b/packages/experimental-nextjs-app-support/tsconfig.json
index 82531828..a29b456c 100644
--- a/packages/experimental-nextjs-app-support/tsconfig.json
+++ b/packages/experimental-nextjs-app-support/tsconfig.json
@@ -11,7 +11,10 @@
"jsx": "react",
"declarationMap": true,
"types": ["react/canary", "node"],
- "esModuleInterop": true
+ "esModuleInterop": true,
+ "paths": {
+ "@apollo/experimental-nextjs-app-support": ["./src/combined.ts"]
+ }
},
"include": ["src"]
}
diff --git a/packages/experimental-nextjs-app-support/tsup.config.ts b/packages/experimental-nextjs-app-support/tsup.config.ts
index 404e5e14..a6d5efff 100644
--- a/packages/experimental-nextjs-app-support/tsup.config.ts
+++ b/packages/experimental-nextjs-app-support/tsup.config.ts
@@ -14,6 +14,7 @@ export default defineConfig((options) => {
external: [
"@apollo/client-react-streaming",
"@apollo/client-react-streaming/manual-transport",
+ "@apollo/experimental-nextjs-app-support",
"react",
"rehackt",
],
@@ -50,7 +51,14 @@ export default defineConfig((options) => {
}
return [
+ {
+ ...entry("other", "src/combined.ts", "combined"),
+ dts: { only: true },
+ },
entry("other", "src/empty.ts", "empty"),
+ entry("rsc", "src/index.rsc.ts", "index.rsc"),
+ entry("ssr", "src/index.ts", "index.ssr"),
+ entry("browser", "src/index.ts", "index.browser"),
entry("rsc", "src/rsc/index.ts", "rsc/index"),
entry("rsc", "src/ssr/index.rsc.ts", "ssr/index.rsc"),
entry("ssr", "src/ssr/index.ts", "ssr/index.ssr"),
From 5757a9c2e1c89d51186f17ae6ec727e6b2110d6b Mon Sep 17 00:00:00 2001
From: Nick Muller <3781551+nphmuller@users.noreply.github.com>
Date: Tue, 28 May 2024 14:53:38 +0200
Subject: [PATCH 07/11] Add next@15.0.0-rc.0 as possible `peerDependency`
(#304)
* Support next@15.0.0-rc.0
* adjust lockfiles
---------
Co-authored-by: Lenz Weber-Tronic
---
integration-test/yarn.lock | 2 +-
packages/experimental-nextjs-app-support/package.json | 2 +-
yarn.lock | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/integration-test/yarn.lock b/integration-test/yarn.lock
index 5dd3b772..c5bde46a 100644
--- a/integration-test/yarn.lock
+++ b/integration-test/yarn.lock
@@ -87,7 +87,7 @@ __metadata:
"@apollo/client-react-streaming": "npm:0.10.1"
peerDependencies:
"@apollo/client": ^3.10.4
- next: ^13.4.1 || ^14.0.0
+ next: ^13.4.1 || ^14.0.0 || 15.0.0-rc.0
react: ^18
checksum: 10/505b723bac0f3a7f15287ea32fab9f2e8c0cd567149abf11d750855f8a9bfc0aa26e44179ad10c32f7d162ad86318717032413ef8e1a25385185178e022588fa
languageName: node
diff --git a/packages/experimental-nextjs-app-support/package.json b/packages/experimental-nextjs-app-support/package.json
index 092904d6..717d60e5 100644
--- a/packages/experimental-nextjs-app-support/package.json
+++ b/packages/experimental-nextjs-app-support/package.json
@@ -127,7 +127,7 @@
},
"peerDependencies": {
"@apollo/client": "^3.10.4",
- "next": "^13.4.1 || ^14.0.0",
+ "next": "^13.4.1 || ^14.0.0 || 15.0.0-rc.0",
"react": "^18"
},
"dependencies": {
diff --git a/yarn.lock b/yarn.lock
index 04a94fd4..131d218f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -157,7 +157,7 @@ __metadata:
vitest: "npm:1.6.0"
peerDependencies:
"@apollo/client": ^3.10.4
- next: ^13.4.1 || ^14.0.0
+ next: ^13.4.1 || ^14.0.0 || 15.0.0-rc.0
react: ^18
languageName: unknown
linkType: soft
From 5cc6125268c1ab2b21764693cab2fa1c3ee5d951 Mon Sep 17 00:00:00 2001
From: Lenz Weber-Tronic
Date: Wed, 29 May 2024 10:28:06 +0200
Subject: [PATCH 08/11] Use a symbol property instead of instanceOf to check
for correct ApolloClient/InMemoryCache (#302)
* Use a symbol property instead of instanceOf
* fix up test
* extract assertInstance function
* Update packages/client-react-streaming/src/importErrors.test.tsx
Co-authored-by: Jerel Miller
---------
Co-authored-by: Jerel Miller
---
packages/client-react-streaming/package.json | 1 +
.../WrapApolloProvider.tsx | 23 +--
.../WrappedApolloClient.test.tsx | 4 +-
.../WrappedApolloClient.tsx | 73 +++----
.../WrappedInMemoryCache.tsx | 17 +-
.../ManualDataTransport/serialization.test.ts | 2 +-
.../src/assertInstance.ts | 14 ++
.../client-react-streaming/src/bundleInfo.ts | 4 +-
.../src/importErrors.test.tsx | 164 +++++++++------
.../src/registerApolloClient.test.tsx | 2 +-
.../tsconfig.tests.json | 15 +-
.../package.json | 23 ++-
.../src/importErrors.test.tsx | 186 ++++++++++++++++++
.../src/index.shared.ts | 22 ++-
.../tsconfig.tests.json | 8 +
packages/test-utils/console.d.ts | 3 +
packages/test-utils/console.js | 9 +
packages/test-utils/hydrationTest.d.ts | 18 ++
.../hydrationTest.js} | 20 +-
packages/test-utils/package.json | 7 +
packages/test-utils/react.d.ts | 14 ++
packages/test-utils/react.js | 34 ++++
packages/test-utils/runInConditions.d.ts | 15 ++
.../runInConditions.js} | 19 +-
packages/test-utils/tsconfig.json | 11 ++
yarn.lock | 47 ++---
26 files changed, 591 insertions(+), 164 deletions(-)
create mode 100644 packages/client-react-streaming/src/assertInstance.ts
create mode 100644 packages/experimental-nextjs-app-support/src/importErrors.test.tsx
create mode 100644 packages/experimental-nextjs-app-support/tsconfig.tests.json
create mode 100644 packages/test-utils/console.d.ts
create mode 100644 packages/test-utils/console.js
create mode 100644 packages/test-utils/hydrationTest.d.ts
rename packages/{client-react-streaming/src/util/hydrationTest.ts => test-utils/hydrationTest.js} (80%)
create mode 100644 packages/test-utils/package.json
create mode 100644 packages/test-utils/react.d.ts
create mode 100644 packages/test-utils/react.js
create mode 100644 packages/test-utils/runInConditions.d.ts
rename packages/{client-react-streaming/src/util/runInConditions.ts => test-utils/runInConditions.js} (73%)
create mode 100644 packages/test-utils/tsconfig.json
diff --git a/packages/client-react-streaming/package.json b/packages/client-react-streaming/package.json
index df318691..10eb866a 100644
--- a/packages/client-react-streaming/package.json
+++ b/packages/client-react-streaming/package.json
@@ -133,6 +133,7 @@
"devDependencies": {
"@apollo/client": "^3.10.4",
"@arethetypeswrong/cli": "0.15.3",
+ "@internal/test-utils": "workspace:^",
"@microsoft/api-extractor": "7.43.2",
"@testing-library/react": "15.0.7",
"@total-typescript/shoehorn": "0.1.2",
diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/WrapApolloProvider.tsx b/packages/client-react-streaming/src/DataTransportAbstraction/WrapApolloProvider.tsx
index a48a052d..23cb9c58 100644
--- a/packages/client-react-streaming/src/DataTransportAbstraction/WrapApolloProvider.tsx
+++ b/packages/client-react-streaming/src/DataTransportAbstraction/WrapApolloProvider.tsx
@@ -1,11 +1,12 @@
"use client";
import React from "react";
import { useRef } from "react";
-import { ApolloClient } from "./WrappedApolloClient.js";
+import type { ApolloClient } from "./WrappedApolloClient.js";
import { ApolloProvider } from "@apollo/client/index.js";
import type { DataTransportProviderImplementation } from "./DataTransportAbstraction.js";
import { ApolloClientSingleton } from "./symbols.js";
import { bundle } from "../bundleInfo.js";
+import { assertInstance } from "../assertInstance.js";
declare global {
interface Window {
@@ -35,8 +36,6 @@ export interface WrappedApolloProvider {
*/
info: {
pkg: string;
- client: string;
- cache: string;
};
}
@@ -59,18 +58,16 @@ export function WrapApolloProvider(
...extraProps
}) => {
const clientRef = useRef>(undefined);
-
- if (process.env.REACT_ENV === "ssr") {
- if (!clientRef.current) {
+ if (!clientRef.current) {
+ if (process.env.REACT_ENV === "ssr") {
clientRef.current = makeClient();
+ } else {
+ clientRef.current = window[ApolloClientSingleton] ??= makeClient();
}
- } else {
- clientRef.current = window[ApolloClientSingleton] ??= makeClient();
- }
-
- if (!(clientRef.current instanceof ApolloClient)) {
- throw new Error(
- `When using \`ApolloClient\` in streaming SSR, you must use the \`${WrappedApolloProvider.info.client}\` export provided by \`"${WrappedApolloProvider.info.pkg}"\`.`
+ assertInstance(
+ clientRef.current,
+ WrappedApolloProvider.info,
+ "ApolloClient"
);
}
diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.test.tsx b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.test.tsx
index d1a8fd65..4dfcc850 100644
--- a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.test.tsx
+++ b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.test.tsx
@@ -1,5 +1,5 @@
import React, { Suspense, use, useMemo } from "rehackt";
-import { outsideOf } from "../util/runInConditions.js";
+import { outsideOf } from "@internal/test-utils/runInConditions.js";
import assert from "node:assert";
import test, { afterEach, describe } from "node:test";
import type {
@@ -233,7 +233,7 @@ describe(
{ skip: outsideOf("browser") },
async () => {
const { $RC, $RS, setBody, hydrateBody, appendToBody } = await import(
- "../util/hydrationTest.js"
+ "@internal/test-utils/hydrationTest.js"
);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
let useStaticValueRefStub = (): { current: T } => {
diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
index 735db7b7..f6f9d726 100644
--- a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
+++ b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedApolloClient.tsx
@@ -5,6 +5,7 @@ import type {
WatchQueryOptions,
FetchResult,
DocumentNode,
+ NormalizedCacheObject,
} from "@apollo/client/index.js";
import {
ApolloClient as OrigApolloClient,
@@ -15,7 +16,7 @@ import { print } from "@apollo/client/utilities/index.js";
import { canonicalStringify } from "@apollo/client/cache/index.js";
import { invariant } from "ts-invariant";
import { createBackpressuredCallback } from "./backpressuredCallback.js";
-import { InMemoryCache } from "./WrappedInMemoryCache.js";
+import type { InMemoryCache } from "./WrappedInMemoryCache.js";
import { hookWrappers } from "./hooks.js";
import type { HookWrappers } from "@apollo/client/react/internal/index.js";
import type { QueryInfo } from "@apollo/client/core/QueryInfo.js";
@@ -24,12 +25,13 @@ import type {
QueryEvent,
TransportIdentifier,
} from "./DataTransportAbstraction.js";
-import { bundle } from "../bundleInfo.js";
+import { bundle, sourceSymbol } from "../bundleInfo.js";
import { serializeOptions, deserializeOptions } from "./transportedOptions.js";
+import { assertInstance } from "../assertInstance.js";
-function getQueryManager(
+function getQueryManager(
client: OrigApolloClient
-): QueryManager & {
+): QueryManager & {
[wrappers]: HookWrappers;
} {
return client["queryManager"];
@@ -49,8 +51,13 @@ type SimulatedQueryInfo = {
options: WatchQueryOptions;
};
+interface WrappedApolloClientOptions
+ extends Omit, "cache"> {
+ cache: InMemoryCache;
+}
+
const wrappers = Symbol.for("apollo.hook.wrappers");
-class ApolloClientBase extends OrigApolloClient {
+class ApolloClientBase extends OrigApolloClient {
/**
* Information about the current package and it's export names, for use in error messages.
*
@@ -58,7 +65,9 @@ class ApolloClientBase extends OrigApolloClient {
*/
static readonly info = bundle;
- constructor(options: ApolloClientOptions) {
+ [sourceSymbol]: string;
+
+ constructor(options: WrappedApolloClientOptions) {
super(
process.env.REACT_ENV === "rsc" || process.env.REACT_ENV === "ssr"
? {
@@ -67,19 +76,19 @@ class ApolloClientBase extends OrigApolloClient {
}
: options
);
+ const info = (this.constructor as typeof ApolloClientBase).info;
+ this[sourceSymbol] = `${info.pkg}:ApolloClient`;
- if (!(this.cache instanceof InMemoryCache)) {
- throw new Error(
- `When using \`InMemoryCache\` in streaming SSR, you must use the \`${(this.constructor as typeof ApolloClientBase).info.cache}\` export provided by \`"${(this.constructor as typeof ApolloClientBase).info.pkg}"\`.`
- );
- }
+ assertInstance(
+ this.cache as unknown as InMemoryCache,
+ info,
+ "InMemoryCache"
+ );
}
}
-export class ApolloClientClientBaseImpl<
- TCacheShape,
-> extends ApolloClientBase {
- constructor(options: ApolloClientOptions) {
+export class ApolloClientClientBaseImpl extends ApolloClientBase {
+ constructor(options: WrappedApolloClientOptions) {
super(options);
this.onQueryStarted = this.onQueryStarted.bind(this);
@@ -102,7 +111,7 @@ export class ApolloClientClientBaseImpl<
const transformedDocument = this.documentTransform.transformDocument(
options.query
);
- const queryManager = getQueryManager(this);
+ const queryManager = getQueryManager(this);
// Calling `transformDocument` will add __typename but won't remove client
// directives, so we need to get the `serverQuery`.
const { serverQuery } = queryManager.getDocumentInfo(transformedDocument);
@@ -127,7 +136,7 @@ export class ApolloClientClientBaseImpl<
const { cacheKey, cacheKeyArr } = this.identifyUniqueQuery(hydratedOptions);
this.transportedQueryOptions.set(id, hydratedOptions);
- const queryManager = getQueryManager(this);
+ const queryManager = getQueryManager(this);
if (
!queryManager["inFlightLinkObservables"].peekArray(cacheKeyArr)
@@ -269,9 +278,7 @@ export class ApolloClientClientBaseImpl<
};
}
-class ApolloClientSSRImpl<
- TCacheShape,
-> extends ApolloClientClientBaseImpl {
+class ApolloClientSSRImpl extends ApolloClientClientBaseImpl {
private forwardedQueries = new (getTrieConstructor(this))();
watchQueryQueue = createBackpressuredCallback<{
@@ -349,9 +356,7 @@ class ApolloClientSSRImpl<
}
}
-export class ApolloClientBrowserImpl<
- TCacheShape,
-> extends ApolloClientClientBaseImpl {}
+export class ApolloClientBrowserImpl extends ApolloClientClientBaseImpl {}
const ApolloClientImplementation =
/*#__PURE__*/ process.env.REACT_ENV === "ssr"
@@ -367,20 +372,22 @@ const ApolloClientImplementation =
*
* @public
*/
-export class ApolloClient
- extends (ApolloClientImplementation as typeof ApolloClientBase)
- implements
- Partial>,
- Partial>
+export class ApolloClient<
+ // this generic is obsolete as we require a `InMemoryStore`, which fixes this generic to `NormalizedCacheObject` anyways
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ Ignored = NormalizedCacheObject,
+ >
+ extends (ApolloClientImplementation as typeof ApolloClientBase)
+ implements Partial, Partial
{
/** @internal */
- declare onQueryStarted?: ApolloClientBrowserImpl["onQueryStarted"];
+ declare onQueryStarted?: ApolloClientBrowserImpl["onQueryStarted"];
/** @internal */
- declare onQueryProgress?: ApolloClientBrowserImpl["onQueryProgress"];
+ declare onQueryProgress?: ApolloClientBrowserImpl["onQueryProgress"];
/** @internal */
- declare rerunSimulatedQueries?: ApolloClientBrowserImpl["rerunSimulatedQueries"];
+ declare rerunSimulatedQueries?: ApolloClientBrowserImpl["rerunSimulatedQueries"];
/** @internal */
- declare rerunSimulatedQuery?: ApolloClientBrowserImpl["rerunSimulatedQuery"];
+ declare rerunSimulatedQuery?: ApolloClientBrowserImpl["rerunSimulatedQuery"];
/** @internal */
- declare watchQueryQueue?: ApolloClientSSRImpl["watchQueryQueue"];
+ declare watchQueryQueue?: ApolloClientSSRImpl["watchQueryQueue"];
}
diff --git a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedInMemoryCache.tsx b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedInMemoryCache.tsx
index 0792b9ed..892aa7d8 100644
--- a/packages/client-react-streaming/src/DataTransportAbstraction/WrappedInMemoryCache.tsx
+++ b/packages/client-react-streaming/src/DataTransportAbstraction/WrappedInMemoryCache.tsx
@@ -1,4 +1,6 @@
+import type { InMemoryCacheConfig } from "@apollo/client/index.js";
import { InMemoryCache as OrigInMemoryCache } from "@apollo/client/index.js";
+import { bundle, sourceSymbol } from "../bundleInfo.js";
/*
* We just subclass `InMemoryCache` here so that `WrappedApolloClient`
* can detect if it was initialized with an `InMemoryCache` instance that
@@ -15,4 +17,17 @@ import { InMemoryCache as OrigInMemoryCache } from "@apollo/client/index.js";
*
* @public
*/
-export class InMemoryCache extends OrigInMemoryCache {}
+export class InMemoryCache extends OrigInMemoryCache {
+ /**
+ * Information about the current package and it's export names, for use in error messages.
+ *
+ * @internal
+ */
+ static readonly info = bundle;
+ [sourceSymbol]: string;
+ constructor(config?: InMemoryCacheConfig | undefined) {
+ super(config);
+ const info = (this.constructor as typeof InMemoryCache).info;
+ this[sourceSymbol] = `${info.pkg}:InMemoryCache`;
+ }
+}
diff --git a/packages/client-react-streaming/src/ManualDataTransport/serialization.test.ts b/packages/client-react-streaming/src/ManualDataTransport/serialization.test.ts
index 427f4981..e29115a7 100644
--- a/packages/client-react-streaming/src/ManualDataTransport/serialization.test.ts
+++ b/packages/client-react-streaming/src/ManualDataTransport/serialization.test.ts
@@ -1,6 +1,6 @@
import test, { describe } from "node:test";
import { revive, stringify } from "./serialization.js";
-import { outsideOf } from "../util/runInConditions.js";
+import { outsideOf } from "@internal/test-utils/runInConditions.js";
import { htmlEscapeJsonString } from "./htmlescape.js";
import assert from "node:assert";
diff --git a/packages/client-react-streaming/src/assertInstance.ts b/packages/client-react-streaming/src/assertInstance.ts
new file mode 100644
index 00000000..8277f41c
--- /dev/null
+++ b/packages/client-react-streaming/src/assertInstance.ts
@@ -0,0 +1,14 @@
+import type { bundle } from "./bundleInfo.js";
+import { sourceSymbol } from "./bundleInfo.js";
+
+export function assertInstance(
+ value: { [sourceSymbol]?: string },
+ info: typeof bundle,
+ name: string
+): void {
+ if (value[sourceSymbol] !== `${info.pkg}:${name}`) {
+ throw new Error(
+ `When using \`${name}\` in streaming SSR, you must use the \`${name}\` export provided by \`"${info.pkg}"\`.`
+ );
+ }
+}
diff --git a/packages/client-react-streaming/src/bundleInfo.ts b/packages/client-react-streaming/src/bundleInfo.ts
index 7b1c68e5..69bea952 100644
--- a/packages/client-react-streaming/src/bundleInfo.ts
+++ b/packages/client-react-streaming/src/bundleInfo.ts
@@ -1,5 +1,5 @@
export const bundle = {
pkg: "@apollo/client-react-streaming",
- client: "ApolloClient",
- cache: "InMemoryCache",
};
+
+export const sourceSymbol = Symbol.for("apollo.source_package");
diff --git a/packages/client-react-streaming/src/importErrors.test.tsx b/packages/client-react-streaming/src/importErrors.test.tsx
index 8c1a8bf2..466104f1 100644
--- a/packages/client-react-streaming/src/importErrors.test.tsx
+++ b/packages/client-react-streaming/src/importErrors.test.tsx
@@ -1,6 +1,8 @@
import assert from "node:assert";
import { test } from "node:test";
-import { outsideOf } from "./util/runInConditions.js";
+import { outsideOf } from "@internal/test-utils/runInConditions.js";
+import { browserEnv } from "@internal/test-utils/react.js";
+import { silenceConsoleErrors } from "@internal/test-utils/console.js";
test("Error message when `WrappedApolloClient` is instantiated with wrong `InMemoryCache`", async () => {
const { ApolloClient } = await import("#bundled");
@@ -8,6 +10,7 @@ test("Error message when `WrappedApolloClient` is instantiated with wrong `InMem
assert.throws(
() =>
new ApolloClient({
+ // @ts-expect-error this is what we're testing
cache: new upstreamPkg.InMemoryCache(),
connectToDevTools: false,
}),
@@ -22,81 +25,120 @@ test(
"Error message when using `ManualDataTransport` with the wrong `ApolloClient`",
{ skip: outsideOf("node") },
async () => {
- const { WrapApolloProvider } = await import("#bundled");
- const upstreamPkg = await import("@apollo/client/index.js");
- const { createElement } = await import("react");
+ const { WrapApolloProvider, ...bundled } = await import("#bundled");
+ const React = await import("react");
const { renderToString } = await import("react-dom/server");
- const Provider = WrapApolloProvider({} as any);
+ function FakeTransport({ children }: { children: any }) {
+ return children;
+ }
+ const Provider = WrapApolloProvider(FakeTransport);
- assert.throws(
- () =>
- renderToString(
- createElement(Provider, {
- makeClient: () =>
- // @ts-expect-error we want to test exactly this
- new upstreamPkg.ApolloClient({
- cache: new upstreamPkg.InMemoryCache(),
- }),
- children: null,
- })
- ),
- {
- message:
- 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/client-react-streaming"`.',
- }
- );
+ await test("@apollo/client should error", async () => {
+ const upstreamPkg = await import("@apollo/client/index.js");
+ assert.throws(
+ () =>
+ renderToString(
+
+ // @ts-expect-error we want to test exactly this
+ new upstreamPkg.ApolloClient({
+ cache: new upstreamPkg.InMemoryCache(),
+ })
+ }
+ >
+ {null}
+
+ ),
+ {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/client-react-streaming"`.',
+ }
+ );
+ });
+ await test("this package should work", async () => {
+ renderToString(
+
+ new bundled.ApolloClient({
+ cache: new bundled.InMemoryCache(),
+ })
+ }
+ >
+ {null}
+
+ );
+ });
}
);
test(
- "Error message when using `ManualDataTransport` with the wrong `ApolloClient`",
+ "Error message when using `ApolloNextAppProvider` with the wrong `ApolloClient`",
{ skip: outsideOf("browser") },
async () => {
- const { WrapApolloProvider } = await import("#bundled");
- const upstreamPkg = await import("@apollo/client/index.js");
- const React = await import("react");
- const { createRoot } = await import("react-dom/client");
-
- const jsdom = await import("global-jsdom");
- using _cleanupJSDOM = { [Symbol.dispose]: jsdom.default() };
+ const { WrapApolloProvider, ...bundled } = await import("#bundled");
+ const React = await import("react");
+ function FakeTransport({ children }: { children: any }) {
+ return children;
+ }
+ const Provider = WrapApolloProvider(FakeTransport);
const { ErrorBoundary } = await import("react-error-boundary");
// Even with an error Boundary, React will still log to `console.error` - we avoid the noise here.
using _restoreConsole = silenceConsoleErrors();
- const Provider = WrapApolloProvider({} as any);
-
- const promise = new Promise((_resolve, reject) => {
- createRoot(document.body).render(
- >}>
-
- // @ts-expect-error we want to test exactly this
- new upstreamPkg.ApolloClient({
- cache: new upstreamPkg.InMemoryCache(),
- connectToDevTools: false,
- })
- }
- >
- {null}
-
-
- );
+ await test("@apollo/client should error", async () => {
+ using env = await browserEnv();
+ const upstreamPkg = await import("@apollo/client/index.js");
+ const promise = new Promise((_resolve, reject) => {
+ env.render(
+ document.body,
+ >}>
+
+ // @ts-expect-error we want to test exactly this
+ new upstreamPkg.ApolloClient({
+ cache: new upstreamPkg.InMemoryCache(),
+ connectToDevTools: false,
+ })
+ }
+ >
+ {null}
+
+
+ );
+ });
+ await assert.rejects(promise, {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/client-react-streaming"`.',
+ });
});
- await assert.rejects(promise, {
- message:
- 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/client-react-streaming"`.',
+
+ await test("this package should work", async () => {
+ using env = await browserEnv();
+ const promise = new Promise((resolve, reject) => {
+ function Child() {
+ resolve();
+ return null;
+ }
+ env.render(
+ document.body,
+ >}>
+
+ new bundled.ApolloClient({
+ cache: new bundled.InMemoryCache(),
+ connectToDevTools: false,
+ })
+ }
+ >
+ {}
+
+
+ );
+ });
+ // correct usage, should not throw
+ await promise;
});
}
);
-
-function silenceConsoleErrors() {
- const { error } = console;
- console.error = () => {};
- return {
- [Symbol.dispose]() {
- console.error = error;
- },
- };
-}
diff --git a/packages/client-react-streaming/src/registerApolloClient.test.tsx b/packages/client-react-streaming/src/registerApolloClient.test.tsx
index 5506040a..bf29c057 100644
--- a/packages/client-react-streaming/src/registerApolloClient.test.tsx
+++ b/packages/client-react-streaming/src/registerApolloClient.test.tsx
@@ -1,7 +1,7 @@
/* eslint-disable no-inner-declarations */
import { it } from "node:test";
import assert from "node:assert";
-import { runInConditions } from "./util/runInConditions.js";
+import { runInConditions } from "@internal/test-utils/runInConditions.js";
import { Writable } from "node:stream";
runInConditions("react-server");
diff --git a/packages/client-react-streaming/tsconfig.tests.json b/packages/client-react-streaming/tsconfig.tests.json
index d809d93d..b3edf69b 100644
--- a/packages/client-react-streaming/tsconfig.tests.json
+++ b/packages/client-react-streaming/tsconfig.tests.json
@@ -1,17 +1,8 @@
{
- "extends": "@tsconfig/recommended/tsconfig.json",
+ "extends": "./tsconfig.json",
"compilerOptions": {
- "module": "NodeNext",
- "moduleResolution": "NodeNext",
- "target": "esnext",
- "rootDir": "src",
- "outDir": "dist",
- "declaration": true,
- "sourceMap": true,
- "jsx": "react",
- "declarationMap": true,
- "types": ["react/canary", "node"],
- "esModuleInterop": false
+ "esModuleInterop": false,
+ "paths": {}
},
"include": ["src"]
}
diff --git a/packages/experimental-nextjs-app-support/package.json b/packages/experimental-nextjs-app-support/package.json
index 717d60e5..0165ccba 100644
--- a/packages/experimental-nextjs-app-support/package.json
+++ b/packages/experimental-nextjs-app-support/package.json
@@ -15,6 +15,22 @@
"app"
],
"type": "module",
+ "imports": {
+ "#bundled": {
+ "require": {
+ "types": "./dist/combined.d.cts",
+ "react-server": "./dist/index.rsc.cjs",
+ "browser": "./dist/index.browser.cjs",
+ "node": "./dist/index.ssr.cjs"
+ },
+ "import": {
+ "types": "./dist/combined.d.ts",
+ "react-server": "./dist/index.rsc.js",
+ "browser": "./dist/index.browser.js",
+ "node": "./dist/index.ssr.js"
+ }
+ }
+ },
"exports": {
".": {
"require": {
@@ -83,7 +99,11 @@
],
"scripts": {
"build": "rimraf dist; tsup",
- "test": "true",
+ "test": "concurrently -c auto \"yarn:test:*(!base) $@\"",
+ "test:base": "TSX_TSCONFIG_PATH=./tsconfig.tests.json node --import tsx/esm --no-warnings --test \"$@\" src/**/*.test.(ts|tsx)",
+ "test:ssr": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=node\" yarn run test:base",
+ "test:browser": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=browser\" yarn run test:base",
+ "test:rsc": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=react-server\" yarn run test:base",
"prepack": "yarn build",
"prepublishOnly": "yarn pack -o attw.tgz && attw attw.tgz && rm attw.tgz && yarn run test",
"test-bundle": "yarn test-bundle:attw && yarn test-bundle:package && yarn test-bundle:publint && yarn test-bundle:shape",
@@ -98,6 +118,7 @@
"@apollo/client": "3.10.4",
"@apollo/client-react-streaming": "workspace:*",
"@arethetypeswrong/cli": "0.15.3",
+ "@internal/test-utils": "workspace:^",
"@microsoft/api-extractor": "7.43.2",
"@testing-library/react": "15.0.7",
"@total-typescript/shoehorn": "0.1.2",
diff --git a/packages/experimental-nextjs-app-support/src/importErrors.test.tsx b/packages/experimental-nextjs-app-support/src/importErrors.test.tsx
new file mode 100644
index 00000000..b0605cb6
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/src/importErrors.test.tsx
@@ -0,0 +1,186 @@
+import * as assert from "node:assert";
+import { test } from "node:test";
+import { outsideOf } from "@internal/test-utils/runInConditions.js";
+import { browserEnv } from "@internal/test-utils/react.js";
+import { silenceConsoleErrors } from "@internal/test-utils/console.js";
+
+test("Error message when `WrappedApolloClient` is instantiated with wrong `InMemoryCache`", async () => {
+ const { ApolloClient } = await import("#bundled");
+ const upstreamPkg = await import("@apollo/client/index.js");
+ assert.throws(
+ () =>
+ new ApolloClient({
+ // @ts-expect-error this is what we're testing
+ cache: new upstreamPkg.InMemoryCache(),
+ connectToDevTools: false,
+ }),
+ {
+ message:
+ 'When using `InMemoryCache` in streaming SSR, you must use the `InMemoryCache` export provided by `"@apollo/experimental-nextjs-app-support"`.',
+ }
+ );
+});
+
+test(
+ "Error message when using `ApolloNextAppProvider` with the wrong `ApolloClient`",
+ { skip: outsideOf("node") },
+ async () => {
+ const { ApolloNextAppProvider, ...bundled } = await import("#bundled");
+ const { ServerInsertedHTMLContext } = await import("next/navigation.js");
+ const React = await import("react");
+ const { renderToString } = await import("react-dom/server");
+ await test("@apollo/client should error", async () => {
+ const upstreamPkg = await import("@apollo/client/index.js");
+ assert.throws(
+ () =>
+ renderToString(
+ {}}>
+
+ // @ts-expect-error we want to test exactly this
+ new upstreamPkg.ApolloClient({
+ cache: new upstreamPkg.InMemoryCache(),
+ })
+ }
+ >
+ {null}
+
+
+ ),
+ {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/experimental-nextjs-app-support"`.',
+ }
+ );
+ });
+ await test("@apollo/client-react-streaming should error", async () => {
+ const streamingPkg = await import("@apollo/client-react-streaming");
+ assert.throws(
+ () =>
+ renderToString(
+ {}}>
+
+ new streamingPkg.ApolloClient({
+ cache: new streamingPkg.InMemoryCache(),
+ })
+ }
+ >
+ {null}
+
+
+ ),
+ {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/experimental-nextjs-app-support"`.',
+ }
+ );
+ });
+ await test("this package should work", async () => {
+ renderToString(
+ {}}>
+
+ new bundled.ApolloClient({
+ cache: new bundled.InMemoryCache(),
+ })
+ }
+ >
+ {null}
+
+
+ );
+ });
+ }
+);
+
+test(
+ "Error message when using `ApolloNextAppProvider` with the wrong `ApolloClient`",
+ { skip: outsideOf("browser") },
+ async () => {
+ const { ApolloNextAppProvider, ...bundled } = await import("#bundled");
+ const React = await import("react");
+
+ const { ErrorBoundary } = await import("react-error-boundary");
+ // Even with an error Boundary, React will still log to `console.error` - we avoid the noise here.
+ using _restoreConsole = silenceConsoleErrors();
+
+ await test("@apollo/client should error", async () => {
+ using env = await browserEnv();
+ const upstreamPkg = await import("@apollo/client/index.js");
+ const promise = new Promise((_resolve, reject) => {
+ env.render(
+ document.body,
+ >}>
+
+ // @ts-expect-error we want to test exactly this
+ new upstreamPkg.ApolloClient({
+ cache: new upstreamPkg.InMemoryCache(),
+ connectToDevTools: false,
+ })
+ }
+ >
+ {null}
+
+
+ );
+ });
+ await assert.rejects(promise, {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/experimental-nextjs-app-support"`.',
+ });
+ });
+ await test("@apollo/client-react-streaming should error", async () => {
+ using env = await browserEnv();
+ const streamingPkg = await import("@apollo/client-react-streaming");
+ const promise = new Promise((_resolve, reject) => {
+ env.render(
+ document.body,
+ >}>
+
+ new streamingPkg.ApolloClient({
+ cache: new streamingPkg.InMemoryCache(),
+ connectToDevTools: false,
+ })
+ }
+ >
+ {null}
+
+
+ );
+ });
+ await assert.rejects(promise, {
+ message:
+ 'When using `ApolloClient` in streaming SSR, you must use the `ApolloClient` export provided by `"@apollo/experimental-nextjs-app-support"`.',
+ });
+ });
+ await test("this package should work", async () => {
+ using env = await browserEnv();
+ const promise = new Promise((resolve, reject) => {
+ function Child() {
+ resolve();
+ return null;
+ }
+ env.render(
+ document.body,
+ >}>
+
+ new bundled.ApolloClient({
+ cache: new bundled.InMemoryCache(),
+ connectToDevTools: false,
+ })
+ }
+ >
+ {}
+
+
+ );
+ });
+ // correct usage, should not throw
+ await promise;
+ });
+ }
+);
diff --git a/packages/experimental-nextjs-app-support/src/index.shared.ts b/packages/experimental-nextjs-app-support/src/index.shared.ts
index 9f55f932..ef4f5123 100644
--- a/packages/experimental-nextjs-app-support/src/index.shared.ts
+++ b/packages/experimental-nextjs-app-support/src/index.shared.ts
@@ -2,11 +2,13 @@ export {
SSRMultipartLink,
DebounceMultipartResponsesLink,
RemoveMultipartDirectivesLink,
- InMemoryCache,
type TransportedQueryRef,
} from "@apollo/client-react-streaming";
import { bundle } from "./bundleInfo.js";
-import { ApolloClient as UpstreamApolloClient } from "@apollo/client-react-streaming";
+import {
+ ApolloClient as UpstreamApolloClient,
+ InMemoryCache as UpstreamInMemoryCache,
+} from "@apollo/client-react-streaming";
/**
* A version of `ApolloClient` to be used with streaming SSR or in React Server Components.
@@ -25,3 +27,19 @@ export class ApolloClient<
*/
static readonly info = bundle;
}
+
+/**
+ * A version of `InMemoryCache` to be used with streaming SSR.
+ *
+ * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/cache/InMemoryCache | the Apollo Client API documentation}.
+ *
+ * @public
+ */
+export class InMemoryCache extends UpstreamInMemoryCache {
+ /**
+ * Information about the current package and it's export names, for use in error messages.
+ *
+ * @internal
+ */
+ static readonly info = bundle;
+}
diff --git a/packages/experimental-nextjs-app-support/tsconfig.tests.json b/packages/experimental-nextjs-app-support/tsconfig.tests.json
new file mode 100644
index 00000000..b3edf69b
--- /dev/null
+++ b/packages/experimental-nextjs-app-support/tsconfig.tests.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "esModuleInterop": false,
+ "paths": {}
+ },
+ "include": ["src"]
+}
diff --git a/packages/test-utils/console.d.ts b/packages/test-utils/console.d.ts
new file mode 100644
index 00000000..5e85d470
--- /dev/null
+++ b/packages/test-utils/console.d.ts
@@ -0,0 +1,3 @@
+export function silenceConsoleErrors(): {
+ [Symbol.dispose](): void;
+};
diff --git a/packages/test-utils/console.js b/packages/test-utils/console.js
new file mode 100644
index 00000000..d98f7ccb
--- /dev/null
+++ b/packages/test-utils/console.js
@@ -0,0 +1,9 @@
+export function silenceConsoleErrors() {
+ const { error } = console;
+ console.error = () => {};
+ return {
+ [Symbol.dispose]() {
+ console.error = error;
+ },
+ };
+}
diff --git a/packages/test-utils/hydrationTest.d.ts b/packages/test-utils/hydrationTest.d.ts
new file mode 100644
index 00000000..15ed80de
--- /dev/null
+++ b/packages/test-utils/hydrationTest.d.ts
@@ -0,0 +1,18 @@
+/** React completeSegment function */
+export function $RS(a: any, b: any): void;
+/** React completeBoundary function */
+export function $RC(b: any, c: any, e?: any): void;
+/**
+ *
+ * @param {Parameters[1]} initialChildren
+ * @param {Parameters[2]} [options]
+ */
+export function hydrateBody(initialChildren: [container: Element | Document, initialChildren: import("react").ReactNode, options?: import("react-dom/client").HydrationOptions][1], options?: [container: Element | Document, initialChildren: import("react").ReactNode, options?: import("react-dom/client").HydrationOptions][2]): import("react-dom/client").Root;
+/**
+ * @param {TemplateStringsArray} html
+ */
+export function setBody(html: TemplateStringsArray): void;
+/**
+ * @param {TemplateStringsArray} html
+ */
+export function appendToBody(html: TemplateStringsArray): void;
diff --git a/packages/client-react-streaming/src/util/hydrationTest.ts b/packages/test-utils/hydrationTest.js
similarity index 80%
rename from packages/client-react-streaming/src/util/hydrationTest.ts
rename to packages/test-utils/hydrationTest.js
index fe4f1829..6d37dc12 100644
--- a/packages/client-react-streaming/src/util/hydrationTest.ts
+++ b/packages/test-utils/hydrationTest.js
@@ -11,21 +11,29 @@ export function $RS(a, b) { a = document.getElementById(a); b = document.getElem
export function $RC(b, c, e = undefined) { c = document.getElementById(c); c.parentNode.removeChild(c); var a = document.getElementById(b); if (a) { b = a.previousSibling; if (e) b.data = "$!", a.setAttribute("data-dgst", e); else { e = b.parentNode; a = b.nextSibling; var f = 0; do { if (a && 8 === a.nodeType) { var d = a.data; if ("/$" === d) if (0 === f) break; else f--; else "$" !== d && "$?" !== d && "$!" !== d || f++ } d = a.nextSibling; e.removeChild(a); a = d } while (a); for (; c.firstChild;)e.insertBefore(c.firstChild, a); b.data = "$" } b._reactRetry && b._reactRetry() } }
/* eslint-enable */
-export function hydrateBody(
- initialChildren: Parameters[1],
- options?: Parameters[2]
-) {
+/**
+ *
+ * @param {Parameters[1]} initialChildren
+ * @param {Parameters[2]} [options]
+ */
+export function hydrateBody(initialChildren, options) {
return hydrateRoot(document.body, initialChildren, options);
}
-export function setBody(html: TemplateStringsArray) {
+/**
+ * @param {TemplateStringsArray} html
+ */
+export function setBody(html) {
if (html.length !== 1)
throw new Error("Expected exactly one template string");
// nosemgrep
document.body.innerHTML = html[0];
}
-export function appendToBody(html: TemplateStringsArray) {
+/**
+ * @param {TemplateStringsArray} html
+ */
+export function appendToBody(html) {
if (html.length !== 1)
throw new Error("Expected exactly one template string");
// nosemgrep
diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json
new file mode 100644
index 00000000..0c21b49d
--- /dev/null
+++ b/packages/test-utils/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@internal/test-utils",
+ "devDependencies": {
+ "typescript": "*"
+ },
+ "type": "module"
+}
diff --git a/packages/test-utils/react.d.ts b/packages/test-utils/react.d.ts
new file mode 100644
index 00000000..9a78c77e
--- /dev/null
+++ b/packages/test-utils/react.d.ts
@@ -0,0 +1,14 @@
+/**
+ * sets up a jsdom environment and returns a render function that can be used to render react components
+ */
+export function browserEnv(): Promise<{
+ /**
+ *
+ * @param {import('react-dom/client').Container} container
+ * @param {import('react').ReactNode} reactNode
+ * @param {import('react-dom/client').RootOptions} [rootOptions]
+ * @returns
+ */
+ render(container: import('react-dom/client').Container, reactNode: import('react').ReactNode, rootOptions?: import('react-dom/client').RootOptions): import("react-dom/client").Root;
+ [Symbol.dispose]: () => void;
+}>;
diff --git a/packages/test-utils/react.js b/packages/test-utils/react.js
new file mode 100644
index 00000000..ca25ae9a
--- /dev/null
+++ b/packages/test-utils/react.js
@@ -0,0 +1,34 @@
+/**
+ * sets up a jsdom environment and returns a render function that can be used to render react components
+ */
+export async function browserEnv() {
+ const { createRoot } = await import("react-dom/client");
+ const { act } = await import("react");
+ const jsdom = await import("global-jsdom");
+ const cleanupJSDOM = jsdom.default();
+ const origActEnv = globalThis.IS_REACT_ACT_ENVIRONMENT;
+
+ let lastRoot;
+
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
+ return {
+ /**
+ *
+ * @param {import('react-dom/client').Container} container
+ * @param {import('react').ReactNode} reactNode
+ * @param {import('react-dom/client').RootOptions} [rootOptions]
+ * @returns
+ */
+ render(container, reactNode, rootOptions) {
+ if (lastRoot) lastRoot.unmount();
+ lastRoot = createRoot(container, rootOptions);
+ act(() => lastRoot.render(reactNode));
+ return lastRoot;
+ },
+ [Symbol.dispose]: () => {
+ if (lastRoot) lastRoot.unmount();
+ globalThis.IS_REACT_ACT_ENVIRONMENT = origActEnv;
+ cleanupJSDOM();
+ },
+ };
+}
diff --git a/packages/test-utils/runInConditions.d.ts b/packages/test-utils/runInConditions.d.ts
new file mode 100644
index 00000000..44f56598
--- /dev/null
+++ b/packages/test-utils/runInConditions.d.ts
@@ -0,0 +1,15 @@
+/**
+ * @typedef {"react-server" | "node" | "browser" | "default"} Condition
+ */
+/**
+ * To be used in test files. This will skip the test if the node runner has not been started matching at least one of the passed conditions.
+ * If node has been started with `node --conditions=node --test`, and `runCondition("node", "browser")` is called, the test will run.
+ * If node has been started with `node --conditions=react-server --test`, and `runCondition("node", "browser")` is called, the test will not run.
+ * @param {Condition[]} validConditions
+ */
+export function runInConditions(...validConditions: Condition[]): void;
+/**
+ * @param {Condition[]} validConditions
+ */
+export function outsideOf(...validConditions: Condition[]): boolean;
+export type Condition = "react-server" | "node" | "browser" | "default";
diff --git a/packages/client-react-streaming/src/util/runInConditions.ts b/packages/test-utils/runInConditions.js
similarity index 73%
rename from packages/client-react-streaming/src/util/runInConditions.ts
rename to packages/test-utils/runInConditions.js
index 450175b3..b111ab9b 100644
--- a/packages/client-react-streaming/src/util/runInConditions.ts
+++ b/packages/test-utils/runInConditions.js
@@ -1,23 +1,30 @@
import { parseArgs } from "node:util";
-type Condition = "react-server" | "node" | "browser" | "default";
+/**
+ * @typedef {"react-server" | "node" | "browser" | "default"} Condition
+ */
/**
* To be used in test files. This will skip the test if the node runner has not been started matching at least one of the passed conditions.
* If node has been started with `node --conditions=node --test`, and `runCondition("node", "browser")` is called, the test will run.
* If node has been started with `node --conditions=react-server --test`, and `runCondition("node", "browser")` is called, the test will not run.
- * @param validConditions
+ * @param {Condition[]} validConditions
*/
-export function runInConditions(...validConditions: Condition[]) {
+export function runInConditions(...validConditions) {
if (!conditionActive(validConditions)) {
process.exit(0);
}
}
-export function outsideOf(...validConditions: Condition[]) {
+/**
+ * @param {Condition[]} validConditions
+ */
+export function outsideOf(...validConditions) {
return !conditionActive(validConditions);
}
-
-function conditionActive(validConditions: Condition[]) {
+/**
+ * @param {Condition[]} validConditions
+ */
+function conditionActive(validConditions) {
const args = parseArgs({
args: (process.env.NODE_OPTIONS || "").split(" ").concat(process.execArgv),
options: {
diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json
new file mode 100644
index 00000000..d8a6c927
--- /dev/null
+++ b/packages/test-utils/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "include": ["./*.js"],
+ "exclude": ["node_modules"],
+ "compilerOptions": {
+ "allowJs": true,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "outDir": ".",
+ "declarationMap": false
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 131d218f..e499c0c6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -51,6 +51,7 @@ __metadata:
dependencies:
"@apollo/client": "npm:^3.10.4"
"@arethetypeswrong/cli": "npm:0.15.3"
+ "@internal/test-utils": "workspace:^"
"@microsoft/api-extractor": "npm:7.43.2"
"@testing-library/react": "npm:15.0.7"
"@total-typescript/shoehorn": "npm:0.1.2"
@@ -129,6 +130,7 @@ __metadata:
"@apollo/client": "npm:3.10.4"
"@apollo/client-react-streaming": "workspace:*"
"@arethetypeswrong/cli": "npm:0.15.3"
+ "@internal/test-utils": "workspace:^"
"@microsoft/api-extractor": "npm:7.43.2"
"@testing-library/react": "npm:15.0.7"
"@total-typescript/shoehorn": "npm:0.1.2"
@@ -3623,6 +3625,14 @@ __metadata:
languageName: node
linkType: hard
+"@internal/test-utils@workspace:^, @internal/test-utils@workspace:packages/test-utils":
+ version: 0.0.0-use.local
+ resolution: "@internal/test-utils@workspace:packages/test-utils"
+ dependencies:
+ typescript: "npm:*"
+ languageName: unknown
+ linkType: soft
+
"@isaacs/cliui@npm:^8.0.2":
version: 8.0.2
resolution: "@isaacs/cliui@npm:8.0.2"
@@ -9537,16 +9547,7 @@ __metadata:
languageName: node
linkType: hard
-"get-tsconfig@npm:^4.2.0":
- version: 4.7.2
- resolution: "get-tsconfig@npm:4.7.2"
- dependencies:
- resolve-pkg-maps: "npm:^1.0.0"
- checksum: 10/f21135848fb5d16012269b7b34b186af7a41824830f8616aba17a15eb4d9e54fdc876833f1e21768395215a826c8145582f5acd594ae2b4de3284d10b38d20f8
- languageName: node
- linkType: hard
-
-"get-tsconfig@npm:^4.7.2":
+"get-tsconfig@npm:^4.2.0, get-tsconfig@npm:^4.7.2":
version: 4.7.5
resolution: "get-tsconfig@npm:4.7.5"
dependencies:
@@ -15346,6 +15347,16 @@ __metadata:
languageName: node
linkType: hard
+"typescript@npm:*, typescript@npm:5.4.5":
+ version: 5.4.5
+ resolution: "typescript@npm:5.4.5"
+ bin:
+ tsc: bin/tsc
+ tsserver: bin/tsserver
+ checksum: 10/d04a9e27e6d83861f2126665aa8d84847e8ebabcea9125b9ebc30370b98cb38b5dff2508d74e2326a744938191a83a69aa9fddab41f193ffa43eabfdf3f190a5
+ languageName: node
+ linkType: hard
+
"typescript@npm:5.3.3":
version: 5.3.3
resolution: "typescript@npm:5.3.3"
@@ -15366,13 +15377,13 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:5.4.5":
+"typescript@patch:typescript@npm%3A*#optional!builtin, typescript@patch:typescript@npm%3A5.4.5#optional!builtin":
version: 5.4.5
- resolution: "typescript@npm:5.4.5"
+ resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: 10/d04a9e27e6d83861f2126665aa8d84847e8ebabcea9125b9ebc30370b98cb38b5dff2508d74e2326a744938191a83a69aa9fddab41f193ffa43eabfdf3f190a5
+ checksum: 10/760f7d92fb383dbf7dee2443bf902f4365db2117f96f875cf809167f6103d55064de973db9f78fe8f31ec08fff52b2c969aee0d310939c0a3798ec75d0bca2e1
languageName: node
linkType: hard
@@ -15396,16 +15407,6 @@ __metadata:
languageName: node
linkType: hard
-"typescript@patch:typescript@npm%3A5.4.5#optional!builtin":
- version: 5.4.5
- resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c"
- bin:
- tsc: bin/tsc
- tsserver: bin/tsserver
- checksum: 10/760f7d92fb383dbf7dee2443bf902f4365db2117f96f875cf809167f6103d55064de973db9f78fe8f31ec08fff52b2c969aee0d310939c0a3798ec75d0bca2e1
- languageName: node
- linkType: hard
-
"ua-parser-js@npm:^0.7.30":
version: 0.7.35
resolution: "ua-parser-js@npm:0.7.35"
From 012afed0095e0900b04254b354567d0a8b57625d Mon Sep 17 00:00:00 2001
From: Lenz Weber-Tronic
Date: Wed, 29 May 2024 10:49:02 +0200
Subject: [PATCH 09/11] Add deprecation messages to moved exports. (#301)
* Add deprecation messages to moved exports.
* fixup
* update wording as per review
---
.../src/rsc/index.ts | 31 +++-
.../src/ssr/index.ts | 169 ++++++++++++++++--
2 files changed, 182 insertions(+), 18 deletions(-)
diff --git a/packages/experimental-nextjs-app-support/src/rsc/index.ts b/packages/experimental-nextjs-app-support/src/rsc/index.ts
index 5d684d3b..e33f76e0 100644
--- a/packages/experimental-nextjs-app-support/src/rsc/index.ts
+++ b/packages/experimental-nextjs-app-support/src/rsc/index.ts
@@ -1,4 +1,29 @@
-export {
- registerApolloClient,
- type TransportedQueryRef,
+import {
+ registerApolloClient as _registerApolloClient,
+ type TransportedQueryRef as _TransportedQueryRef,
} from "@apollo/experimental-nextjs-app-support";
+
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { registerApolloClient } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const registerApolloClient = _registerApolloClient;
+
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import type { TransportedQueryRef } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export type TransportedQueryRef<
+ TData = unknown,
+ TVariables = unknown,
+> = _TransportedQueryRef;
diff --git a/packages/experimental-nextjs-app-support/src/ssr/index.ts b/packages/experimental-nextjs-app-support/src/ssr/index.ts
index c1c625d2..474637e2 100644
--- a/packages/experimental-nextjs-app-support/src/ssr/index.ts
+++ b/packages/experimental-nextjs-app-support/src/ssr/index.ts
@@ -1,17 +1,156 @@
-export {
- InMemoryCache as NextSSRInMemoryCache,
- ApolloClient as NextSSRApolloClient,
- SSRMultipartLink,
- DebounceMultipartResponsesLink,
- RemoveMultipartDirectivesLink,
- ApolloNextAppProvider,
- resetApolloClientSingletons as resetNextSSRApolloSingletons,
- type TransportedQueryRef,
+import {
+ InMemoryCache,
+ ApolloClient,
+ resetApolloClientSingletons,
+ SSRMultipartLink as _SSRMultipartLink,
+ DebounceMultipartResponsesLink as _DebounceMultipartResponsesLink,
+ RemoveMultipartDirectivesLink as _RemoveMultipartDirectivesLink,
+ ApolloNextAppProvider as _ApolloNextAppProvider,
+ type TransportedQueryRef as _TransportedQueryRef,
} from "@apollo/experimental-nextjs-app-support";
-export {
- useBackgroundQuery,
- useFragment,
- useQuery,
- useReadQuery,
- useSuspenseQuery,
+import {
+ useBackgroundQuery as _useBackgroundQuery,
+ useFragment as _useFragment,
+ useQuery as _useQuery,
+ useReadQuery as _useReadQuery,
+ useSuspenseQuery as _useSuspenseQuery,
} from "@apollo/client/index.js";
+
+/**
+ * @deprecated
+ * This import has been renamed to `InMemoryCache` and moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { InMemoryCache } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const NextSSRInMemoryCache = InMemoryCache;
+/**
+ * @deprecated
+ * This import has been renamed to `ApolloClient` and moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { ApolloClient } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const NextSSRApolloClient = ApolloClient;
+/**
+ * @deprecated
+ * This import has been renamed to `resetApolloClientSingletons` and moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { resetApolloClientSingletons } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const resetNextSSRApolloSingletons = resetApolloClientSingletons;
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { SSRMultipartLink } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const SSRMultipartLink = _SSRMultipartLink;
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { DebounceMultipartResponsesLink } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const DebounceMultipartResponsesLink = _DebounceMultipartResponsesLink;
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { RemoveMultipartDirectivesLink } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const RemoveMultipartDirectivesLink = _RemoveMultipartDirectivesLink;
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import { ApolloNextAppProvider } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export const ApolloNextAppProvider = _ApolloNextAppProvider;
+/**
+ * @deprecated
+ * This import has moved to `"@apollo/experimental-nextjs-app-support"`.
+ *
+ * Please update your import to
+ * ```ts
+ * import type { TransportedQueryRef } from "@apollo/experimental-nextjs-app-support";
+ * ```
+ */
+export type TransportedQueryRef<
+ TData = unknown,
+ TVariables = unknown,
+> = _TransportedQueryRef;
+/**
+ * @deprecated
+ * Importing `useBackgroundQuery` from this package is no longer necessary.
+ * Import it directly from `@apollo/client` instead.
+
+ * Please update your import to
+ * ```ts
+ * import { useBackgroundQuery } from "@apollo/client";
+ * ```
+ */
+export const useBackgroundQuery = _useBackgroundQuery;
+/**
+ * @deprecated
+ * Importing `useFragment` from this package is no longer necessary.
+ * Import it directly from `@apollo/client` instead.
+
+ * Please update your import to
+ * ```ts
+ * import { useFragment } from "@apollo/client";
+ * ```
+ */
+export const useFragment = _useFragment;
+/**
+ * @deprecated
+ * Importing `useQuery` from this package is no longer necessary.
+ * Import it directly from `@apollo/client` instead.
+
+ * Please update your import to
+ * ```ts
+ * import { useQuery } from "@apollo/client";
+ * ```
+ */
+export const useQuery = _useQuery;
+/**
+ * @deprecated
+ * Importing `useReadQuery` from this package is no longer necessary.
+ * Import it directly from `@apollo/client` instead.
+
+ * Please update your import to
+ * ```ts
+ * import { useReadQuery } from "@apollo/client";
+ * ```
+ */
+export const useReadQuery = _useReadQuery;
+/**
+ * @deprecated
+ * Importing `useSuspenseQuery` from this package is no longer necessary.
+ * Import it directly from `@apollo/client` instead.
+
+ * Please update your import to
+ * ```ts
+ * import { useSuspenseQuery } from "@apollo/client";
+ * ```
+ */
+export const useSuspenseQuery = _useSuspenseQuery;
From 6db4732d9bbfea79a91f7aa951e279fa7e462ebb Mon Sep 17 00:00:00 2001
From: Lenz Weber-Tronic
Date: Wed, 29 May 2024 11:58:39 +0200
Subject: [PATCH 10/11] update README to include PreloadQuery (#303)
* update README
* anchor
* Apply suggestions from code review
Co-authored-by: Jerel Miller
* review feedback
* clarification
---------
Co-authored-by: Jerel Miller
---
.../experimental-nextjs-app-support/README.md | 97 ++++++++++++++++++-
1 file changed, 93 insertions(+), 4 deletions(-)
diff --git a/packages/experimental-nextjs-app-support/README.md b/packages/experimental-nextjs-app-support/README.md
index 1eadf21a..940d941e 100644
--- a/packages/experimental-nextjs-app-support/README.md
+++ b/packages/experimental-nextjs-app-support/README.md
@@ -9,8 +9,8 @@
> This cannot be addressed from our side, but would need API changes in Next.js or React itself.
> If you do not use suspense in your application, this will not be a problem to you.
-| ☑️ Apollo Client User Survey |
-| :----- |
+| ☑️ Apollo Client User Survey |
+| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| What do you like best about Apollo Client? What needs to be improved? Please tell us by taking a [one-minute survey](https://docs.google.com/forms/d/e/1FAIpQLSczNDXfJne3ZUOXjk9Ursm9JYvhTh1_nFTDfdq3XBAFWCzplQ/viewform?usp=pp_url&entry.1170701325=Apollo+Client&entry.204965213=Readme). Your responses will help us understand Apollo Client usage and allow us to serve you better. |
## Detailed technical breakdown
@@ -57,7 +57,7 @@ import {
InMemoryCache,
} from "@apollo/experimental-nextjs-app-support";
-export const { getClient } = registerApolloClient(() => {
+export const { getClient, query, PreloadQuery } = registerApolloClient(() => {
return new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
@@ -75,9 +75,13 @@ You can then use that `getClient` function in your server components:
```js
const { data } = await getClient().query({ query: userQuery });
+// `query` is a shortcut for `getClient().query`
+const { data } = await query({ query: userQuery });
```
-### In SSR
+For a description of `PreloadQuery`, see [Preloading data in RSC for usage in Client Components](#preloading-data-in-rsc-for-usage-in-client-components)
+
+### In Client Components and streaming SSR
If you use the `app` directory, each Client Component _will_ be SSR-rendered for the initial request. So you will need to use this package.
@@ -154,6 +158,91 @@ export default function RootLayout({
If you want to make the most of the streaming SSR features offered by React & the Next.js App Router, consider using the [`useSuspenseQuery`](https://www.apollographql.com/docs/react/api/react/hooks-experimental/#using-usesuspensequery_experimental) and [`useFragment`](https://www.apollographql.com/docs/react/api/react/hooks-experimental/#using-usefragment_experimental) hooks.
+### Preloading data in RSC for usage in Client Components
+
+Starting with version 0.11, you can preload data in RSC to populate the cache of your Client Components.
+
+For that, follow the setup steps for both RSC and Client Components as laid out in the last two paragraphs. Then you can use the `PreloadQuery` component in your React Server Components:
+
+```jsx
+
+ loading>}>
+
+
+
+```
+
+And you can use `useSuspenseQuery` in your `ClientChild` component with the same QUERY:
+
+```jsx
+"use client";
+
+import { useSuspenseQuery } from "@apollo/client";
+// ...
+
+export function ClientChild() {
+ const { data } = useSuspenseQuery(QUERY);
+ return ... ;
+}
+```
+
+> [!TIP]
+> The `Suspense` boundary here is optional and only for demonstration purposes to show that something suspenseful is going on.
+> Place `Suspense` boundaries at meaningful places in your UI, where they give your users the best user experience.
+
+This example will fetch a query in RSC, and then transport the data into the Client Component cache.
+Before the child `ClientChild` in the example renders, a "simulated network request" for this query is started in your Client Components.
+That way, if you repeat the query in your Client Component using `useSuspenseQuery` (or even `useQuery`!), it will wait for the network request in your Server Component to finish instead of making it's own network request.
+
+> [!IMPORTANT]
+> Keep in mind that we don't recommend mixing data between Client Components and Server Components. Data fetched this way should be considered client data and never be referenced in your Server Components. `PreloadQuery` prevents mixing server data and client data by creating a separate `ApolloClient` instance using the `makeClient` function passed into `registerApolloClient`.
+
+#### Usage with `useReadQuery`.
+
+You can also use this approach in combination with `useReadQuery` in Client Components. Use the render prop approach to get a `QueryRef` that you can pass to your Client Component:
+
+```jsx
+
+ {(queryRef) => (
+ loading>}>
+
+
+ )}
+
+```
+
+Inside of `ClientChild`, you could then call `useReadQuery` with the `queryRef` prop.
+
+```jsx
+"use client";
+
+import { useQueryRefHandlers, useReadQuery, QueryRef } from "@apollo/client";
+
+export function ClientChild({ queryRef }: { queryRef: QueryRef }) {
+ const { refetch } = useQueryRefHandlers(queryRef);
+ const { data } = useReadQuery(queryRef);
+ return ... ;
+}
+```
+
+> [!TIP]
+> The `Suspense` boundary here is optional and only for demonstration purposes to show that something suspenseful is going on.
+> Place `Suspense` boundaries at meaningful places in your UI, where they give your users the best user experience.
+
+#### Caveat
+
+Keep in mind that this will look like a "current network request" to your Client Component and as such will update data that is already in your Client Component cache, so make sure that the data you pass from your Server Components is not outdated, e.g. because of other caching layers you might be using, like the Next.js fetch cache.
+
### Resetting singletons between tests.
This package uses some singleton instances on the Browser side - if you are writing tests, you must reset them between tests.
From 6e76e6dd5dbcba5c0cc406d945e107ba1c06b0e0 Mon Sep 17 00:00:00 2001
From: Lenz Weber-Tronic
Date: Wed, 29 May 2024 12:20:06 +0200
Subject: [PATCH 11/11] adjust docblock for api-extractor
---
.../src/ApolloNextAppProvider.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts b/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
index b1d3c9c1..d2945cfb 100644
--- a/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
+++ b/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts
@@ -13,7 +13,7 @@ import { bundle } from "./bundleInfo.js";
* but requires a `makeClient` prop instead.
*
* Use this component together with `ApolloClient` and `InMemoryCache`
- * from the "@apollo/experimental-nextjs-app-support" package
+ * from the `"@apollo/experimental-nextjs-app-support"` package
* to make an ApolloClient instance available to your Client Component hooks in the
* Next.js App Router.
*
|