UNPKG

@apollo/client-integration-tanstack-start

Version:

This package provides integrations between Apollo Client and TanStack Start to support modern streaming SSR.

1 lines 18.5 kB
{"version":3,"sources":["../src/routerWithApolloClient.tsx","../src/ApolloProvider.tsx","../src/bundleInfo.ts","../src/QueryRefSerializationAdapter.tsx","../src/Transport.ts","../src/ApolloClient.tsx","../src/InMemoryCache.tsx"],"names":["React","createSerializationAdapter","event"],"mappings":";AAAA,SAAS,uCAAuC;AAChD,SAAS,4BAA4B;AAErC,OAAOA,YAAW;;;ACFlB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,OAAO,WAAW;;;ACNX,IAAM,SAAS;AAAA,EACpB,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO;AACT;;;ADmBO,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,MAGO;AACL,SACE,oCAAC,yBAAsB,YAAY,MAAM,QAAQ,WAC9C,QACH;AAEJ;AAEA,IAAM,wBAAwB,mBAE3B,CAAC,UAAU;AACZ,QAAM,YAAY,MAAM,QAAQ;AAChC,YAAU,WAAW,kCAAkC;AACvD,MAAI,4BAA4B,WAAW;AACzC;AAAA,MACE,MAAM;AAAA,MACN;AAAA,IACF;AACA,UAAM,+BAA+B,UAAU,sBAAsB;AAAA,EACvE,OAAO;AACL;AAAA,MACE,MAAM,gBAAgB,MAAM;AAAA,MAC5B;AAAA,IACF;AACA,cAAU,eAAe,MAAM;AAC/B,cAAU,wBAAwB,MAAM;AAAA,EAC1C;AAEA,SACE,oCAAC,qBAAqB,UAArB,EAA8B,OAAO,aACnC,MAAM,QACT;AAEJ,CAAC;AACD,sBAAsB,OAAO;;;AEhE7B;AAAA,EAGE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAkC;AAEpC,IAAM,kCAAkC,CAAC,iBAC9C,2BAGE;AAAA,EACA,KAAK;AAAA,EACL,MAAM;AAAA,EACN,iBAAiB,OAAO;AACtB,UAAM,WAAW;AAAA,MACf,oBAAoB;AAAA,IACtB;AACA,8BAA0B,UAAU,YAAY;AAChD,WAAO;AAAA,EACT;AAAA,EACA,eAAe,UAAU;AACvB,WAAO,SAAS;AAAA,EAClB;AACF,CAAC;;;ACpBH,SAAS,8BAAAC,mCAAkC;AAE3C,OAAOD,YAAW;AAeX,IAAM,gCAAgCC,4BAG3C;AAAA,EACA,KAAK;AAAA,EACL,KAAK,OAAiC;AACpC,WAAO,iBAAiB;AAAA,EAC1B;AAAA,EACA,eAAe,OAAO;AAEpB,WAAQ,MAA0B;AAAA,EACpC;AAAA,EACA,iBAAiB,OAAO;AACtB,WAAO,IAAI,gBAAgB,KAAK;AAAA,EAClC;AACF,CAAC;AAEM,IAAM,kBAAN,MAA0D;AAAA,EAC/D;AAAA,EACQ;AAAA,EACA,iBAAiB,oBAAI,IAA8C;AAAA,EAE3E,cAAc;AACZ,SAAK,SAAS,IAAI,eAAwC;AAAA,MACxD,OAAO,CAAC,eAAe;AACrB,aAAK,aAAa;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc;AAAA,EACtB,oBAAoB;AAClB,SAAK,cAAc;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EACQ,SAAS;AAAA,EACT,kBAAkB;AACxB,QAAI,KAAK,eAAe,KAAK,eAAe,SAAS,KAAK,CAAC,KAAK,QAAQ;AACtE,WAAK,WAAW,MAAM;AACtB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,yBAAyB,CAAC;AAAA,IACxB;AAAA,IACA;AAAA,EACF,MAGY;AACV,SAAK,WAAW,QAAQ,KAAK;AAC7B,SAAK,eAAe,IAAI,KAAK;AAC7B,UAAM,WAAW,MAAM;AACrB,WAAK,eAAe,OAAO,KAAK;AAChC,WAAK,gBAAgB;AAAA,IACvB;AACA,eAAW,UAAU;AAAA,MACnB,MAAM,CAACC,WAAU;AACf,YAAI,CAAC,KAAK;AAAQ,eAAK,WAAW,QAAQA,MAAK;AAAA,MACjD;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,YAAY,IAAY,OAAgB;AACtC,SAAK,WAAW,QAAQ,EAAE,MAAM,SAAS,IAAI,MAAM,CAAC;AAAA,EACtD;AAAA,EAEA,oBAAoB,CAAI,UAAiC;AACvD,UAAM,KAAKF,OAAM,MAAM;AACvB,SAAK,YAAY,IAAI,KAAK;AAC1B,WAAOA,OAAM,OAAO,KAAK;AAAA,EAC3B;AACF;AAEO,IAAM,kBAAN,MAA0D;AAAA,EACvD,iBAA+B,CAAC;AAAA,EAChC,iBAA0C,CAAC;AAAA,EACnD,YAAY,gBAA6B;AACvC,SAAK,QAAQ,cAAc;AAAA,EAC7B;AAAA,EAEA,MAAc,QAAQ,QAAiD;AACrE,qBAAiB,SAAS,QAEvB;AACD,UAAI,MAAM,SAAS,SAAS;AAC1B,aAAK,eAAe,MAAM,EAAE,IAAI,MAAM;AAAA,MACxC,OAAO;AACL,aAAK,eAAe,KAAK,KAAK;AAAA,MAChC;AAAA,IACF;AACA,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAW,aAAa,UAAuC;AAC7D,QAAI;AACJ,WAAQ,QAAQ,KAAK,eAAe,MAAM,GAAI;AAC5C,eAAS,KAAK;AAAA,IAChB;AACA,SAAK,eAAe,OAAO,IAAI,WAAyB;AACtD,iBAAWE,UAAS,QAAQ;AAC1B,iBAASA,MAAK;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAEO;AAAA,EAEA,iBAAoB,IAA2B;AACpD,WAAO,KAAK,eAAe,EAAE;AAAA,EAC/B;AAAA,EACO,oBAAoB,IAAY;AACrC,WAAO,KAAK,eAAe,EAAE;AAAA,EAC/B;AAAA,EAEA,oBAAoB,CAAI,UAAiC;AACvD,UAAM,KAAKF,OAAM,MAAM;AACvB,UAAM,gBAAgB,KAAK,iBAAoB,EAAE;AACjD,UAAM,YAAY,kBAAkB,SAAY,gBAAgB;AAChE,IAAAA,OAAM,UAAU,MAAM;AACpB,WAAK,oBAAoB,EAAE;AAAA,IAC7B,GAAG,CAAC,EAAE,CAAC;AACP,WAAOA,OAAM,OAAO,SAAS;AAAA,EAC/B;AACF;;;AJ3IO,SAAS,uBACd,QAGA,cACS;AACT,SAAO,QAAQ,YAAY,CAAC;AAC5B,SAAO,QAAQ,QAAQ,eAAe;AACtC,SAAO,QAAQ,QAAQ,eAAgB,OAAO,WAC1C,gCAAgC,cAAc,EAAE,iBAAiB,KAAK,CAAC,IACvE;AAAA,IACE;AAAA,EACF;AAEJ,QAAM,kBAAkB,CAAC;AAEzB,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,cAAc,OAAO,QAAQ;AAEnC,MAAI,OAAO,UAAU;AACnB,UAAM,wBAAwB,IAAI,gBAAgB;AAClD,oBAAgB,YAAY;AAC5B,WAAO,QAAQ,YAAY,YAAY;AACrC,aAAO,UAAW;AAAA,QAAiB,MACjC,sBAAsB,kBAAkB;AAAA,MAC1C;AACA,aAAO;AAAA,QACL,GAAI,MAAM,cAAc;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AACL,WAAO,QAAQ,UAAU,CAAC,oBAAoB;AAC5C,sBAAgB,YAAY,gBAAgB;AAC5C,aAAO,YAAY,eAAe;AAAA,IACpC;AAAA,EACF;AAEA,SAAO,QAAQ,wBAAwB;AAAA,IACrC,GAAI,OAAO,QAAQ,yBAAyB,CAAC;AAAA,IAC7C,gCAAgC,YAAY;AAAA,IAC5C;AAAA,EACF;AAIA,QAAM,oBAAoB,OAAO,QAAQ,aAAaA,OAAM;AAE5D,SAAO,QAAQ,YAAY,CAAC,EAAE,SAAS,MACrC,gBAAAA,OAAA,cAAC,kBAAe,QAAQ,cAAc,SAAS,mBAC7C,gBAAAA,OAAA,cAAC,yBAAmB,QAAS,CAC/B;AAGF,SAAO;AACT;AAEA,uBAAuB,iBAAiB;AAAA,EACtC,cAAc,IAAI,MAAM,CAAC,GAAU,EAAE,KAAK,cAAc,CAAC;AAAA,EACzD,cAAc,IAAI,MAAM,eAAsB,EAAE,KAAK,cAAc,CAAC;AACtE;AAEA,SAAS,gBAAuB;AAC9B,QAAM,IAAI;AAAA,IACR;AAAA;AAAA;AAAA,EAGF,KAAK;AAAA,EACL;AACF;;;AKhFA,SAAS,gBAAgB,4BAA4B;AA2C9C,IAAM,eAAN,cAA2B,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrD,OAAgB,OAAO;AACzB;;;AClDA,SAAS,iBAAiB,6BAA6B;AAUhD,IAAM,gBAAN,cAA4B,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,OAAgB,OAAO;AACzB","sourcesContent":["import { createTransportedQueryPreloader } from \"@apollo/client-react-streaming\";\nimport { createQueryPreloader } from \"@apollo/client/react\";\nimport type { AnyRouter } from \"@tanstack/router-core\";\nimport React from \"react\";\nimport type { ApolloClientIntegration } from \"./ApolloClientIntegration.js\";\nimport { ApolloProvider } from \"./ApolloProvider.js\";\nimport type { ApolloClient } from \"./index.js\";\nimport { getQueryRefSerializationAdapter } from \"./QueryRefSerializationAdapter.js\";\nimport { ServerTransport, transportSerializationAdapter } from \"./Transport.js\";\n\n/** @public */\nexport function routerWithApolloClient<TRouter extends AnyRouter>(\n router: TRouter[\"options\"][\"context\"] extends ApolloClientIntegration.RouterContext\n ? TRouter\n : never,\n apolloClient: ApolloClient\n): TRouter {\n router.options.context ??= {};\n router.options.context.apolloClient = apolloClient;\n router.options.context.preloadQuery = (router.isServer\n ? createTransportedQueryPreloader(apolloClient, { prepareForReuse: true })\n : createQueryPreloader(\n apolloClient\n )) as unknown as ApolloClientIntegration.PreloadTransportedQueryFunction;\n\n const providerContext = {} as ApolloProvider.Context;\n\n const ogHydrate = router.options.hydrate;\n const ogDehydrate = router.options.dehydrate;\n\n if (router.isServer) {\n const apolloClientTransport = new ServerTransport();\n providerContext.transport = apolloClientTransport;\n router.options.dehydrate = async () => {\n router.serverSsr!.onRenderFinished(() =>\n apolloClientTransport.closeOnceFinished()\n );\n return {\n ...(await ogDehydrate?.()),\n apolloClientTransport,\n };\n };\n } else {\n router.options.hydrate = (dehydratedState) => {\n providerContext.transport = dehydratedState.apolloClientTransport;\n return ogHydrate?.(dehydratedState);\n };\n }\n\n router.options.serializationAdapters = [\n ...(router.options.serializationAdapters ?? []),\n getQueryRefSerializationAdapter(apolloClient),\n transportSerializationAdapter,\n ];\n\n // necessary to have `RouterOptionsExtensions` applied to `router.options` - otherwise the build fails\n type _ForceRouterTypes = typeof import(\"@tanstack/react-router\");\n const PreviousInnerWrap = router.options.InnerWrap || React.Fragment;\n // eslint-disable-next-line react/display-name\n router.options.InnerWrap = ({ children }) => (\n <ApolloProvider client={apolloClient} context={providerContext}>\n <PreviousInnerWrap>{children}</PreviousInnerWrap>\n </ApolloProvider>\n );\n\n return router;\n}\n\nrouterWithApolloClient.defaultContext = {\n apolloClient: new Proxy({} as any, { get: contextAccess }),\n preloadQuery: new Proxy(contextAccess as any, { get: contextAccess }),\n} satisfies ApolloClientIntegration.RouterContext as ApolloClientIntegration.RouterContext;\n\nfunction contextAccess(): never {\n throw new Error(\n `\nCould not find Apollo Client in router context. \nDid you forget to wrap your router in a \\`routerWithApolloClient\\` call before returning it from \\`getRouter\\`?\n`.trim()\n );\n}\n","import type { QueryEvent } from \"@apollo/client-react-streaming\";\nimport {\n DataTransportContext,\n WrapApolloProvider,\n} from \"@apollo/client-react-streaming\";\nimport { invariant } from \"@apollo/client/utilities/invariant\";\nimport React from \"react\";\nimport { bundle } from \"./bundleInfo.js\";\nimport type { ClientTransport, ServerTransport } from \"./Transport.js\";\nimport type { ApolloClient } from \"./ApolloClient.js\";\n\ndeclare global {\n interface Window {\n __APOLLO_EVENTS__?: Pick<QueryEvent[], \"push\">;\n }\n}\n\nexport declare namespace ApolloProvider {\n export interface Context {\n transport: ServerTransport | ClientTransport;\n }\n}\n\nexport const ApolloProvider = ({\n client,\n context,\n children,\n}: React.PropsWithChildren<{\n client: ApolloClient;\n context: ApolloProvider.Context;\n}>) => {\n return (\n <WrappedApolloProvider makeClient={() => client} context={context}>\n {children}\n </WrappedApolloProvider>\n );\n};\n\nconst WrappedApolloProvider = WrapApolloProvider<{\n context: ApolloProvider.Context;\n}>((props) => {\n const transport = props.context.transport;\n invariant(transport, \"No apolloClientTransport defined\");\n if (\"dispatchRequestStarted\" in transport) {\n invariant(\n props.registerDispatchRequestStarted,\n \"Browser bundle loaded in SSR\"\n );\n props.registerDispatchRequestStarted(transport.dispatchRequestStarted);\n } else {\n invariant(\n props.onQueryEvent && props.rerunSimulatedQueries,\n \"SSR bundle loaded in Browser\"\n );\n transport.onQueryEvent = props.onQueryEvent;\n transport.rerunSimulatedQueries = props.rerunSimulatedQueries;\n }\n\n return (\n <DataTransportContext.Provider value={transport}>\n {props.children}\n </DataTransportContext.Provider>\n );\n});\nWrappedApolloProvider.info = bundle;\n","export const bundle = {\n pkg: \"@apollo/client-integration-tanstack-start\",\n client: \"ApolloClient\",\n cache: \"InMemoryCache\",\n};\n","import {\n type ApolloClient,\n type TransportedQueryRef,\n isTransportedQueryRef,\n reviveTransportedQueryRef,\n} from \"@apollo/client-react-streaming\";\nimport { createSerializationAdapter } from \"@tanstack/router-core\";\n\nexport const getQueryRefSerializationAdapter = (apolloClient: ApolloClient) =>\n createSerializationAdapter<\n TransportedQueryRef,\n TransportedQueryRef[\"$__apollo_queryRef\"]\n >({\n key: \"apollo-query-ref\",\n test: isTransportedQueryRef,\n fromSerializable(value) {\n const queryRef = {\n $__apollo_queryRef: value,\n };\n reviveTransportedQueryRef(queryRef, apolloClient);\n return queryRef;\n },\n toSerializable(queryRef) {\n return queryRef.$__apollo_queryRef;\n },\n });\n","import type {\n DataTransportContext,\n QueryEvent,\n} from \"@apollo/client-react-streaming\";\n\nimport { createSerializationAdapter } from \"@tanstack/router-core\";\nimport type { Observable } from \"rxjs\";\nimport React from \"react\";\n\ninterface ValueEvent {\n type: \"value\";\n value: unknown;\n id: string;\n}\n\ntype Transported = ReadableStream<QueryEvent | ValueEvent>;\n\ntype DataTransportAbstraction =\n typeof DataTransportContext extends React.Context<infer T>\n ? NonNullable<T>\n : never;\n\nexport const transportSerializationAdapter = createSerializationAdapter<\n ServerTransport | ClientTransport,\n Transported\n>({\n key: \"apollo-transport\",\n test(value): value is ServerTransport {\n return value instanceof ServerTransport;\n },\n toSerializable(value) {\n // TS is a bit too strict about serializability here - some values are just `unknown`, but definitely serializable\n return (value as ServerTransport).stream satisfies Transported as any;\n },\n fromSerializable(value) {\n return new ClientTransport(value);\n },\n});\n\nexport class ServerTransport implements DataTransportAbstraction {\n stream: Transported;\n private controller!: ReadableStreamDefaultController<QueryEvent | ValueEvent>;\n private ongoingStreams = new Set<Extract<QueryEvent, { type: \"started\" }>>();\n\n constructor() {\n this.stream = new ReadableStream<QueryEvent | ValueEvent>({\n start: (controller) => {\n this.controller = controller;\n },\n });\n }\n\n private shouldClose = false;\n closeOnceFinished() {\n this.shouldClose = true;\n this.closeIfFinished();\n }\n private closed = false;\n private closeIfFinished() {\n if (this.shouldClose && this.ongoingStreams.size === 0 && !this.closed) {\n this.controller.close();\n this.closed = true;\n }\n }\n\n dispatchRequestStarted = ({\n event,\n observable,\n }: {\n event: Extract<QueryEvent, { type: \"started\" }>;\n observable: Observable<Exclude<QueryEvent, { type: \"started\" }>>;\n }): void => {\n this.controller.enqueue(event);\n this.ongoingStreams.add(event);\n const finalize = () => {\n this.ongoingStreams.delete(event);\n this.closeIfFinished();\n };\n observable.subscribe({\n next: (event) => {\n if (!this.closed) this.controller.enqueue(event);\n },\n error: finalize,\n complete: finalize,\n });\n };\n\n streamValue(id: string, value: unknown) {\n this.controller.enqueue({ type: \"value\", id, value });\n }\n\n useStaticValueRef = <T>(value: T): React.RefObject<T> => {\n const id = React.useId();\n this.streamValue(id, value);\n return React.useRef(value);\n };\n}\n\nexport class ClientTransport implements DataTransportAbstraction {\n private bufferedEvents: QueryEvent[] = [];\n private receivedValues: Record<string, unknown> = {};\n constructor(incomingStream: Transported) {\n this.consume(incomingStream);\n }\n\n private async consume(stream: ReadableStream<QueryEvent | ValueEvent>) {\n for await (const event of stream as any as AsyncIterable<\n QueryEvent | ValueEvent\n >) {\n if (event.type === \"value\") {\n this.receivedValues[event.id] = event.value;\n } else {\n this.bufferedEvents.push(event);\n }\n }\n this.rerunSimulatedQueries?.();\n }\n\n // this will be set from the `WrapApolloProvider` data transport\n public set onQueryEvent(callback: (event: QueryEvent) => void) {\n let event: QueryEvent | undefined;\n while ((event = this.bufferedEvents.shift())) {\n callback(event);\n }\n this.bufferedEvents.push = (...events: QueryEvent[]) => {\n for (const event of events) {\n callback(event);\n }\n return 0;\n };\n }\n // this will be set from the `WrapApolloProvider` data transport\n public rerunSimulatedQueries?: () => void;\n\n public getStreamedValue<T>(id: string): T | undefined {\n return this.receivedValues[id] as T | undefined;\n }\n public deleteStreamedValue(id: string) {\n delete this.receivedValues[id];\n }\n\n useStaticValueRef = <T>(value: T): React.RefObject<T> => {\n const id = React.useId();\n const streamedValue = this.getStreamedValue<T>(id);\n const dataValue = streamedValue !== undefined ? streamedValue : value;\n React.useEffect(() => {\n this.deleteStreamedValue(id);\n }, [id]);\n return React.useRef(dataValue);\n };\n}\n","import { ApolloClient as UpstreamApolloClient } from \"@apollo/client-react-streaming\";\nimport { bundle } from \"./bundleInfo.js\";\n\n/** @public */\nexport declare namespace ApolloClient {\n /** @public */\n export interface Options extends UpstreamApolloClient.Options {}\n /*\n We can't currently re-export these types from the upstream ApolloClient implementation because the build \n tooling doesn't support that:\n > DTS Build error\n > Error: namespace child (hoisting) not supported yet\n\n We could re-export them by defining them as new types that reference the originals, but that would require us\n to keep generics in sync and would either erase the docblocks or require us to duplicate them \n (and they would go out of sync).\n If you need any of the other types, please use the `ApolloClient` namespace exported from `@apollo/client` directly.\n */\n // export import DefaultOptions = UpstreamApolloClient.DefaultOptions;\n // export import DevtoolsOptions = UpstreamApolloClient.DevtoolsOptions;\n // export import MutateOptions = UpstreamApolloClient.MutateOptions;\n // export import MutateResult = UpstreamApolloClient.MutateResult;\n // export import QueryOptions = UpstreamApolloClient.QueryOptions;\n // export import QueryResult = UpstreamApolloClient.QueryResult;\n // export import RefetchQueriesOptions = UpstreamApolloClient.RefetchQueriesOptions;\n // export import RefetchQueriesResult = UpstreamApolloClient.RefetchQueriesResult;\n // export import SubscribeOptions = UpstreamApolloClient.SubscribeOptions;\n // export import SubscribeResult = UpstreamApolloClient.SubscribeResult;\n // export import WatchFragmentOptions = UpstreamApolloClient.WatchFragmentOptions;\n // export import WatchFragmentResult = UpstreamApolloClient.WatchFragmentResult;\n // export import WatchQueryOptions = UpstreamApolloClient.WatchQueryOptions;\n // export import ReadQueryOptions = UpstreamApolloClient.ReadQueryOptions;\n // export import WriteQueryOptions = UpstreamApolloClient.WriteQueryOptions;\n // export import WriteFragmentOptions = UpstreamApolloClient.WriteFragmentOptions;\n}\n\n/**\n * A version of `ApolloClient` to be used with TanStack Start.\n *\n * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/core/ApolloClient | the Apollo Client API documentation}.\n *\n * @public\n */\nexport class ApolloClient extends UpstreamApolloClient {\n /**\n * Information about the current package and it's export names, for use in error messages.\n *\n * @internal\n */\n static readonly info = bundle;\n}\n","import { InMemoryCache as UpstreamInMemoryCache } from \"@apollo/client-react-streaming\";\nimport { bundle } from \"./bundleInfo.js\";\n\n/**\n * A version of `InMemoryCache` to be used with TanStack Start.\n *\n * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/cache/InMemoryCache | the Apollo Client API documentation}.\n *\n * @public\n */\nexport class InMemoryCache extends UpstreamInMemoryCache {\n /**\n * Information about the current package and it's export names, for use in error messages.\n *\n * @internal\n */\n static readonly info = bundle;\n}\n"]}