From 162b0cb58c4bf1793297523b294d97cd54bfb6ee Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Mar 2025 15:29:49 +0100 Subject: [PATCH 01/11] update Apollo Client dev dependencies --- examples/app-dir-experiments/package.json | 2 +- examples/hack-the-supergraph-ssr/package.json | 2 +- examples/polls-demo/package.json | 2 +- integration-test/jest/package.json | 2 +- integration-test/nextjs/package.json | 2 +- integration-test/react-router/package.json | 2 +- integration-test/tanstack-start/package.json | 2 +- integration-test/vite-streaming/package.json | 2 +- integration-test/vitest/package.json | 2 +- packages/client-react-streaming/package.json | 2 +- .../package.json | 2 +- packages/nextjs/package.json | 2 +- packages/react-router/package.json | 2 +- packages/tanstack-start/package.json | 2 +- yarn.lock | 36 +++++++++---------- 15 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/app-dir-experiments/package.json b/examples/app-dir-experiments/package.json index b7c5c82f..5cfdd8de 100644 --- a/examples/app-dir-experiments/package.json +++ b/examples/app-dir-experiments/package.json @@ -10,7 +10,7 @@ "lint": "next lint" }, "dependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@apollo/server": "^4.9.5", "@as-integrations/next": "^3.0.0", diff --git a/examples/hack-the-supergraph-ssr/package.json b/examples/hack-the-supergraph-ssr/package.json index 46d5f4c4..f5742d79 100644 --- a/examples/hack-the-supergraph-ssr/package.json +++ b/examples/hack-the-supergraph-ssr/package.json @@ -10,7 +10,7 @@ "lint": "next lint" }, "dependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@apollo/space-kit": "^9.11.0", "@chakra-ui/next-js": "2.4.2", diff --git a/examples/polls-demo/package.json b/examples/polls-demo/package.json index 7b8d1c77..a930151e 100644 --- a/examples/polls-demo/package.json +++ b/examples/polls-demo/package.json @@ -11,7 +11,7 @@ "codegen": "graphql-codegen --config codegen.ts" }, "dependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@apollo/server": "^4.9.5", "@types/node": "20.12.11", diff --git a/integration-test/jest/package.json b/integration-test/jest/package.json index 07f7c757..a34ec465 100644 --- a/integration-test/jest/package.json +++ b/integration-test/jest/package.json @@ -6,7 +6,7 @@ "test": "jest" }, "dependencies": { - "@apollo/client": "^3.11.10", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@apollo/client-react-streaming": "workspace:^", "@graphql-tools/schema": "^10.0.3", diff --git a/integration-test/nextjs/package.json b/integration-test/nextjs/package.json index 19e28cb4..4bd5bf3c 100644 --- a/integration-test/nextjs/package.json +++ b/integration-test/nextjs/package.json @@ -10,7 +10,7 @@ "test": "yarn test:nextjs" }, "dependencies": { - "@apollo/client": "^3.11.10", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@apollo/server": "^4.11.2", "@as-integrations/next": "^3.2.0", diff --git a/integration-test/react-router/package.json b/integration-test/react-router/package.json index f7e0551b..d0cba06d 100644 --- a/integration-test/react-router/package.json +++ b/integration-test/react-router/package.json @@ -11,7 +11,7 @@ "test": "yarn test:react-router" }, "dependencies": { - "@apollo/client": "^3.11.10", + "@apollo/client": "^3.13.4", "@apollo/client-integration-react-router": "workspace:^", "@integration-test/shared": "workspace:^", "@react-router/fs-routes": "^7.2.0-pre.3", diff --git a/integration-test/tanstack-start/package.json b/integration-test/tanstack-start/package.json index 64427754..30d525cc 100644 --- a/integration-test/tanstack-start/package.json +++ b/integration-test/tanstack-start/package.json @@ -10,7 +10,7 @@ "test": "yarn test:tanstack" }, "dependencies": { - "@apollo/client": "^3.12.4", + "@apollo/client": "^3.13.4", "@apollo/client-integration-tanstack-start": "workspace:^", "@integration-test/shared": "workspace:^", "@tanstack/router-devtools": "^1.99.0", diff --git a/integration-test/vite-streaming/package.json b/integration-test/vite-streaming/package.json index 484369ac..b7ca0300 100644 --- a/integration-test/vite-streaming/package.json +++ b/integration-test/vite-streaming/package.json @@ -13,7 +13,7 @@ "test": "yarn playwright test" }, "dependencies": { - "@apollo/client": "^3.11.10", + "@apollo/client": "^3.13.4", "@apollo/client-react-streaming": "workspace:^", "compression": "^1.7.4", "express": "^4.18.2", diff --git a/integration-test/vitest/package.json b/integration-test/vitest/package.json index 74de012a..613e7fb4 100644 --- a/integration-test/vitest/package.json +++ b/integration-test/vitest/package.json @@ -6,7 +6,7 @@ "test": "vitest" }, "dependencies": { - "@apollo/client": "^3.11.10", + "@apollo/client": "^3.13.4", "@apollo/client-integration-nextjs": "workspace:^", "@graphql-tools/schema": "^10.0.3", "graphql-tag": "^2.12.6", diff --git a/packages/client-react-streaming/package.json b/packages/client-react-streaming/package.json index e28af704..dbcf32e7 100644 --- a/packages/client-react-streaming/package.json +++ b/packages/client-react-streaming/package.json @@ -131,7 +131,7 @@ "lint": "eslint --ext .ts,.tsx src" }, "devDependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@arethetypeswrong/cli": "0.15.3", "@internal/test-utils": "workspace:^", "@microsoft/api-extractor": "7.43.2", diff --git a/packages/experimental-nextjs-app-support/package.json b/packages/experimental-nextjs-app-support/package.json index 31142f11..cd0afa88 100644 --- a/packages/experimental-nextjs-app-support/package.json +++ b/packages/experimental-nextjs-app-support/package.json @@ -80,7 +80,7 @@ "bundle-info": "yarn test-bundle --format json | jq '.analysis.entrypoints|to_entries|map({key:.key,value:.value.resolutions|to_entries|map({key:.key,value:.value.resolution.fileName })|from_entries})|from_entries'" }, "devDependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@arethetypeswrong/cli": "0.15.3", "@tsconfig/recommended": "1.0.6", "@types/react": "*", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index a599d286..6af67e50 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -77,7 +77,7 @@ "lint": "eslint --ext .ts,.tsx ." }, "devDependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-react-streaming": "workspace:*", "@arethetypeswrong/cli": "0.15.3", "@internal/test-utils": "workspace:^", diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 741fe2fc..c64536e2 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -64,7 +64,7 @@ "lint": "eslint --ext .ts,.tsx ." }, "devDependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-react-streaming": "workspace:*", "@arethetypeswrong/cli": "0.15.3", "@microsoft/api-extractor": "7.43.2", diff --git a/packages/tanstack-start/package.json b/packages/tanstack-start/package.json index d52832cb..252203ad 100644 --- a/packages/tanstack-start/package.json +++ b/packages/tanstack-start/package.json @@ -64,7 +64,7 @@ "lint": "eslint --ext .ts,.tsx ." }, "devDependencies": { - "@apollo/client": "^3.12.6", + "@apollo/client": "^3.13.4", "@apollo/client-react-streaming": "workspace:*", "@arethetypeswrong/cli": "0.15.3", "@internal/test-utils": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 28e8640a..0d335c33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -207,7 +207,7 @@ __metadata: version: 0.0.0-use.local resolution: "@apollo/client-integration-nextjs@workspace:packages/nextjs" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-react-streaming": "workspace:*" "@arethetypeswrong/cli": "npm:0.15.3" "@internal/test-utils": "workspace:^" @@ -240,7 +240,7 @@ __metadata: version: 0.0.0-use.local resolution: "@apollo/client-integration-react-router@workspace:packages/react-router" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-react-streaming": "workspace:*" "@arethetypeswrong/cli": "npm:0.15.3" "@microsoft/api-extractor": "npm:7.43.2" @@ -272,7 +272,7 @@ __metadata: version: 0.0.0-use.local resolution: "@apollo/client-integration-tanstack-start@workspace:packages/tanstack-start" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-react-streaming": "workspace:*" "@arethetypeswrong/cli": "npm:0.15.3" "@internal/test-utils": "workspace:^" @@ -311,7 +311,7 @@ __metadata: version: 0.0.0-use.local resolution: "@apollo/client-react-streaming@workspace:packages/client-react-streaming" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@arethetypeswrong/cli": "npm:0.15.3" "@internal/test-utils": "workspace:^" "@microsoft/api-extractor": "npm:7.43.2" @@ -347,9 +347,9 @@ __metadata: languageName: unknown linkType: soft -"@apollo/client@npm:^3.11.10, @apollo/client@npm:^3.12.4, @apollo/client@npm:^3.12.6": - version: 3.13.0 - resolution: "@apollo/client@npm:3.13.0" +"@apollo/client@npm:^3.13.4": + version: 3.13.4 + resolution: "@apollo/client@npm:3.13.4" dependencies: "@graphql-typed-document-node/core": "npm:^3.1.1" "@wry/caches": "npm:^1.0.0" @@ -379,7 +379,7 @@ __metadata: optional: true subscriptions-transport-ws: optional: true - checksum: 10/396715aae8b7d07f7b32443e6442e2a6c7c6ac4ab3682b8820c4544c96ba2e07bc9502e41d5b5dcf9d24c255556a1abb2baa7029b88a656819b139a2b1eea355 + checksum: 10/35ddc4bc66c5fdd5f1561e5a3c36759f1ddba10de020441f7594d63ea72300ff75813878f17677b4e06a30749ce255f7f66c25a0af718d407ff1a51abf7205fd languageName: node linkType: hard @@ -387,7 +387,7 @@ __metadata: version: 0.0.0-use.local resolution: "@apollo/experimental-nextjs-app-support@workspace:packages/experimental-nextjs-app-support" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "npm:0.12.0-alpha.2" "@arethetypeswrong/cli": "npm:0.15.3" "@tsconfig/recommended": "npm:1.0.6" @@ -6726,7 +6726,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/jest@workspace:integration-test/jest" dependencies: - "@apollo/client": "npm:^3.11.10" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@apollo/client-react-streaming": "workspace:^" "@babel/core": "npm:^7.24.0" @@ -6753,7 +6753,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/nextjs@workspace:integration-test/nextjs" dependencies: - "@apollo/client": "npm:^3.11.10" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@apollo/server": "npm:^4.11.2" "@as-integrations/next": "npm:^3.2.0" @@ -6788,7 +6788,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/react-router@workspace:integration-test/react-router" dependencies: - "@apollo/client": "npm:^3.11.10" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-react-router": "workspace:^" "@integration-test/shared": "workspace:^" "@react-router/dev": "npm:^7.2.0-pre.3" @@ -6831,7 +6831,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/tanstack-start@workspace:integration-test/tanstack-start" dependencies: - "@apollo/client": "npm:^3.12.4" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-tanstack-start": "workspace:^" "@integration-test/shared": "workspace:^" "@tanstack/router-devtools": "npm:^1.99.0" @@ -6852,7 +6852,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/vite-streaming@workspace:integration-test/vite-streaming" dependencies: - "@apollo/client": "npm:^3.11.10" + "@apollo/client": "npm:^3.13.4" "@apollo/client-react-streaming": "workspace:^" "@playwright/test": "npm:^1.49.1" "@tsconfig/vite-react": "npm:^3.0.0" @@ -6876,7 +6876,7 @@ __metadata: version: 0.0.0-use.local resolution: "@integration-test/vitest@workspace:integration-test/vitest" dependencies: - "@apollo/client": "npm:^3.11.10" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@graphql-tools/schema": "npm:^10.0.3" "@testing-library/jest-dom": "npm:^6.4.2" @@ -12385,7 +12385,7 @@ __metadata: version: 0.0.0-use.local resolution: "apollo-next-13-demo@workspace:examples/polls-demo" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@apollo/server": "npm:^4.9.5" "@graphql-codegen/cli": "npm:3.3.1" @@ -12418,7 +12418,7 @@ __metadata: version: 0.0.0-use.local resolution: "app-dir@workspace:examples/app-dir-experiments" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@apollo/server": "npm:^4.9.5" "@as-integrations/next": "npm:^3.0.0" @@ -18105,7 +18105,7 @@ __metadata: version: 0.0.0-use.local resolution: "hack-the-supergraph-ssr@workspace:examples/hack-the-supergraph-ssr" dependencies: - "@apollo/client": "npm:^3.12.6" + "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-nextjs": "workspace:^" "@apollo/space-kit": "npm:^9.11.0" "@chakra-ui/next-js": "npm:2.4.2" From cc7f36d259cf057ee10384d351452adea48b69e7 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Mar 2025 15:40:42 +0100 Subject: [PATCH 02/11] update integration test imports to work with AC4.0 --- integration-test/jest/src/App.jsx | 8 +--- .../react-router/app/entry.client.tsx | 2 +- .../react-router/app/entry.server.tsx | 2 +- .../react-router/app/routes/_index.tsx | 2 +- .../preloadQuery.queryRef-useReadQuery.tsx | 4 +- .../routes/preloadQuery.useSuspenseQuery.tsx | 16 ++------ .../shared/IncrementalSchemaLink.ts | 4 +- integration-test/shared/errorLink.tsx | 2 +- integration-test/shared/queries.ts | 2 +- .../app/routes/loader-defer.tsx | 2 +- .../preloadQuery/queryRef-useReadQuery.tsx | 4 +- .../routes/preloadQuery/useSuspenseQuery.tsx | 3 +- .../app/routes/useBackgroundQuery.tsx | 2 +- .../useBackgroundQueryWithoutSsrReadQuery.tsx | 2 +- .../tanstack-start/app/routes/useQuery.tsx | 2 +- .../app/routes/useQueryWithCache.tsx | 2 +- .../app/routes/useSuspenseQuery-defer.tsx | 41 ++++++++++--------- .../app/routes/useSuspenseQuery.tsx | 3 +- integration-test/vite-streaming/src/App.tsx | 2 +- integration-test/vitest/src/App.jsx | 8 +--- 20 files changed, 51 insertions(+), 62 deletions(-) diff --git a/integration-test/jest/src/App.jsx b/integration-test/jest/src/App.jsx index e9e43c49..0cb6c9a3 100644 --- a/integration-test/jest/src/App.jsx +++ b/integration-test/jest/src/App.jsx @@ -5,12 +5,8 @@ import { InMemoryCache, } from "@apollo/client-integration-nextjs"; import { SchemaLink } from "@apollo/client/link/schema/index.js"; -import { - useSuspenseQuery, - gql, - ApolloLink, - Observable, -} from "@apollo/client/index.js"; +import { gql, ApolloLink, Observable } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import { schema } from "./schema"; const delayLink = new ApolloLink((operation, forward) => { diff --git a/integration-test/react-router/app/entry.client.tsx b/integration-test/react-router/app/entry.client.tsx index 4f23189b..34777a8f 100644 --- a/integration-test/react-router/app/entry.client.tsx +++ b/integration-test/react-router/app/entry.client.tsx @@ -2,7 +2,7 @@ import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; import { makeClient } from "./apollo"; -import { ApolloProvider } from "@apollo/client/index.js"; +import { ApolloProvider } from "@apollo/client/react/index.js"; startTransition(() => { const client = makeClient(); diff --git a/integration-test/react-router/app/entry.server.tsx b/integration-test/react-router/app/entry.server.tsx index 7eb35026..201ab103 100644 --- a/integration-test/react-router/app/entry.server.tsx +++ b/integration-test/react-router/app/entry.server.tsx @@ -10,7 +10,7 @@ import type { } from "react-dom/server"; import { renderToPipeableStream } from "react-dom/server"; import { makeClient } from "./apollo"; -import { ApolloProvider } from "@apollo/client/index.js"; +import { ApolloProvider } from "@apollo/client/react/index.js"; export const streamTimeout = 5_000; export type RenderOptions = { diff --git a/integration-test/react-router/app/routes/_index.tsx b/integration-test/react-router/app/routes/_index.tsx index 0eccabcb..57aff9b3 100644 --- a/integration-test/react-router/app/routes/_index.tsx +++ b/integration-test/react-router/app/routes/_index.tsx @@ -4,7 +4,7 @@ import { useApolloClient, useQueryRefHandlers, useReadQuery, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; import { apolloLoader } from "~/apollo"; import { DEFERRED_QUERY } from "@integration-test/shared/queries"; import { useTransition } from "react"; diff --git a/integration-test/react-router/app/routes/preloadQuery.queryRef-useReadQuery.tsx b/integration-test/react-router/app/routes/preloadQuery.queryRef-useReadQuery.tsx index b13f57c6..61774bd9 100644 --- a/integration-test/react-router/app/routes/preloadQuery.queryRef-useReadQuery.tsx +++ b/integration-test/react-router/app/routes/preloadQuery.queryRef-useReadQuery.tsx @@ -4,8 +4,8 @@ import { useQueryRefHandlers, useReadQuery, type QueryRef, - type DefaultContext, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; +import { type DefaultContext } from "@apollo/client/index.js"; import { apolloLoader } from "~/apollo"; import { QUERY, diff --git a/integration-test/react-router/app/routes/preloadQuery.useSuspenseQuery.tsx b/integration-test/react-router/app/routes/preloadQuery.useSuspenseQuery.tsx index d550eb7a..19586e0d 100644 --- a/integration-test/react-router/app/routes/preloadQuery.useSuspenseQuery.tsx +++ b/integration-test/react-router/app/routes/preloadQuery.useSuspenseQuery.tsx @@ -1,17 +1,9 @@ -import { useLoaderData, useSearchParams } from "react-router"; +import { useSearchParams } from "react-router"; import type { Route } from "./+types/preloadQuery.useSuspenseQuery"; -import { - useQueryRefHandlers, - useReadQuery, - type QueryRef, - type DefaultContext, - useSuspenseQuery, -} from "@apollo/client/index.js"; +import { type DefaultContext } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import { apolloLoader } from "~/apollo"; -import { - QUERY, - type DynamicProductResult, -} from "@integration-test/shared/queries"; +import { QUERY } from "@integration-test/shared/queries"; import { Suspense } from "react"; export const loader = apolloLoader()(({ diff --git a/integration-test/shared/IncrementalSchemaLink.ts b/integration-test/shared/IncrementalSchemaLink.ts index 09caea8c..4de38de2 100644 --- a/integration-test/shared/IncrementalSchemaLink.ts +++ b/integration-test/shared/IncrementalSchemaLink.ts @@ -1,8 +1,8 @@ import { ApolloLink, - FetchResult, + type FetchResult, Observable, - Operation, + type Operation, } from "@apollo/client/index.js"; import type { SchemaLink } from "@apollo/client/link/schema"; import { experimentalExecuteIncrementally, validate } from "graphql"; diff --git a/integration-test/shared/errorLink.tsx b/integration-test/shared/errorLink.tsx index 21afb259..aed8ff15 100644 --- a/integration-test/shared/errorLink.tsx +++ b/integration-test/shared/errorLink.tsx @@ -1,5 +1,5 @@ import { ApolloLink, Observable } from "@apollo/client/index.js"; -import { GraphQLError, GraphQLFormattedError } from "graphql"; +import { GraphQLError, type GraphQLFormattedError } from "graphql"; import * as entryPoint from "@apollo/client-react-streaming"; declare module "@apollo/client" { diff --git a/integration-test/shared/queries.ts b/integration-test/shared/queries.ts index 66ba3bc9..69803971 100644 --- a/integration-test/shared/queries.ts +++ b/integration-test/shared/queries.ts @@ -1,4 +1,4 @@ -import { TypedDocumentNode, gql } from "@apollo/client/index.js"; +import { type TypedDocumentNode, gql } from "@apollo/client/index.js"; export interface DynamicProductResult { products: { diff --git a/integration-test/tanstack-start/app/routes/loader-defer.tsx b/integration-test/tanstack-start/app/routes/loader-defer.tsx index cf366fd1..40bc4848 100644 --- a/integration-test/tanstack-start/app/routes/loader-defer.tsx +++ b/integration-test/tanstack-start/app/routes/loader-defer.tsx @@ -4,7 +4,7 @@ import { useApolloClient, useQueryRefHandlers, useReadQuery, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; import { useTransition } from "react"; export const Route = createFileRoute("/loader-defer")({ diff --git a/integration-test/tanstack-start/app/routes/preloadQuery/queryRef-useReadQuery.tsx b/integration-test/tanstack-start/app/routes/preloadQuery/queryRef-useReadQuery.tsx index b15647bc..fb388711 100644 --- a/integration-test/tanstack-start/app/routes/preloadQuery/queryRef-useReadQuery.tsx +++ b/integration-test/tanstack-start/app/routes/preloadQuery/queryRef-useReadQuery.tsx @@ -3,8 +3,8 @@ import { QueryRef, useQueryRefHandlers, useReadQuery, - type DefaultContext, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; +import { type DefaultContext } from "@apollo/client/index.js"; import "@integration-test/shared/errorLink"; import { createFileRoute, ErrorComponentProps } from "@tanstack/react-router"; import { Suspense } from "react"; diff --git a/integration-test/tanstack-start/app/routes/preloadQuery/useSuspenseQuery.tsx b/integration-test/tanstack-start/app/routes/preloadQuery/useSuspenseQuery.tsx index 4fc3c2c6..7d0f41d1 100644 --- a/integration-test/tanstack-start/app/routes/preloadQuery/useSuspenseQuery.tsx +++ b/integration-test/tanstack-start/app/routes/preloadQuery/useSuspenseQuery.tsx @@ -1,5 +1,6 @@ import { QUERY } from "@integration-test/shared/queries"; -import { useSuspenseQuery, type DefaultContext } from "@apollo/client/index.js"; +import { type DefaultContext } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import "@integration-test/shared/errorLink"; import { createFileRoute } from "@tanstack/react-router"; import { Suspense } from "react"; diff --git a/integration-test/tanstack-start/app/routes/useBackgroundQuery.tsx b/integration-test/tanstack-start/app/routes/useBackgroundQuery.tsx index 61876dae..641bfecc 100644 --- a/integration-test/tanstack-start/app/routes/useBackgroundQuery.tsx +++ b/integration-test/tanstack-start/app/routes/useBackgroundQuery.tsx @@ -2,7 +2,7 @@ import { QueryRef, useBackgroundQuery, useReadQuery, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; import { DynamicProductResult, QUERY } from "@integration-test/shared/queries"; import { createFileRoute } from "@tanstack/react-router"; import { Suspense } from "react"; diff --git a/integration-test/tanstack-start/app/routes/useBackgroundQueryWithoutSsrReadQuery.tsx b/integration-test/tanstack-start/app/routes/useBackgroundQueryWithoutSsrReadQuery.tsx index 0eb68870..110548b9 100644 --- a/integration-test/tanstack-start/app/routes/useBackgroundQueryWithoutSsrReadQuery.tsx +++ b/integration-test/tanstack-start/app/routes/useBackgroundQueryWithoutSsrReadQuery.tsx @@ -2,7 +2,7 @@ import { QueryRef, useBackgroundQuery, useReadQuery, -} from "@apollo/client/index.js"; +} from "@apollo/client/react/index.js"; import { DynamicProductResult, QUERY } from "@integration-test/shared/queries"; import { createFileRoute } from "@tanstack/react-router"; import { Suspense, useSyncExternalStore } from "react"; diff --git a/integration-test/tanstack-start/app/routes/useQuery.tsx b/integration-test/tanstack-start/app/routes/useQuery.tsx index e363a15a..7a087582 100644 --- a/integration-test/tanstack-start/app/routes/useQuery.tsx +++ b/integration-test/tanstack-start/app/routes/useQuery.tsx @@ -1,4 +1,4 @@ -import { useQuery } from "@apollo/client/index.js"; +import { useQuery } from "@apollo/client/react/index.js"; import { QUERY } from "@integration-test/shared/queries"; import { createFileRoute } from "@tanstack/react-router"; diff --git a/integration-test/tanstack-start/app/routes/useQueryWithCache.tsx b/integration-test/tanstack-start/app/routes/useQueryWithCache.tsx index d3962de1..8673bcb0 100644 --- a/integration-test/tanstack-start/app/routes/useQueryWithCache.tsx +++ b/integration-test/tanstack-start/app/routes/useQueryWithCache.tsx @@ -1,4 +1,4 @@ -import { useQuery, useSuspenseQuery } from "@apollo/client/index.js"; +import { useQuery, useSuspenseQuery } from "@apollo/client/react/index.js"; import { QUERY } from "@integration-test/shared/queries"; import { createFileRoute } from "@tanstack/react-router"; diff --git a/integration-test/tanstack-start/app/routes/useSuspenseQuery-defer.tsx b/integration-test/tanstack-start/app/routes/useSuspenseQuery-defer.tsx index 1204f8ea..1760b67a 100644 --- a/integration-test/tanstack-start/app/routes/useSuspenseQuery-defer.tsx +++ b/integration-test/tanstack-start/app/routes/useSuspenseQuery-defer.tsx @@ -1,18 +1,21 @@ -import { createFileRoute } from '@tanstack/react-router' -import { DEFERRED_QUERY } from '@integration-test/shared/queries' -import { useApolloClient, useSuspenseQuery } from '@apollo/client/index.js' -import { useTransition } from 'react' +import { createFileRoute } from "@tanstack/react-router"; +import { DEFERRED_QUERY } from "@integration-test/shared/queries"; +import { + useApolloClient, + useSuspenseQuery, +} from "@apollo/client/react/index.js"; +import { useTransition } from "react"; -export const Route = createFileRoute('/useSuspenseQuery-defer')({ +export const Route = createFileRoute("/useSuspenseQuery-defer")({ component: RouteComponent, -}) +}); function RouteComponent() { - const [refetching, startTransition] = useTransition() - const client = useApolloClient() + const [refetching, startTransition] = useTransition(); + const client = useApolloClient(); const { data, refetch } = useSuspenseQuery(DEFERRED_QUERY, { variables: { delayDeferred: 1000 }, - }) + }); return ( <> @@ -21,11 +24,11 @@ function RouteComponent() {
  • {title}
    - Rating:{' '} -
    - {rating?.value || ''} + Rating:{" "} +
    + {rating?.value || ""}
    - {rating ? `Queried in ${rating.env} environment` : 'loading...'} + {rating ? `Queried in ${rating.env} environment` : "loading..."}
  • ))} @@ -42,17 +45,17 @@ function RouteComponent() { fields: { rating: () => null, }, - }) + }); } }, - }) + }); startTransition(() => { - refetch() - }) + refetch(); + }); }} > - {refetching ? 'refetching...' : 'refetch'} + {refetching ? "refetching..." : "refetch"} - ) + ); } diff --git a/integration-test/tanstack-start/app/routes/useSuspenseQuery.tsx b/integration-test/tanstack-start/app/routes/useSuspenseQuery.tsx index 6a035023..a7c74618 100644 --- a/integration-test/tanstack-start/app/routes/useSuspenseQuery.tsx +++ b/integration-test/tanstack-start/app/routes/useSuspenseQuery.tsx @@ -2,7 +2,8 @@ import { createFileRoute } from "@tanstack/react-router"; import { ErrorBoundary, FallbackProps } from "react-error-boundary"; import { QUERY } from "@integration-test/shared/queries"; -import { DefaultContext, useSuspenseQuery } from "@apollo/client/index.js"; +import { DefaultContext } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import { Suspense } from "react"; export const Route = createFileRoute("/useSuspenseQuery")({ diff --git a/integration-test/vite-streaming/src/App.tsx b/integration-test/vite-streaming/src/App.tsx index 6b396114..b3b7a049 100644 --- a/integration-test/vite-streaming/src/App.tsx +++ b/integration-test/vite-streaming/src/App.tsx @@ -7,8 +7,8 @@ import { ApolloLink, Observable, TypedDocumentNode, - useSuspenseQuery, } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import { schema } from "./schema"; import { WrappedApolloProvider } from "./Transport"; diff --git a/integration-test/vitest/src/App.jsx b/integration-test/vitest/src/App.jsx index 131446de..b4af1f94 100644 --- a/integration-test/vitest/src/App.jsx +++ b/integration-test/vitest/src/App.jsx @@ -5,12 +5,8 @@ import { InMemoryCache, } from "@apollo/client-integration-nextjs"; import { SchemaLink } from "@apollo/client/link/schema/index.js"; -import { - useSuspenseQuery, - gql, - ApolloLink, - Observable, -} from "@apollo/client/index.js"; +import { gql, ApolloLink, Observable } from "@apollo/client/index.js"; +import { useSuspenseQuery } from "@apollo/client/react/index.js"; import { schema } from "./schema"; const delayLink = new ApolloLink((operation, forward) => { From 81bfedf9c908e42f9266f97ddec38ebe9d56760f Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Mar 2025 18:05:22 +0100 Subject: [PATCH 03/11] replace `_hydrated` property with `hydrationCache` --- .../src/transportedQueryRef.ts | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/client-react-streaming/src/transportedQueryRef.ts b/packages/client-react-streaming/src/transportedQueryRef.ts index 5a6377ca..d6477bb9 100644 --- a/packages/client-react-streaming/src/transportedQueryRef.ts +++ b/packages/client-react-streaming/src/transportedQueryRef.ts @@ -81,8 +81,6 @@ export interface TransportedQueryRef< */ queryKey: string; }; - /** @private */ - _hydrated?: CacheKey; } export interface PreloadTransportedQueryFunction { @@ -163,28 +161,26 @@ function createTransportedQueryRef< }; } +const hydrationCache = new WeakMap< + TransportedQueryRef, + { cacheKey: CacheKey; wrapped: ReturnType> } +>(); + export function reviveTransportedQueryRef( queryRef: TransportedQueryRef, client: ApolloClient ): asserts queryRef is TransportedQueryRef & - ReturnType> & { _hydrated: CacheKey } { + ReturnType> { const { $__apollo_queryRef: { options, stream, queryKey }, } = queryRef; - const hydratedOptions = deserializeOptions(options); - const cacheKey: CacheKey = [ - hydratedOptions.query, - canonicalStringify(hydratedOptions.variables), - queryKey, - ]; - if (!queryRef._hydrated) { - // TanStack query has a timing where this is potentially transported over the wire if it - // is consumed in a component during SSR (it will be revived there) unless we make it non-enumerable - Object.defineProperty(queryRef, "_hydrated", { - value: cacheKey, - enumerable: false, - configurable: true, - }); + if (!hydrationCache.has(queryRef)) { + const hydratedOptions = deserializeOptions(options); + const cacheKey: CacheKey = [ + hydratedOptions.query, + canonicalStringify(hydratedOptions.variables), + queryKey, + ]; const internalQueryRef = getSuspenseCache(client).getQueryRef( cacheKey, () => @@ -199,7 +195,9 @@ export function reviveTransportedQueryRef( ), }) ); - Object.assign(queryRef, wrapQueryRef(internalQueryRef)); + const wrapped = wrapQueryRef(internalQueryRef); + hydrationCache.set(queryRef, { cacheKey, wrapped }); + Object.assign(queryRef, wrapped); } } @@ -217,7 +215,7 @@ export function useWrapTransportedQueryRef( let isTransported: boolean; if ((isTransported = isTransportedQueryRef(queryRef))) { reviveTransportedQueryRef(queryRef, client); - cacheKey = queryRef._hydrated; + cacheKey = hydrationCache.get(queryRef)?.cacheKey; } const unwrapped = unwrapQueryRef(queryRef)!; From 21741364e438f5d7e6bebb12ce0f3fd4c15fc66e Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Mar 2025 18:05:54 +0100 Subject: [PATCH 04/11] use `useWrapTransportedQueryRef` in react-router wrapped hooks --- packages/client-react-streaming/src/index.shared.ts | 1 + packages/react-router/src/ApolloClient.tsx | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/client-react-streaming/src/index.shared.ts b/packages/client-react-streaming/src/index.shared.ts index 4b7c949d..653b3da4 100644 --- a/packages/client-react-streaming/src/index.shared.ts +++ b/packages/client-react-streaming/src/index.shared.ts @@ -14,6 +14,7 @@ export { createTransportedQueryPreloader, isTransportedQueryRef, reviveTransportedQueryRef, + useWrapTransportedQueryRef, } from "./transportedQueryRef.js"; export { ReadFromReadableStreamLink, diff --git a/packages/react-router/src/ApolloClient.tsx b/packages/react-router/src/ApolloClient.tsx index cb945aae..5bcf301d 100644 --- a/packages/react-router/src/ApolloClient.tsx +++ b/packages/react-router/src/ApolloClient.tsx @@ -1,6 +1,7 @@ import { ReadFromReadableStreamLink, TeeToReadableStreamLink, + useWrapTransportedQueryRef, } from "@apollo/client-react-streaming"; import type { QueryManager } from "@apollo/client/core/QueryManager.js"; import type { NormalizedCacheObject } from "@apollo/client/index.js"; @@ -30,13 +31,21 @@ export class ApolloClient extends _ApolloClient { useReadQuery(originalHook) { return function useReadQuery(queryRef) { const client = useApolloClient(); - return originalHook(hydrateIfNecessary(queryRef, client) as any); + return originalHook( + useWrapTransportedQueryRef( + hydrateIfNecessary(queryRef, client) as any + ) + ); }; }, useQueryRefHandlers(originalHook) { return function useQueryRefHandlers(queryRef) { const client = useApolloClient(); - return originalHook(hydrateIfNecessary(queryRef, client) as any); + return originalHook( + useWrapTransportedQueryRef( + hydrateIfNecessary(queryRef, client) as any + ) + ); }; }, }; From 69cda0db2b0846153d72e99cde8d42789e9e3db4 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 12 Mar 2025 18:06:34 +0100 Subject: [PATCH 05/11] immeditately promiscade preloader return value instead of iterating `loader` result to deal with nested promises --- packages/react-router/src/preloader.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/react-router/src/preloader.tsx b/packages/react-router/src/preloader.tsx index 46ecd47a..a60726cf 100644 --- a/packages/react-router/src/preloader.tsx +++ b/packages/react-router/src/preloader.tsx @@ -36,18 +36,14 @@ export function createApolloLoaderHandler( ): ApolloLoader { return () => (loader) => (args) => { const client = makeClient(args.request); - const preloadQuery = createTransportedQueryPreloader(client); + const preloader = createTransportedQueryPreloader(client); + const preloadQuery: typeof preloader = (...args) => + replaceStreamWithPromiscade(preloader(...args)); const loaded = loader({ ...args, preloadQuery, }); - JSON.stringify(loaded, (_key, value) => { - if (isTransportedQueryRef(value)) { - replaceStreamWithPromiscade(value); - } - return value; - }); - return loaded as any; + return loaded as MarkedForSerialization; }; } @@ -77,15 +73,18 @@ function isPromiscaded( /** * This function is used to convert a stream ref to a promiscaded ref * - * **modifies the object in place** + * **modifies the object in place and returns it** */ -function replaceStreamWithPromiscade(queryRef: TransportedQueryRef) { +function replaceStreamWithPromiscade( + queryRef: T +) { const typed = queryRef as unknown as PromiscadedRef; // the stream will be tee'd so it can be used in the same environment, // but also transported over the wire in the form of a promiscade const stream = queryRef.$__apollo_queryRef.stream; typed.$__apollo_queryRef.promiscade = streamToPromiscade(stream); delete typed.$__apollo_queryRef.stream; + return queryRef; } /** From f1a5f2262b56d9308e1733cb4313767951c8bf18 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 13 Mar 2025 12:28:00 +0100 Subject: [PATCH 06/11] fix up exports --- packages/client-react-streaming/package-shape.json | 3 +++ packages/client-react-streaming/src/index.shared.ts | 1 - packages/client-react-streaming/src/index.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/client-react-streaming/package-shape.json b/packages/client-react-streaming/package-shape.json index 57294ea5..4bf5555c 100644 --- a/packages/client-react-streaming/package-shape.json +++ b/packages/client-react-streaming/package-shape.json @@ -33,6 +33,7 @@ "createTransportedQueryPreloader", "isTransportedQueryRef", "reviveTransportedQueryRef", + "useWrapTransportedQueryRef", "built_for_browser" ], "node": [ @@ -52,6 +53,7 @@ "createTransportedQueryPreloader", "isTransportedQueryRef", "reviveTransportedQueryRef", + "useWrapTransportedQueryRef", "built_for_ssr" ], "edge-light,worker,browser": [ @@ -71,6 +73,7 @@ "createTransportedQueryPreloader", "isTransportedQueryRef", "reviveTransportedQueryRef", + "useWrapTransportedQueryRef", "built_for_ssr" ] }, diff --git a/packages/client-react-streaming/src/index.shared.ts b/packages/client-react-streaming/src/index.shared.ts index 653b3da4..4b7c949d 100644 --- a/packages/client-react-streaming/src/index.shared.ts +++ b/packages/client-react-streaming/src/index.shared.ts @@ -14,7 +14,6 @@ export { createTransportedQueryPreloader, isTransportedQueryRef, reviveTransportedQueryRef, - useWrapTransportedQueryRef, } from "./transportedQueryRef.js"; export { ReadFromReadableStreamLink, diff --git a/packages/client-react-streaming/src/index.ts b/packages/client-react-streaming/src/index.ts index 3ebc9e42..e5a00a24 100644 --- a/packages/client-react-streaming/src/index.ts +++ b/packages/client-react-streaming/src/index.ts @@ -8,3 +8,4 @@ export { WrappedApolloProvider, skipDataTransport, } from "./DataTransportAbstraction/index.js"; +export { useWrapTransportedQueryRef } from "./transportedQueryRef.js"; From 77ed938b8f9e136a5cdc3904743adb07a48fd4f4 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 13 Mar 2025 12:41:34 +0100 Subject: [PATCH 07/11] no need to track `wrapped`, minimize diff --- packages/client-react-streaming/src/transportedQueryRef.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/client-react-streaming/src/transportedQueryRef.ts b/packages/client-react-streaming/src/transportedQueryRef.ts index d6477bb9..bf68b932 100644 --- a/packages/client-react-streaming/src/transportedQueryRef.ts +++ b/packages/client-react-streaming/src/transportedQueryRef.ts @@ -163,7 +163,7 @@ function createTransportedQueryRef< const hydrationCache = new WeakMap< TransportedQueryRef, - { cacheKey: CacheKey; wrapped: ReturnType> } + { cacheKey: CacheKey } >(); export function reviveTransportedQueryRef( @@ -181,6 +181,7 @@ export function reviveTransportedQueryRef( canonicalStringify(hydratedOptions.variables), queryKey, ]; + hydrationCache.set(queryRef, { cacheKey }); const internalQueryRef = getSuspenseCache(client).getQueryRef( cacheKey, () => @@ -195,9 +196,7 @@ export function reviveTransportedQueryRef( ), }) ); - const wrapped = wrapQueryRef(internalQueryRef); - hydrationCache.set(queryRef, { cacheKey, wrapped }); - Object.assign(queryRef, wrapped); + Object.assign(queryRef, wrapQueryRef(internalQueryRef)); } } From a30c12fd1688c8d9f2b4356d832d27625ae748de Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 13 Mar 2025 14:00:08 +0100 Subject: [PATCH 08/11] add test --- .../playwright/src/cc-dynamic.test.ts | 16 ++++ .../react-router/app/routes/asyncLoader.tsx | 73 +++++++++++++++++++ integration-test/shared/schema.ts | 2 +- 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 integration-test/react-router/app/routes/asyncLoader.tsx diff --git a/integration-test/playwright/src/cc-dynamic.test.ts b/integration-test/playwright/src/cc-dynamic.test.ts index 3d460444..8a4292b7 100644 --- a/integration-test/playwright/src/cc-dynamic.test.ts +++ b/integration-test/playwright/src/cc-dynamic.test.ts @@ -186,4 +186,20 @@ test.describe("CC dynamic", () => { } ); }); + + test.only("async loader", { tag: ["@react-router"] }, async ({ page }) => { + await page.goto(`${base}/asyncLoader`, { + waitUntil: "commit", + }); + + // main data already there + await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); + expect(await page.getByText("Queried in SSR environment").count()).toBe(1); + // deferred chunks still loading + expect(await page.getByText("loading...").count()).toBe(6); + // deferred chunk came in + await expect(page.getByText("cuteness overload")).toBeVisible(); + expect(await page.getByText("Queried in SSR environment").count()).toBe(7); + expect(await page.getByText("loading...").count()).toBe(0); + }); }); diff --git a/integration-test/react-router/app/routes/asyncLoader.tsx b/integration-test/react-router/app/routes/asyncLoader.tsx new file mode 100644 index 00000000..df847272 --- /dev/null +++ b/integration-test/react-router/app/routes/asyncLoader.tsx @@ -0,0 +1,73 @@ +import { useLoaderData } from "react-router"; +import type { Route } from "./+types/home"; +import { + useApolloClient, + useQueryRefHandlers, + useReadQuery, +} from "@apollo/client/react/index.js"; +import { apolloLoader } from "~/apollo"; +import { DEFERRED_QUERY } from "@integration-test/shared/queries"; +import { useTransition } from "react"; + +export const loader = apolloLoader()(async ({ + preloadQuery, +}) => { + const queryRef = preloadQuery(DEFERRED_QUERY, { + variables: { delayDeferred: 1000 }, + }); + await new Promise((resolve) => setTimeout(resolve, 300)); + return { + queryRef, + }; +}); + +export default function Home() { + const { queryRef } = useLoaderData(); + + const { refetch } = useQueryRefHandlers(queryRef); + const [refetching, startTransition] = useTransition(); + const { data } = useReadQuery(queryRef); + const client = useApolloClient(); + + return ( + <> +
      + {data.products.map(({ id, title, rating }) => ( +
    • + {title} +
      + Rating:{" "} +
      + {rating?.value || ""} +
      + {rating ? `Queried in ${rating.env} environment` : "loading..."} +
      +
    • + ))} +
    +

    Queried in {data.env} environment

    + + + ); +} diff --git a/integration-test/shared/schema.ts b/integration-test/shared/schema.ts index 37e65a5c..46cfda2a 100644 --- a/integration-test/shared/schema.ts +++ b/integration-test/shared/schema.ts @@ -79,7 +79,7 @@ const resolvers = { Product: { rating: (source, args, context) => { return new Promise((resolve) => - setTimeout(resolve, Math.random() * 2 * args.delay, { + setTimeout(resolve, args.delay / 2 + Math.random() * args.delay, { value: products.find((p) => p.id === source.id)?.rating, env: getEnv(context), }) From d8a7c2ab5e85b944e9bc90fd0901acb630b15a8f Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 13 Mar 2025 14:03:10 +0100 Subject: [PATCH 09/11] remove .only --- integration-test/playwright/src/cc-dynamic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-test/playwright/src/cc-dynamic.test.ts b/integration-test/playwright/src/cc-dynamic.test.ts index 8a4292b7..b18a0aa2 100644 --- a/integration-test/playwright/src/cc-dynamic.test.ts +++ b/integration-test/playwright/src/cc-dynamic.test.ts @@ -187,7 +187,7 @@ test.describe("CC dynamic", () => { ); }); - test.only("async loader", { tag: ["@react-router"] }, async ({ page }) => { + test("async loader", { tag: ["@react-router"] }, async ({ page }) => { await page.goto(`${base}/asyncLoader`, { waitUntil: "commit", }); From 9e1ab2d46aa12d5eaf3c45c8ceadbdbd854faf6d Mon Sep 17 00:00:00 2001 From: Vlady Veselinov Date: Fri, 14 Mar 2025 18:45:49 +0000 Subject: [PATCH 10/11] react router context api --- .../playwright/src/cc-dynamic.test.ts | 16 ++ integration-test/react-router/app/apollo.ts | 3 + .../react-router/app/routes/middleware.tsx | 76 +++++++ integration-test/react-router/package.json | 10 +- .../react-router/react-router.config.ts | 10 + packages/react-router/package.json | 4 +- packages/react-router/src/index.ts | 6 +- packages/react-router/src/preloader.tsx | 33 ++- yarn.lock | 204 ++++-------------- 9 files changed, 188 insertions(+), 174 deletions(-) create mode 100644 integration-test/react-router/app/routes/middleware.tsx diff --git a/integration-test/playwright/src/cc-dynamic.test.ts b/integration-test/playwright/src/cc-dynamic.test.ts index b18a0aa2..5918f205 100644 --- a/integration-test/playwright/src/cc-dynamic.test.ts +++ b/integration-test/playwright/src/cc-dynamic.test.ts @@ -202,4 +202,20 @@ test.describe("CC dynamic", () => { expect(await page.getByText("Queried in SSR environment").count()).toBe(7); expect(await page.getByText("loading...").count()).toBe(0); }); + + test("middleware", { tag: ["@react-router"] }, async ({ page }) => { + await page.goto(`${base}/middleware`, { + waitUntil: "commit", + }); + + // main data already there + await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); + expect(await page.getByText("Queried in SSR environment").count()).toBe(1); + // deferred chunks still loading + expect(await page.getByText("loading...").count()).toBe(6); + // deferred chunk came in + await expect(page.getByText("cuteness overload")).toBeVisible(); + expect(await page.getByText("Queried in SSR environment").count()).toBe(7); + expect(await page.getByText("loading...").count()).toBe(0); + }); }); diff --git a/integration-test/react-router/app/apollo.ts b/integration-test/react-router/app/apollo.ts index eea30ada..78db9dad 100644 --- a/integration-test/react-router/app/apollo.ts +++ b/integration-test/react-router/app/apollo.ts @@ -1,6 +1,7 @@ import { ApolloLink, HttpLink, InMemoryCache } from "@apollo/client/index.js"; import { createApolloLoaderHandler, + createApolloMiddleware, ApolloClient, } from "@apollo/client-integration-react-router"; import { IncrementalSchemaLink } from "@integration-test/shared/IncrementalSchemaLink"; @@ -22,4 +23,6 @@ export const makeClient = (request?: Request) => { link, }); }; + export const apolloLoader = createApolloLoaderHandler(makeClient); +export const apolloMiddleware = createApolloMiddleware(makeClient); diff --git a/integration-test/react-router/app/routes/middleware.tsx b/integration-test/react-router/app/routes/middleware.tsx new file mode 100644 index 00000000..6f426cfd --- /dev/null +++ b/integration-test/react-router/app/routes/middleware.tsx @@ -0,0 +1,76 @@ +import type { Route } from "./+types/middleware"; +import { + useApolloClient, + useQueryRefHandlers, + useReadQuery, +} from "@apollo/client/react/index.js"; +import { apolloMiddleware } from "~/apollo"; +import { DEFERRED_QUERY } from "@integration-test/shared/queries"; +import { useTransition } from "react"; +import { apolloContext } from "@apollo/client-integration-react-router"; + +export const unstable_middleware = [apolloMiddleware] + +export async function loader({ context }: Route.LoaderArgs) { + const apollo = context.get(apolloContext) + const queryRef = apollo.preloadQuery(DEFERRED_QUERY, { + variables: { delayDeferred: 1000 }, + }); + + await new Promise((resolve) => setTimeout(resolve, 300)); + + return { + queryRef, + }; +} + +export default function Middleware({ loaderData }: Route.ComponentProps) { + const { queryRef } = loaderData + + const { refetch } = useQueryRefHandlers(queryRef); + const [refetching, startTransition] = useTransition(); + const { data } = useReadQuery(queryRef); + const client = useApolloClient(); + + return ( + <> +
      + {data.products.map(({ id, title, rating }) => ( +
    • + {title} +
      + Rating:{" "} +
      + {rating?.value || ""} +
      + {rating ? `Queried in ${rating.env} environment` : "loading..."} +
      +
    • + ))} +
    +

    Queried in {data.env} environment

    + + + ); +} diff --git a/integration-test/react-router/package.json b/integration-test/react-router/package.json index d0cba06d..55cda8ec 100644 --- a/integration-test/react-router/package.json +++ b/integration-test/react-router/package.json @@ -14,18 +14,18 @@ "@apollo/client": "^3.13.4", "@apollo/client-integration-react-router": "workspace:^", "@integration-test/shared": "workspace:^", - "@react-router/fs-routes": "^7.2.0-pre.3", - "@react-router/node": "^7.2.0-pre.3", - "@react-router/serve": "^7.2.0-pre.3", + "@react-router/fs-routes": "^7.3.0", + "@react-router/node": "^7.3.0", + "@react-router/serve": "^7.3.0", "@vercel/react-router": "^1.0.2", "graphql": "*", "isbot": "^5.1.17", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router": "^7.2.0-pre.3" + "react-router": "^7.3.0" }, "devDependencies": { - "@react-router/dev": "^7.2.0-pre.3", + "@react-router/dev": "^7.3.0", "@tailwindcss/vite": "^4.0.0", "@types/node": "^20", "@types/react": "^19.0.1", diff --git a/integration-test/react-router/react-router.config.ts b/integration-test/react-router/react-router.config.ts index fc433a55..84edca21 100644 --- a/integration-test/react-router/react-router.config.ts +++ b/integration-test/react-router/react-router.config.ts @@ -7,9 +7,19 @@ if (process.env.VERCEL) { presets.push(vercelPreset()); } +declare module "react-router" { + interface Future { + unstable_middleware: true; // 👈 Enable middleware types + } +} + export default { // Config options... // Server-side render by default, to enable SPA mode set this to `false` ssr: true, presets, + future: { + // https://reactrouter.com/start/changelog#middleware-unstable + unstable_middleware: true, + }, } satisfies Config; diff --git a/packages/react-router/package.json b/packages/react-router/package.json index c64536e2..e35655b7 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -79,7 +79,7 @@ "publint": "0.2.7", "react": "^19.0.0", "react-dom": "*", - "react-router": "^7.2.0-pre.3", + "react-router": "^7.3.0", "rimraf": "5.0.5", "tsup": "8.0.2", "typescript": "5.4.5", @@ -88,7 +88,7 @@ "peerDependencies": { "@apollo/client": "^3.13.0", "react": "^19", - "react-router": "^7.2.0-pre.3" + "react-router": "^7.3.0" }, "dependencies": { "@apollo/client-react-streaming": "0.12.0-alpha.2", diff --git a/packages/react-router/src/index.ts b/packages/react-router/src/index.ts index 15c9e897..4cb7bd34 100644 --- a/packages/react-router/src/index.ts +++ b/packages/react-router/src/index.ts @@ -1,3 +1,7 @@ export { ApolloClient } from "./ApolloClient.js"; export { ApolloHydrationHelper } from "./ApolloHydrationHelper.js"; -export { createApolloLoaderHandler } from "./preloader.js"; +export { + apolloContext, + createApolloLoaderHandler, + createApolloMiddleware, +} from "./preloader.js"; diff --git a/packages/react-router/src/preloader.tsx b/packages/react-router/src/preloader.tsx index a60726cf..d78e09bf 100644 --- a/packages/react-router/src/preloader.tsx +++ b/packages/react-router/src/preloader.tsx @@ -13,21 +13,24 @@ import { } from "@apollo/client-react-streaming"; import type { Promiscade } from "promiscade"; import { promiscadeToReadableStream, streamToPromiscade } from "promiscade"; -import type { unstable_SerializesTo } from "react-router"; +import { unstable_createContext, type unstable_MiddlewareFunction, type unstable_SerializesTo } from "react-router"; import type { JsonString } from "@apollo/client-react-streaming/stream-utils"; type MarkedForSerialization = T extends TransportedQueryRef - ? unstable_SerializesTo> - : { [K in keyof T]: MarkedForSerialization }; + ? unstable_SerializesTo> + : { [K in keyof T]: MarkedForSerialization }; + +export type ApolloContext = { + preloadQuery: PreloadTransportedQueryFunction; +}; + type ApolloLoader = >() => < ReturnValue, >( loader: ( - args: LoaderArgs & { - preloadQuery: PreloadTransportedQueryFunction; - } + args: LoaderArgs & ApolloContext ) => ReturnValue ) => (args: LoaderArgs) => MarkedForSerialization; @@ -47,6 +50,24 @@ export function createApolloLoaderHandler( }; } + +export const apolloContext = unstable_createContext(); + +export function createApolloMiddleware( + makeClient: (request: Request) => ApolloClient +): unstable_MiddlewareFunction { + return async ({ request, context }) => { + const client = makeClient(request); + const preloader = createTransportedQueryPreloader(client); + const preloadQuery: typeof preloader = (...args) => + replaceStreamWithPromiscade(preloader(...args)); + + context.set(apolloContext, { + preloadQuery, + }); + }; +} + // currently, `turbo-stream` cannot stream a `ReadableStream`. // until https://github.com/jacob-ebey/turbo-stream/pull/51 // is merged or similar functionality is added, we need to diff --git a/yarn.lock b/yarn.lock index 0d335c33..3db24ced 100644 --- a/yarn.lock +++ b/yarn.lock @@ -256,7 +256,7 @@ __metadata: publint: "npm:0.2.7" react: "npm:^19.0.0" react-dom: "npm:*" - react-router: "npm:^7.2.0-pre.3" + react-router: "npm:^7.3.0" rimraf: "npm:5.0.5" tsup: "npm:8.0.2" typescript: "npm:5.4.5" @@ -264,7 +264,7 @@ __metadata: peerDependencies: "@apollo/client": ^3.13.0 react: ^19 - react-router: ^7.2.0-pre.3 + react-router: ^7.3.0 languageName: unknown linkType: soft @@ -6791,10 +6791,10 @@ __metadata: "@apollo/client": "npm:^3.13.4" "@apollo/client-integration-react-router": "workspace:^" "@integration-test/shared": "workspace:^" - "@react-router/dev": "npm:^7.2.0-pre.3" - "@react-router/fs-routes": "npm:^7.2.0-pre.3" - "@react-router/node": "npm:^7.2.0-pre.3" - "@react-router/serve": "npm:^7.2.0-pre.3" + "@react-router/dev": "npm:^7.3.0" + "@react-router/fs-routes": "npm:^7.3.0" + "@react-router/node": "npm:^7.3.0" + "@react-router/serve": "npm:^7.3.0" "@tailwindcss/vite": "npm:^4.0.0" "@types/node": "npm:^20" "@types/react": "npm:^19.0.1" @@ -6804,7 +6804,7 @@ __metadata: isbot: "npm:^5.1.17" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - react-router: "npm:^7.2.0-pre.3" + react-router: "npm:^7.3.0" react-router-devtools: "npm:^1.1.0" tailwindcss: "npm:^4.0.0" typescript: "npm:^5.7.2" @@ -8542,9 +8542,9 @@ __metadata: languageName: node linkType: hard -"@react-router/dev@npm:^7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "@react-router/dev@npm:7.2.0-pre.3" +"@react-router/dev@npm:^7.3.0": + version: 7.3.0 + resolution: "@react-router/dev@npm:7.3.0" dependencies: "@babel/core": "npm:^7.21.8" "@babel/generator": "npm:^7.21.5" @@ -8555,7 +8555,7 @@ __metadata: "@babel/traverse": "npm:^7.23.2" "@babel/types": "npm:^7.22.5" "@npmcli/package-json": "npm:^4.0.1" - "@react-router/node": "npm:7.2.0-pre.3" + "@react-router/node": "npm:7.3.0" arg: "npm:^5.0.1" babel-dead-code-elimination: "npm:^1.0.6" chokidar: "npm:^4.0.0" @@ -8563,12 +8563,10 @@ __metadata: es-module-lexer: "npm:^1.3.1" exit-hook: "npm:2.2.1" fs-extra: "npm:^10.0.0" - gunzip-maybe: "npm:^1.4.2" jsesc: "npm:3.0.2" lodash: "npm:^4.17.21" pathe: "npm:^1.1.2" picocolors: "npm:^1.1.1" - picomatch: "npm:^2.3.1" prettier: "npm:^2.7.1" react-refresh: "npm:^0.14.0" semver: "npm:^7.3.7" @@ -8576,8 +8574,8 @@ __metadata: valibot: "npm:^0.41.0" vite-node: "npm:3.0.0-beta.2" peerDependencies: - "@react-router/serve": ^7.2.0-pre.3 - react-router: ^7.2.0-pre.3 + "@react-router/serve": ^7.3.0 + react-router: ^7.3.0 typescript: ^5.1.0 vite: ^5.1.0 || ^6.0.0 wrangler: ^3.28.2 @@ -8590,75 +8588,75 @@ __metadata: optional: true bin: react-router: bin.js - checksum: 10/98a9cc39811235474b350e6e1f9c1a0767ec90689e9de44ace6da78c66ecf92262bb4e1b8dda27049e8871ba53c91da1ddb956ce9e4ada40c523947a9a8346b3 + checksum: 10/361d1a2f6eeb67297b3d000846256f09e2f870d10fa044758b6341539ab672c5f618e190aa5f61603882914543b0935c23ff27eec768d1f0d815ee9f0dfc2818 languageName: node linkType: hard -"@react-router/express@npm:7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "@react-router/express@npm:7.2.0-pre.3" +"@react-router/express@npm:7.3.0": + version: 7.3.0 + resolution: "@react-router/express@npm:7.3.0" dependencies: - "@react-router/node": "npm:7.2.0-pre.3" + "@react-router/node": "npm:7.3.0" peerDependencies: - express: ^4.17.1 - react-router: 7.2.0-pre.3 + express: ^4.17.1 || ^5 + react-router: 7.3.0 typescript: ^5.1.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/e0f203097a22a6fc80463dad6cebb309a12daffb8c75cf1515ceb02ccc81075e46e4c5bf208e958f62827a994ff322e6b9fd35dfc3a94e38aeaa8abf8fc0e70a + checksum: 10/00cbf2079c6bfc398e201bb902bd20f865177edb103d5dfc24ebece4781da9ebc7ca663a2f845b925e9edebc467b391131e043df22768343c40744246a41ba73 languageName: node linkType: hard -"@react-router/fs-routes@npm:^7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "@react-router/fs-routes@npm:7.2.0-pre.3" +"@react-router/fs-routes@npm:^7.3.0": + version: 7.3.0 + resolution: "@react-router/fs-routes@npm:7.3.0" dependencies: minimatch: "npm:^9.0.0" peerDependencies: - "@react-router/dev": ^7.2.0-pre.3 + "@react-router/dev": ^7.3.0 typescript: ^5.1.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/a11c4e2a8cf8ef6c68a66179e865e185c2c5a7bfb5afd6b849ac4d43c4fab937691fcd625b5f999da90f683462f587040108a241565f00d0cb3622e0b302b935 + checksum: 10/545a719ffc3d5522a1d09cba83d60afcd3a25ac4f8b48bd0bc6929b34191cc5561dfdceb26f54d8466fba5c2c9ac284dba2d244181f5f66c784aa3508327f50f languageName: node linkType: hard -"@react-router/node@npm:7.2.0-pre.3, @react-router/node@npm:^7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "@react-router/node@npm:7.2.0-pre.3" +"@react-router/node@npm:7.3.0, @react-router/node@npm:^7.3.0": + version: 7.3.0 + resolution: "@react-router/node@npm:7.3.0" dependencies: "@mjackson/node-fetch-server": "npm:^0.2.0" source-map-support: "npm:^0.5.21" stream-slice: "npm:^0.1.2" undici: "npm:^6.19.2" peerDependencies: - react-router: 7.2.0-pre.3 + react-router: 7.3.0 typescript: ^5.1.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/82139f928daa27affa629500d964500aca69750b0dc9f92abf118997da464925f4b628ecffc1948f2780e26f33694e9bc8782e642736b474e00ccd34f25a8592 + checksum: 10/4b7336017bf07e7c4b053b2700725186dd607732dfd488de136e84fcebfe936250de7a841a87068eba9ee093d2e1fbd2fbd101d29d68d43a4bad639f84e8982f languageName: node linkType: hard -"@react-router/serve@npm:^7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "@react-router/serve@npm:7.2.0-pre.3" +"@react-router/serve@npm:^7.3.0": + version: 7.3.0 + resolution: "@react-router/serve@npm:7.3.0" dependencies: - "@react-router/express": "npm:7.2.0-pre.3" - "@react-router/node": "npm:7.2.0-pre.3" + "@react-router/express": "npm:7.3.0" + "@react-router/node": "npm:7.3.0" compression: "npm:^1.7.4" express: "npm:^4.19.2" get-port: "npm:5.1.1" morgan: "npm:^1.10.0" source-map-support: "npm:^0.5.21" peerDependencies: - react-router: 7.2.0-pre.3 + react-router: 7.3.0 bin: react-router-serve: bin.js - checksum: 10/212cddc84d90e2d4431dd222a8d66ce683062f374e8ea17ea7f2dd33b68c9077d2836fe945b423c012b4fb41fc3b9e699d2e28531ab3136223409f1dcab818e5 + checksum: 10/c29211dbeb5f053c1579263039c39e9755dd5925745b579b5ddb70110db607f5bfc435a9d4d82585bb686e1de068a7481ac06ae2731122f889fa61267a5b73d2 languageName: node linkType: hard @@ -13209,15 +13207,6 @@ __metadata: languageName: node linkType: hard -"browserify-zlib@npm:^0.1.4": - version: 0.1.4 - resolution: "browserify-zlib@npm:0.1.4" - dependencies: - pako: "npm:~0.2.0" - checksum: 10/cd506a1ef9c3280f6537a17ed1352ef7738b66fef0a15a655dc3a43edc34be6ee78c5838427146ae1fcd4801fc06d2ab203614d0f8c4df8b5a091cf0134b9a80 - languageName: node - linkType: hard - "browserslist@npm:^4.21.10, browserslist@npm:^4.24.3": version: 4.24.4 resolution: "browserslist@npm:4.24.4" @@ -15256,18 +15245,6 @@ __metadata: languageName: node linkType: hard -"duplexify@npm:^3.5.0, duplexify@npm:^3.6.0": - version: 3.7.1 - resolution: "duplexify@npm:3.7.1" - dependencies: - end-of-stream: "npm:^1.0.0" - inherits: "npm:^2.0.1" - readable-stream: "npm:^2.0.0" - stream-shift: "npm:^1.0.0" - checksum: 10/7799984d178fb57e11c43f5f172a10f795322ec85ff664c2a98d2c2de6deeb9d7a30b810f83923dcd7ebe0f1786724b8aee2b62ca4577522141f93d6d48fb31c - languageName: node - linkType: hard - "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -15374,7 +15351,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -18031,22 +18008,6 @@ __metadata: languageName: node linkType: hard -"gunzip-maybe@npm:^1.4.2": - version: 1.4.2 - resolution: "gunzip-maybe@npm:1.4.2" - dependencies: - browserify-zlib: "npm:^0.1.4" - is-deflate: "npm:^1.0.0" - is-gzip: "npm:^1.0.0" - peek-stream: "npm:^1.1.0" - pumpify: "npm:^1.3.3" - through2: "npm:^2.0.3" - bin: - gunzip-maybe: bin.js - checksum: 10/82a4eadb617e50ac63cb88b3c1ebef0f85de702c0c2031c5d9c0575837e1eef7c94fa4ad69ca4aec2dc3d939c89054ec07c91c233648433058efa7d44354d456 - languageName: node - linkType: hard - "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -18897,13 +18858,6 @@ __metadata: languageName: node linkType: hard -"is-deflate@npm:^1.0.0": - version: 1.0.0 - resolution: "is-deflate@npm:1.0.0" - checksum: 10/c2f9f2d3db79ac50c5586697d1e69a55282a2b0cc5e437b3c470dd47f24e40b6216dcd7e024511e21381607bf57afa019343e3bd0e08a119032818b596004262 - languageName: node - linkType: hard - "is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": version: 2.2.1 resolution: "is-docker@npm:2.2.1" @@ -18970,13 +18924,6 @@ __metadata: languageName: node linkType: hard -"is-gzip@npm:^1.0.0": - version: 1.0.0 - resolution: "is-gzip@npm:1.0.0" - checksum: 10/0d28931c1f445fa29c900cf9f48e06e9d1d477a3bf7bd7332e7ce68f1333ccd8cb381de2f0f62a9a262d9c0912608a9a71b4a40e788e201b3dbd67072bb20d86 - languageName: node - linkType: hard - "is-inside-container@npm:^1.0.0": version: 1.0.0 resolution: "is-inside-container@npm:1.0.0" @@ -22370,13 +22317,6 @@ __metadata: languageName: node linkType: hard -"pako@npm:~0.2.0": - version: 0.2.9 - resolution: "pako@npm:0.2.9" - checksum: 10/627c6842e90af0b3a9ee47345bd66485a589aff9514266f4fa9318557ad819c46fedf97510f2cef9b6224c57913777966a05cb46caf6a9b31177a5401a06fe15 - languageName: node - linkType: hard - "param-case@npm:^3.0.4": version: 3.0.4 resolution: "param-case@npm:3.0.4" @@ -22641,17 +22581,6 @@ __metadata: languageName: node linkType: hard -"peek-stream@npm:^1.1.0": - version: 1.1.3 - resolution: "peek-stream@npm:1.1.3" - dependencies: - buffer-from: "npm:^1.0.0" - duplexify: "npm:^3.5.0" - through2: "npm:^2.0.3" - checksum: 10/a0e09d6d1a8a01158a3334f20d6b1cdd91747eba24eb06a1d742eefb620385593121a76d4378cc81f77cdce6a66df0575a41041b1189c510254aec91878afc99 - languageName: node - linkType: hard - "perfect-debounce@npm:^1.0.0": version: 1.0.0 resolution: "perfect-debounce@npm:1.0.0" @@ -23062,16 +22991,6 @@ __metadata: languageName: node linkType: hard -"pump@npm:^2.0.0": - version: 2.0.1 - resolution: "pump@npm:2.0.1" - dependencies: - end-of-stream: "npm:^1.1.0" - once: "npm:^1.3.1" - checksum: 10/e9f26a17be00810bff37ad0171edb35f58b242487b0444f92fb7d78bc7d61442fa9b9c5bd93a43fd8fd8ddd3cc75f1221f5e04c790f42907e5baab7cf5e2b931 - languageName: node - linkType: hard - "pump@npm:^3.0.0": version: 3.0.2 resolution: "pump@npm:3.0.2" @@ -23082,17 +23001,6 @@ __metadata: languageName: node linkType: hard -"pumpify@npm:^1.3.3": - version: 1.5.1 - resolution: "pumpify@npm:1.5.1" - dependencies: - duplexify: "npm:^3.6.0" - inherits: "npm:^2.0.3" - pump: "npm:^2.0.0" - checksum: 10/5d11a99f320dc2a052610399bac6d03db0a23bc23b23aa2a7d0adf879da3065a55134b975db66dc46bc79f54af3dd575d8119113a0a5b311a00580e1f053896b - languageName: node - linkType: hard - "punycode@npm:^1.3.2": version: 1.4.1 resolution: "punycode@npm:1.4.1" @@ -23564,9 +23472,9 @@ __metadata: languageName: node linkType: hard -"react-router@npm:^7.2.0-pre.3": - version: 7.2.0-pre.3 - resolution: "react-router@npm:7.2.0-pre.3" +"react-router@npm:^7.3.0": + version: 7.3.0 + resolution: "react-router@npm:7.3.0" dependencies: "@types/cookie": "npm:^0.6.0" cookie: "npm:^1.0.1" @@ -23578,7 +23486,7 @@ __metadata: peerDependenciesMeta: react-dom: optional: true - checksum: 10/15494f1be3780a82c5728e489be5fdf93b45c404b6683e597340e12177a2eba81f341d359e1d1459a726aaa7d78db94e3cbcf587340c392f561b73fc837a6cb9 + checksum: 10/f7694785f95b989e55c0ae058c36c5f523e318109aea26f4e1567a3c50dcbff769417c54451d976990c507f83499bfe7bc4ec9bea52b2fabab4e6da57d21b231 languageName: node linkType: hard @@ -23671,7 +23579,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -25269,13 +25177,6 @@ __metadata: languageName: node linkType: hard -"stream-shift@npm:^1.0.0": - version: 1.0.3 - resolution: "stream-shift@npm:1.0.3" - checksum: 10/a24c0a3f66a8f9024bd1d579a533a53be283b4475d4e6b4b3211b964031447bdf6532dd1f3c2b0ad66752554391b7c62bd7ca4559193381f766534e723d50242 - languageName: node - linkType: hard - "stream-slice@npm:^0.1.2": version: 0.1.2 resolution: "stream-slice@npm:0.1.2" @@ -25913,16 +25814,6 @@ __metadata: languageName: node linkType: hard -"through2@npm:^2.0.3": - version: 2.0.5 - resolution: "through2@npm:2.0.5" - dependencies: - readable-stream: "npm:~2.3.6" - xtend: "npm:~4.0.1" - checksum: 10/cd71f7dcdc7a8204fea003a14a433ef99384b7d4e31f5497e1f9f622b3cf3be3691f908455f98723bdc80922a53af7fa10c3b7abbe51c6fd3d536dbc7850e2c4 - languageName: node - linkType: hard - "through@npm:^2.3.6, through@npm:^2.3.8": version: 2.3.8 resolution: "through@npm:2.3.8" @@ -28107,13 +27998,6 @@ __metadata: languageName: node linkType: hard -"xtend@npm:~4.0.1": - version: 4.0.2 - resolution: "xtend@npm:4.0.2" - checksum: 10/ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a - languageName: node - linkType: hard - "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" From 3492bc5b352002c504ab3fa9007911564aefaf1c Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Wed, 19 Mar 2025 11:29:14 +0100 Subject: [PATCH 11/11] support for `unstable_getContext` --- .../playwright/src/cc-dynamic.test.ts | 12 +++-- integration-test/react-router/app/apollo.ts | 2 - .../react-router/app/entry.client.tsx | 11 ++++- .../{middleware.tsx => clientLoader.tsx} | 16 +++---- packages/react-router/src/index.ts | 2 +- packages/react-router/src/preloader.tsx | 46 ++++++++++--------- yarn.lock | 6 +-- 7 files changed, 53 insertions(+), 42 deletions(-) rename integration-test/react-router/app/routes/{middleware.tsx => clientLoader.tsx} (80%) diff --git a/integration-test/playwright/src/cc-dynamic.test.ts b/integration-test/playwright/src/cc-dynamic.test.ts index 2a7fd864..f0a46092 100644 --- a/integration-test/playwright/src/cc-dynamic.test.ts +++ b/integration-test/playwright/src/cc-dynamic.test.ts @@ -203,21 +203,25 @@ test.describe("CC dynamic", () => { expect(await page.getByText("loading...").count()).toBe(0); }); - test("middleware", { tag: ["@react-router"] }, async ({ page }) => { - await page.goto(`${base}/middleware`, { + test("client loader", { tag: ["@react-router"] }, async ({ page }) => { + await page.goto(`${base}/clientLoader`, { waitUntil: "commit", }); // main data already there await expect(page.getByText("Soft Warm Apollo Beanie")).toBeVisible(); - expect(await page.getByText("Queried in SSR environment").count()).toBe(1); + expect(await page.getByText("Queried in browser environment").count()).toBe( + 1 + ); // deferred chunks still loading expect(await page.getByText("loading...").count()).toBe(6); // deferred chunk came in await expect(page.getByText("cuteness overload")).toBeVisible(); await new Promise((resolve) => setTimeout(resolve, 500)); - expect(await page.getByText("Queried in SSR environment").count()).toBe(7); + expect(await page.getByText("Queried in browser environment").count()).toBe( + 7 + ); expect(await page.getByText("loading...").count()).toBe(0); }); }); diff --git a/integration-test/react-router/app/apollo.ts b/integration-test/react-router/app/apollo.ts index 78db9dad..ca3e58e8 100644 --- a/integration-test/react-router/app/apollo.ts +++ b/integration-test/react-router/app/apollo.ts @@ -1,7 +1,6 @@ import { ApolloLink, HttpLink, InMemoryCache } from "@apollo/client/index.js"; import { createApolloLoaderHandler, - createApolloMiddleware, ApolloClient, } from "@apollo/client-integration-react-router"; import { IncrementalSchemaLink } from "@integration-test/shared/IncrementalSchemaLink"; @@ -25,4 +24,3 @@ export const makeClient = (request?: Request) => { }; export const apolloLoader = createApolloLoaderHandler(makeClient); -export const apolloMiddleware = createApolloMiddleware(makeClient); diff --git a/integration-test/react-router/app/entry.client.tsx b/integration-test/react-router/app/entry.client.tsx index 34777a8f..cfb44f7a 100644 --- a/integration-test/react-router/app/entry.client.tsx +++ b/integration-test/react-router/app/entry.client.tsx @@ -3,6 +3,7 @@ import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; import { makeClient } from "./apollo"; import { ApolloProvider } from "@apollo/client/react/index.js"; +import { initializeApolloContext } from "@apollo/client-integration-react-router"; startTransition(() => { const client = makeClient(); @@ -10,7 +11,15 @@ startTransition(() => { document, - + { + const context = new Map(); + // set other context values here + return initializeApolloContext(client, context); + }} + /> + {/* if you have no other context values, as a shortcut */} + {/* initializeApolloContext(client)} /> */} ); diff --git a/integration-test/react-router/app/routes/middleware.tsx b/integration-test/react-router/app/routes/clientLoader.tsx similarity index 80% rename from integration-test/react-router/app/routes/middleware.tsx rename to integration-test/react-router/app/routes/clientLoader.tsx index 6f426cfd..91db1311 100644 --- a/integration-test/react-router/app/routes/middleware.tsx +++ b/integration-test/react-router/app/routes/clientLoader.tsx @@ -1,19 +1,17 @@ -import type { Route } from "./+types/middleware"; +import type { Route } from "./+types/clientLoader"; import { useApolloClient, useQueryRefHandlers, useReadQuery, } from "@apollo/client/react/index.js"; -import { apolloMiddleware } from "~/apollo"; +import {} from "~/apollo"; import { DEFERRED_QUERY } from "@integration-test/shared/queries"; import { useTransition } from "react"; import { apolloContext } from "@apollo/client-integration-react-router"; -export const unstable_middleware = [apolloMiddleware] - -export async function loader({ context }: Route.LoaderArgs) { - const apollo = context.get(apolloContext) - const queryRef = apollo.preloadQuery(DEFERRED_QUERY, { +export async function clientLoader({ context }: Route.LoaderArgs) { + const { preloadQuery } = context.get(apolloContext); + const queryRef = preloadQuery(DEFERRED_QUERY, { variables: { delayDeferred: 1000 }, }); @@ -24,8 +22,8 @@ export async function loader({ context }: Route.LoaderArgs) { }; } -export default function Middleware({ loaderData }: Route.ComponentProps) { - const { queryRef } = loaderData +export default function WithClientLoader({ loaderData }: Route.ComponentProps) { + const { queryRef } = loaderData; const { refetch } = useQueryRefHandlers(queryRef); const [refetching, startTransition] = useTransition(); diff --git a/packages/react-router/src/index.ts b/packages/react-router/src/index.ts index 4cb7bd34..f9c152b6 100644 --- a/packages/react-router/src/index.ts +++ b/packages/react-router/src/index.ts @@ -3,5 +3,5 @@ export { ApolloHydrationHelper } from "./ApolloHydrationHelper.js"; export { apolloContext, createApolloLoaderHandler, - createApolloMiddleware, + initializeApolloContext, } from "./preloader.js"; diff --git a/packages/react-router/src/preloader.tsx b/packages/react-router/src/preloader.tsx index d78e09bf..e180f2ea 100644 --- a/packages/react-router/src/preloader.tsx +++ b/packages/react-router/src/preloader.tsx @@ -13,25 +13,31 @@ import { } from "@apollo/client-react-streaming"; import type { Promiscade } from "promiscade"; import { promiscadeToReadableStream, streamToPromiscade } from "promiscade"; -import { unstable_createContext, type unstable_MiddlewareFunction, type unstable_SerializesTo } from "react-router"; +import { + unstable_createContext, + type unstable_InitialContext, + type unstable_SerializesTo, +} from "react-router"; import type { JsonString } from "@apollo/client-react-streaming/stream-utils"; +import { + createQueryPreloader, + type PreloadQueryFunction, +} from "@apollo/client/react/index.js"; + type MarkedForSerialization = T extends TransportedQueryRef - ? unstable_SerializesTo> - : { [K in keyof T]: MarkedForSerialization }; + ? unstable_SerializesTo> + : { [K in keyof T]: MarkedForSerialization }; export type ApolloContext = { preloadQuery: PreloadTransportedQueryFunction; }; - type ApolloLoader = >() => < ReturnValue, >( - loader: ( - args: LoaderArgs & ApolloContext - ) => ReturnValue + loader: (args: LoaderArgs & ApolloContext) => ReturnValue ) => (args: LoaderArgs) => MarkedForSerialization; export function createApolloLoaderHandler( @@ -50,22 +56,18 @@ export function createApolloLoaderHandler( }; } +export const apolloContext = unstable_createContext<{ + client: ApolloClient; + preloadQuery: PreloadQueryFunction; +}>(); -export const apolloContext = unstable_createContext(); - -export function createApolloMiddleware( - makeClient: (request: Request) => ApolloClient -): unstable_MiddlewareFunction { - return async ({ request, context }) => { - const client = makeClient(request); - const preloader = createTransportedQueryPreloader(client); - const preloadQuery: typeof preloader = (...args) => - replaceStreamWithPromiscade(preloader(...args)); - - context.set(apolloContext, { - preloadQuery, - }); - }; +export function initializeApolloContext( + client: ApolloClient, + contextMap: unstable_InitialContext = new Map() +) { + const preloader = createQueryPreloader(client); + contextMap.set(apolloContext, { client, preloadQuery: preloader }); + return contextMap; } // currently, `turbo-stream` cannot stream a `ReadableStream`. diff --git a/yarn.lock b/yarn.lock index 4eb329c3..eb4c18dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11109,8 +11109,8 @@ __metadata: linkType: hard "@vercel/react-router@npm:^1.0.2": - version: 1.0.2 - resolution: "@vercel/react-router@npm:1.0.2" + version: 1.1.0 + resolution: "@vercel/react-router@npm:1.1.0" dependencies: "@vercel/static-config": "npm:3.0.0" ts-morph: "npm:12.0.0" @@ -11120,7 +11120,7 @@ __metadata: isbot: 5 react: ">=18" react-dom: ">=18" - checksum: 10/5f7e60adb6f84473603d37e61572f24395490bce19ff0fe2fb240fa97a8be77c9e4da27ad9f45bc053d9c87f63d2068ca93027fc2014f0ac6ee0659c2e111628 + checksum: 10/d04ff10f34595b3c47f467585fc39ce233a4ec58d64f8f56dc6242cefe222d6a7ea91ad414bf70998d2b7de6ce0bd3019fde5c6dd05612e0aa9e2bc5b5329aae languageName: node linkType: hard