diff --git a/examples/react/start-basic-apollo-client/.gitignore b/examples/react/start-basic-apollo-client/.gitignore new file mode 100644 index 0000000000..be342025da --- /dev/null +++ b/examples/react/start-basic-apollo-client/.gitignore @@ -0,0 +1,22 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +.output +.vinxi + +/build/ +/api/ +/server/build +/public/build +.vinxi +# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/examples/react/start-basic-apollo-client/.prettierignore b/examples/react/start-basic-apollo-client/.prettierignore new file mode 100644 index 0000000000..2be5eaa6ec --- /dev/null +++ b/examples/react/start-basic-apollo-client/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/examples/react/start-basic-apollo-client/README.md b/examples/react/start-basic-apollo-client/README.md new file mode 100644 index 0000000000..eb580a5bf8 --- /dev/null +++ b/examples/react/start-basic-apollo-client/README.md @@ -0,0 +1,72 @@ +# Welcome to TanStack.com! + +This site is built with TanStack Router! + +- [TanStack Router Docs](https://tanstack.com/router) + +It's deployed automagically with Vercel! + +- [Vercel](https://vercel.com/) + +## Development + +From your terminal: + +```sh +pnpm install +pnpm dev +``` + +This starts your app in development mode, rebuilding assets on file changes. + +## Editing and previewing the docs of TanStack projects locally + +The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app. +In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system. + +Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally : + +1. Create a new directory called `tanstack`. + +```sh +mkdir tanstack +``` + +2. Enter the directory and clone this repo and the repo of the project there. + +```sh +cd tanstack +git clone git@github.com:TanStack/tanstack.com.git +git clone git@github.com:TanStack/form.git +``` + +> [!NOTE] +> Your `tanstack` directory should look like this: +> +> ``` +> tanstack/ +> | +> +-- form/ +> | +> +-- tanstack.com/ +> ``` + +> [!WARNING] +> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found. + +3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: + +```sh +cd tanstack.com +pnpm i +# The app will run on https://localhost:3000 by default +pnpm dev +``` + +4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`. + +> [!NOTE] +> The updated pages need to be manually reloaded in the browser. + +> [!WARNING] +> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page! diff --git a/examples/react/start-basic-apollo-client/app.config.ts b/examples/react/start-basic-apollo-client/app.config.ts new file mode 100644 index 0000000000..732f04eabe --- /dev/null +++ b/examples/react/start-basic-apollo-client/app.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from '@tanstack/start/config' +import tsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + vite: { + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + ], + }, +}) diff --git a/examples/react/start-basic-apollo-client/app/api.ts b/examples/react/start-basic-apollo-client/app/api.ts new file mode 100644 index 0000000000..0caf78bb9d --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/api.ts @@ -0,0 +1,6 @@ +import { + createStartAPIHandler, + defaultAPIFileRouteHandler, +} from '@tanstack/start/api' + +export default createStartAPIHandler(defaultAPIFileRouteHandler) diff --git a/examples/react/start-basic-apollo-client/app/apollo/ApolloClient.tsx b/examples/react/start-basic-apollo-client/app/apollo/ApolloClient.tsx new file mode 100644 index 0000000000..331ee73084 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/apollo/ApolloClient.tsx @@ -0,0 +1 @@ +export { ApolloClient } from '@apollo/client-react-streaming' diff --git a/examples/react/start-basic-apollo-client/app/apollo/ApolloProvider.tsx b/examples/react/start-basic-apollo-client/app/apollo/ApolloProvider.tsx new file mode 100644 index 0000000000..a0585395bd --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/apollo/ApolloProvider.tsx @@ -0,0 +1,105 @@ +import { + DataTransportContext, + WrapApolloProvider, +} from '@apollo/client-react-streaming' +import { useRouter } from '@tanstack/react-router' +import { useCallback, useEffect, useId, useMemo, useRef } from 'react' +import type { QueryEvent } from '@apollo/client-react-streaming' + +const APOLLO_EVENT_PREFIX = '@@apollo.event/' +const APOLLO_HOOK_PREFIX = '@@apollo.hook/' + +const handledEvents = new WeakSet() + +export const ApolloProvider = (props: React.PropsWithChildren) => { + const router = useRouter() + return ( + router.options.context.apolloClient} + > + {props.children} + + ) +} + +const WrappedApolloProvider = WrapApolloProvider((props) => { + const router = useRouter() + + const consumeBackPressure = useCallback(() => { + for (const key of router.streamedKeys) { + if (key.startsWith(APOLLO_EVENT_PREFIX)) { + const streamedValue = router.getStreamedValue(key) + if ( + streamedValue && + typeof streamedValue === 'object' && + !handledEvents.has(streamedValue) && + props.onQueryEvent + ) { + handledEvents.add(streamedValue) + props.onQueryEvent(streamedValue as QueryEvent) + } + } + } + }, [router, props.onQueryEvent]) + + if (router.isServer) { + props.registerDispatchRequestStarted!(({ event, observable }) => { + const id = crypto.randomUUID() as typeof event.id + event.id = id + router.streamValue( + `${APOLLO_EVENT_PREFIX}${event.id}/${event.type}`, + event satisfies QueryEvent, + ) + observable.subscribe({ + next(event) { + event.id = id + router.streamValue( + `${APOLLO_EVENT_PREFIX}${event.id}/${event.type}`, + event satisfies QueryEvent, + ) + }, + }) + }) + } else { + // this needs to happen synchronously before the render continues + // so "loading" is kicked off before the respecitve component is rendered + consumeBackPressure() + } + + useEffect(() => { + consumeBackPressure() + return router.subscribe('onStreamedValue', ({ key }) => { + if (!key.startsWith(APOLLO_EVENT_PREFIX)) return + const streamedValue = router.getStreamedValue(key) + if (streamedValue && props.onQueryEvent) { + handledEvents.add(streamedValue) + props.onQueryEvent(streamedValue as QueryEvent) + } + }) + }, [consumeBackPressure, router, props.onQueryEvent]) + + const dataTransport = useMemo(() => ({ useStaticValueRef }), []) + + return ( + + {props.children} + + ) +}) + +function useStaticValueRef(value: T) { + const router = useRouter() + const key = APOLLO_HOOK_PREFIX + useId() + const dataValue = + !router.isServer && router.streamedKeys.has(key) + ? (router.getStreamedValue(key) as T) + : value + const dataRef = useRef(dataValue) + + if (router.isServer) { + if (!router.streamedKeys.has(key)) { + router.streamValue(key, value) + } + } + return dataRef +} diff --git a/examples/react/start-basic-apollo-client/app/apollo/createQueryPreloader.ts b/examples/react/start-basic-apollo-client/app/apollo/createQueryPreloader.ts new file mode 100644 index 0000000000..50ff7cd241 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/apollo/createQueryPreloader.ts @@ -0,0 +1 @@ +export { createTransportedQueryPreloader } from '@apollo/client-react-streaming' diff --git a/examples/react/start-basic-apollo-client/app/apollo/index.ts b/examples/react/start-basic-apollo-client/app/apollo/index.ts new file mode 100644 index 0000000000..5a7a497bf1 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/apollo/index.ts @@ -0,0 +1,73 @@ +import { defaultTransformer } from '@tanstack/react-router' +import { + isTransportedQueryRef, + reviveTransportedQueryRef, +} from '@apollo/client-react-streaming' +import { createTransportedQueryPreloader } from './createQueryPreloader' +import { ApolloProvider } from './ApolloProvider' +import type { PreloadQueryFunction } from '@apollo/client' +import type { AnyRouter } from '@tanstack/react-router' +import type { ApolloClient } from './ApolloClient' + +export function routerWithApolloClient( + router: TRouter['options']['context'] extends { + apolloClient: ApolloClient + preloadQuery: PreloadQueryFunction + } + ? TRouter + : never, + apolloClient: ApolloClient, +): TRouter { + // @ts-ignore unavoidable due to the ternary in arguments + router.options.context.apolloClient = apolloClient + + // it would be nice to do this in the long run + // @ts-ignore unavoidable due to the ternary in arguments + // router.options.context.preloadQuery = router.isServer + // ? createTransportedQueryPreloader(apolloClient) + // : createQueryPreloader(apolloClient) + + // @ts-ignore unavoidable due to the ternary in arguments + router.options.context.preloadQuery = + createTransportedQueryPreloader(apolloClient) + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const previousTransformer = router.options.transformer || defaultTransformer + + router.options.transformer = { + stringify(value) { + return previousTransformer.stringify(value) + }, + parse(str) { + const transformed = previousTransformer.parse(str) + + JSON.stringify(transformed, (_, value) => { + if (isTransportedQueryRef(value)) { + // Due to the timing in `afterHydrate`, the stream at this point will still be an empty object + // to be replaced by the `extracted` stream value a moment later + // Nonetheless, we want to kick off hydration of the queryRef here already, + // so query deduplication kicks in. + // So we create an intermediate `TransformStream` that is exposed via a getter, + // while at the same time, a setter is put into place that will start piping the + // incoming stream into our intermediate stream as soon as it is set. + // The check for `instanceof ReadableStream` is here just in case that the timing changes + // in the future and we don't need to do this anymore. + const intermediateStream = new TransformStream() + Object.defineProperty(value.$__apollo_queryRef, 'stream', { + get: () => intermediateStream.readable, + set: (value: ReadableStream) => { + value.pipeTo(intermediateStream.writable) + }, + }) + reviveTransportedQueryRef(value, apolloClient) + } + }) + + return transformed + }, + } + + router.options.InnerWrap = ApolloProvider + + return router +} diff --git a/examples/react/start-basic-apollo-client/app/client.tsx b/examples/react/start-basic-apollo-client/app/client.tsx new file mode 100644 index 0000000000..7bde1ff01e --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/client.tsx @@ -0,0 +1,8 @@ +/// +import { hydrateRoot } from 'react-dom/client' +import { StartClient } from '@tanstack/start' +import { createRouter } from './router' + +const router = createRouter() + +hydrateRoot(document.getElementById('root')!, ) diff --git a/examples/react/start-basic-apollo-client/app/components/DefaultCatchBoundary.tsx b/examples/react/start-basic-apollo-client/app/components/DefaultCatchBoundary.tsx new file mode 100644 index 0000000000..f0ce51dc57 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + ErrorComponentProps, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error(error) + + return ( +
+ +
+ + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/components/NotFound.tsx b/examples/react/start-basic-apollo-client/app/components/NotFound.tsx new file mode 100644 index 0000000000..7b54fa5680 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/react-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routeTree.gen.ts b/examples/react/start-basic-apollo-client/app/routeTree.gen.ts new file mode 100644 index 0000000000..24bdc7404e --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routeTree.gen.ts @@ -0,0 +1,472 @@ +/* prettier-ignore-start */ + +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file is auto-generated by TanStack Router + +// Import Routes + +import { Route as rootRoute } from './routes/__root' +import { Route as UsersImport } from './routes/users' +import { Route as RedirectImport } from './routes/redirect' +import { Route as PostsImport } from './routes/posts' +import { Route as DeferredImport } from './routes/deferred' +import { Route as LayoutImport } from './routes/_layout' +import { Route as IndexImport } from './routes/index' +import { Route as UsersIndexImport } from './routes/users.index' +import { Route as PostsIndexImport } from './routes/posts.index' +import { Route as UsersUserIdImport } from './routes/users.$userId' +import { Route as PostsPostIdImport } from './routes/posts.$postId' +import { Route as LayoutLayout2Import } from './routes/_layout/_layout-2' +import { Route as PostsPostIdDeepImport } from './routes/posts_.$postId.deep' +import { Route as LayoutLayout2LayoutBImport } from './routes/_layout/_layout-2/layout-b' +import { Route as LayoutLayout2LayoutAImport } from './routes/_layout/_layout-2/layout-a' + +// Create/Update Routes + +const UsersRoute = UsersImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRoute, +} as any) + +const RedirectRoute = RedirectImport.update({ + id: '/redirect', + path: '/redirect', + getParentRoute: () => rootRoute, +} as any) + +const PostsRoute = PostsImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRoute, +} as any) + +const DeferredRoute = DeferredImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRoute, +} as any) + +const LayoutRoute = LayoutImport.update({ + id: '/_layout', + getParentRoute: () => rootRoute, +} as any) + +const IndexRoute = IndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRoute, +} as any) + +const UsersIndexRoute = UsersIndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, +} as any) + +const PostsIndexRoute = PostsIndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, +} as any) + +const UsersUserIdRoute = UsersUserIdImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, +} as any) + +const PostsPostIdRoute = PostsPostIdImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) + +const LayoutLayout2Route = LayoutLayout2Import.update({ + id: '/_layout-2', + getParentRoute: () => LayoutRoute, +} as any) + +const PostsPostIdDeepRoute = PostsPostIdDeepImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRoute, +} as any) + +const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBImport.update({ + id: '/layout-b', + path: '/layout-b', + getParentRoute: () => LayoutLayout2Route, +} as any) + +const LayoutLayout2LayoutARoute = LayoutLayout2LayoutAImport.update({ + id: '/layout-a', + path: '/layout-a', + getParentRoute: () => LayoutLayout2Route, +} as any) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/_layout': { + id: '/_layout' + path: '' + fullPath: '' + preLoaderRoute: typeof LayoutImport + parentRoute: typeof rootRoute + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredImport + parentRoute: typeof rootRoute + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsImport + parentRoute: typeof rootRoute + } + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectImport + parentRoute: typeof rootRoute + } + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersImport + parentRoute: typeof rootRoute + } + '/_layout/_layout-2': { + id: '/_layout/_layout-2' + path: '' + fullPath: '' + preLoaderRoute: typeof LayoutLayout2Import + parentRoute: typeof LayoutImport + } + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdImport + parentRoute: typeof PostsImport + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdImport + parentRoute: typeof UsersImport + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexImport + parentRoute: typeof PostsImport + } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexImport + parentRoute: typeof UsersImport + } + '/_layout/_layout-2/layout-a': { + id: '/_layout/_layout-2/layout-a' + path: '/layout-a' + fullPath: '/layout-a' + preLoaderRoute: typeof LayoutLayout2LayoutAImport + parentRoute: typeof LayoutLayout2Import + } + '/_layout/_layout-2/layout-b': { + id: '/_layout/_layout-2/layout-b' + path: '/layout-b' + fullPath: '/layout-b' + preLoaderRoute: typeof LayoutLayout2LayoutBImport + parentRoute: typeof LayoutLayout2Import + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepImport + parentRoute: typeof rootRoute + } + } +} + +// Create and export the route tree + +interface LayoutLayout2RouteChildren { + LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute + LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute +} + +const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { + LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, + LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, +} + +const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( + LayoutLayout2RouteChildren, +) + +interface LayoutRouteChildren { + LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren +} + +const LayoutRouteChildren: LayoutRouteChildren = { + LayoutLayout2Route: LayoutLayout2RouteWithChildren, +} + +const LayoutRouteWithChildren = + LayoutRoute._addFileChildren(LayoutRouteChildren) + +interface PostsRouteChildren { + PostsPostIdRoute: typeof PostsPostIdRoute + PostsIndexRoute: typeof PostsIndexRoute +} + +const PostsRouteChildren: PostsRouteChildren = { + PostsPostIdRoute: PostsPostIdRoute, + PostsIndexRoute: PostsIndexRoute, +} + +const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) + +interface UsersRouteChildren { + UsersUserIdRoute: typeof UsersUserIdRoute + UsersIndexRoute: typeof UsersIndexRoute +} + +const UsersRouteChildren: UsersRouteChildren = { + UsersUserIdRoute: UsersUserIdRoute, + UsersIndexRoute: UsersIndexRoute, +} + +const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '': typeof LayoutLayout2RouteWithChildren + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/layout-a': typeof LayoutLayout2LayoutARoute + '/layout-b': typeof LayoutLayout2LayoutBRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} + +export interface FileRoutesByTo { + '/': typeof IndexRoute + '': typeof LayoutLayout2RouteWithChildren + '/deferred': typeof DeferredRoute + '/redirect': typeof RedirectRoute + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts': typeof PostsIndexRoute + '/users': typeof UsersIndexRoute + '/layout-a': typeof LayoutLayout2LayoutARoute + '/layout-b': typeof LayoutLayout2LayoutBRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} + +export interface FileRoutesById { + __root__: typeof rootRoute + '/': typeof IndexRoute + '/_layout': typeof LayoutRouteWithChildren + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute + '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute + '/posts_/$postId/deep': typeof PostsPostIdDeepRoute +} + +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/layout-a' + | '/layout-b' + | '/posts/$postId/deep' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '' + | '/deferred' + | '/redirect' + | '/posts/$postId' + | '/users/$userId' + | '/posts' + | '/users' + | '/layout-a' + | '/layout-b' + | '/posts/$postId/deep' + id: + | '__root__' + | '/' + | '/_layout' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/_layout/_layout-2' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/_layout/_layout-2/layout-a' + | '/_layout/_layout-2/layout-b' + | '/posts_/$postId/deep' + fileRoutesById: FileRoutesById +} + +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + LayoutRoute: typeof LayoutRouteWithChildren + DeferredRoute: typeof DeferredRoute + PostsRoute: typeof PostsRouteWithChildren + RedirectRoute: typeof RedirectRoute + UsersRoute: typeof UsersRouteWithChildren + PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + LayoutRoute: LayoutRouteWithChildren, + DeferredRoute: DeferredRoute, + PostsRoute: PostsRouteWithChildren, + RedirectRoute: RedirectRoute, + UsersRoute: UsersRouteWithChildren, + PostsPostIdDeepRoute: PostsPostIdDeepRoute, +} + +export const routeTree = rootRoute + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +/* prettier-ignore-end */ + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/", + "/_layout", + "/deferred", + "/posts", + "/redirect", + "/users", + "/posts_/$postId/deep" + ] + }, + "/": { + "filePath": "index.tsx" + }, + "/_layout": { + "filePath": "_layout.tsx", + "children": [ + "/_layout/_layout-2" + ] + }, + "/deferred": { + "filePath": "deferred.tsx" + }, + "/posts": { + "filePath": "posts.tsx", + "children": [ + "/posts/$postId", + "/posts/" + ] + }, + "/redirect": { + "filePath": "redirect.tsx" + }, + "/users": { + "filePath": "users.tsx", + "children": [ + "/users/$userId", + "/users/" + ] + }, + "/_layout/_layout-2": { + "filePath": "_layout/_layout-2.tsx", + "parent": "/_layout", + "children": [ + "/_layout/_layout-2/layout-a", + "/_layout/_layout-2/layout-b" + ] + }, + "/posts/$postId": { + "filePath": "posts.$postId.tsx", + "parent": "/posts" + }, + "/users/$userId": { + "filePath": "users.$userId.tsx", + "parent": "/users" + }, + "/posts/": { + "filePath": "posts.index.tsx", + "parent": "/posts" + }, + "/users/": { + "filePath": "users.index.tsx", + "parent": "/users" + }, + "/_layout/_layout-2/layout-a": { + "filePath": "_layout/_layout-2/layout-a.tsx", + "parent": "/_layout/_layout-2" + }, + "/_layout/_layout-2/layout-b": { + "filePath": "_layout/_layout-2/layout-b.tsx", + "parent": "/_layout/_layout-2" + }, + "/posts_/$postId/deep": { + "filePath": "posts_.$postId.deep.tsx" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/examples/react/start-basic-apollo-client/app/router.tsx b/examples/react/start-basic-apollo-client/app/router.tsx new file mode 100644 index 0000000000..44df33deeb --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/router.tsx @@ -0,0 +1,30 @@ +import { createRouter as createTanStackRouter } from '@tanstack/react-router' +import { InMemoryCache } from '@apollo/client-react-streaming' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' +import { ApolloClient } from './apollo/ApolloClient' +import { routerWithApolloClient } from './apollo' + +export function createRouter() { + const apolloClient = new ApolloClient({ + cache: new InMemoryCache(), + uri: 'https://graphqlzero.almansi.me/api', + }) + const router = createTanStackRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + // will be filled by `routerWithApolloClient` + context: {} as any, + }) + + return routerWithApolloClient(router, apolloClient) +} + +declare module '@tanstack/react-router' { + interface Register { + router: ReturnType + } +} diff --git a/examples/react/start-basic-apollo-client/app/routes/__root.tsx b/examples/react/start-basic-apollo-client/app/routes/__root.tsx new file mode 100644 index 0000000000..9244426bb9 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/__root.tsx @@ -0,0 +1,143 @@ +import { + Link, + Outlet, + ScrollRestoration, + createRootRouteWithContext, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/router-devtools' +import { Body, Head, Html, Meta, Scripts } from '@tanstack/start' +import * as React from 'react' +import type { ApolloClient } from '@apollo/client-react-streaming' +import type { PreloadQueryFunction } from '@apollo/client/index.js' +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' +import { seo } from '~/utils/seo' + +export const Route = createRootRouteWithContext<{ + apolloClient: ApolloClient + preloadQuery: PreloadQueryFunction +}>()({ + meta: () => [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ...seo({ + title: + 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', + description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, + }), + ], + links: () => [ + { rel: 'stylesheet', href: appCss }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, + { rel: 'icon', href: '/favicon.ico' }, + ], + errorComponent: (props) => { + return ( + + + + ) + }, + notFoundComponent: () => , + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + ) +} + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
+ + Home + {' '} + + Posts + {' '} + + Users + {' '} + + Layout + {' '} + + Deferred + {' '} + + This Route Does Not Exist + +
+
+ {children} + + + + + + ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/_layout.tsx b/examples/react/start-basic-apollo-client/app/routes/_layout.tsx new file mode 100644 index 0000000000..02ddbb1cd9 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/_layout.tsx @@ -0,0 +1,16 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a layout
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2.tsx b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2.tsx new file mode 100644 index 0000000000..3b7dbf2903 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2.tsx @@ -0,0 +1,34 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout/_layout-2')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a nested layout
+
+ + Layout A + + + Layout B + +
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-a.tsx b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-a.tsx new file mode 100644 index 0000000000..61e19b4d9f --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-a.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ + component: LayoutAComponent, +}) + +function LayoutAComponent() { + return
I'm layout A!
+} diff --git a/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-b.tsx b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-b.tsx new file mode 100644 index 0000000000..cceed1fb9a --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/_layout/_layout-2/layout-b.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ + component: LayoutBComponent, +}) + +function LayoutBComponent() { + return
I'm layout B!
+} diff --git a/examples/react/start-basic-apollo-client/app/routes/api.users.ts b/examples/react/start-basic-apollo-client/app/routes/api.users.ts new file mode 100644 index 0000000000..3b11af9b9d --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/api.users.ts @@ -0,0 +1,17 @@ +import { json } from '@tanstack/start' +import { createAPIFileRoute } from '@tanstack/start/api' +import axios from 'redaxios' +import type { User } from '../utils/users' + +export const Route = createAPIFileRoute('/api/users')({ + GET: async ({ request }) => { + console.info('Fetching users... @', request.url) + const res = await axios.get>( + 'https://jsonplaceholder.typicode.com/users', + ) + + const list = res.data.slice(0, 10) + + return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email }))) + }, +}) diff --git a/examples/react/start-basic-apollo-client/app/routes/api/users.$id.ts b/examples/react/start-basic-apollo-client/app/routes/api/users.$id.ts new file mode 100644 index 0000000000..849d45e8d0 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/api/users.$id.ts @@ -0,0 +1,24 @@ +import { json } from '@tanstack/start' +import { createAPIFileRoute } from '@tanstack/start/api' +import axios from 'redaxios' +import type { User } from '../../utils/users' + +export const Route = createAPIFileRoute('/api/users/$id')({ + GET: async ({ request, params }) => { + console.info(`Fetching users by id=${params.id}... @`, request.url) + try { + const res = await axios.get( + 'https://jsonplaceholder.typicode.com/users/' + params.id, + ) + + return json({ + id: res.data.id, + name: res.data.name, + email: res.data.email, + }) + } catch (e) { + console.error(e) + return json({ error: 'User not found' }, { status: 404 }) + } + }, +}) diff --git a/examples/react/start-basic-apollo-client/app/routes/deferred.tsx b/examples/react/start-basic-apollo-client/app/routes/deferred.tsx new file mode 100644 index 0000000000..8627bc6ba6 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/deferred.tsx @@ -0,0 +1,60 @@ +import { Await, createFileRoute, defer } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/start' +import { Suspense, useState } from 'react' + +const personServerFn = createServerFn('GET', (name: string) => { + return { name, randomNumber: Math.floor(Math.random() * 100) } +}) + +const slowServerFn = createServerFn('GET', async (name: string) => { + await new Promise((r) => setTimeout(r, 1000)) + return { name, randomNumber: Math.floor(Math.random() * 100) } +}) + +export const Route = createFileRoute('/deferred')({ + loader: async () => { + return { + deferredStuff: defer( + new Promise((r) => + setTimeout(() => r('Hello deferred!'), 2000), + ), + ), + deferredPerson: defer(slowServerFn('Tanner Linsley')), + person: await personServerFn('John Doe'), + } + }, + component: Deferred, +}) + +function Deferred() { + const [count, setCount] = useState(0) + const { deferredStuff, deferredPerson, person } = Route.useLoaderData() + + return ( +
+
+ {person.name} - {person.randomNumber} +
+ Loading person...
}> + ( +
+ {data.name} - {data.randomNumber} +
+ )} + /> + + Loading stuff...}> +

{data}

} + /> +
+
Count: {count}
+
+ +
+ + ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/index.tsx b/examples/react/start-basic-apollo-client/app/routes/index.tsx new file mode 100644 index 0000000000..3007a7ab07 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/index.tsx @@ -0,0 +1,29 @@ +import { createFileRoute } from '@tanstack/react-router' +import { + gql, + TypedDocumentNode, + useSuspenseQuery, +} from '@apollo/client/index.js' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +const GET_LATEST_PRODUCTS: TypedDocumentNode<{ + products: { id: string }[] +}> = gql` + query HomepageProducts { + products { + id + } + } +` + +function Home() { + const { data } = useSuspenseQuery(GET_LATEST_PRODUCTS) + return ( +
+

Welcome Home!!! {JSON.stringify(data)}

+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/posts.$postId.tsx b/examples/react/start-basic-apollo-client/app/routes/posts.$postId.tsx new file mode 100644 index 0000000000..ad8e267703 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/posts.$postId.tsx @@ -0,0 +1,38 @@ +import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import type { ErrorComponentProps } from '@tanstack/react-router' +import { NotFound } from '~/components/NotFound' + +export const Route = createFileRoute('/posts/$postId')({ + loader: async ({ params: { postId } }) => fetchPost(postId), + errorComponent: PostErrorComponent as any, + component: PostComponent, + notFoundComponent: () => { + return Post not found + }, +}) + +export function PostErrorComponent({ error }: ErrorComponentProps) { + return +} + +function PostComponent() { + const post = Route.useLoaderData() + + return ( +
+

{post.title}

+
{post.body}
+ + Deep View + +
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/posts.index.tsx b/examples/react/start-basic-apollo-client/app/routes/posts.index.tsx new file mode 100644 index 0000000000..5b5f08f95b --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/posts.index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/posts/')({ + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} diff --git a/examples/react/start-basic-apollo-client/app/routes/posts.tsx b/examples/react/start-basic-apollo-client/app/routes/posts.tsx new file mode 100644 index 0000000000..6866dcf49d --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/posts.tsx @@ -0,0 +1,61 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { gql, useReadQuery } from '@apollo/client/index.js' +import type { TypedDocumentNode } from '@apollo/client/index.js' + +const GET_POSTS: TypedDocumentNode<{ + posts: { + data: Array<{ + id: string + title: string + }> + } +}> = gql` + query GetPosts { + posts(options: { paginate: { page: 1, limit: 5 } }) { + data { + id + title + } + } + } +` + +export const Route = createFileRoute('/posts')({ + loader: ({ context }) => { + const postsRef = context.preloadQuery(GET_POSTS) + return { postsRef } + }, + component: PostsComponent, +}) + +function PostsComponent() { + const data = Route.useLoaderData() + const posts = useReadQuery(data.postsRef).data.posts.data + + return ( +
+
    + {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( + (post) => { + return ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ) + }, + )} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/posts_.$postId.deep.tsx b/examples/react/start-basic-apollo-client/app/routes/posts_.$postId.deep.tsx new file mode 100644 index 0000000000..ecb046ade4 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/posts_.$postId.deep.tsx @@ -0,0 +1,26 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { PostErrorComponent } from './posts.$postId' + +export const Route = createFileRoute('/posts_/$postId/deep')({ + loader: async ({ params: { postId } }) => fetchPost(postId), + errorComponent: PostErrorComponent as any, + component: PostDeepComponent, +}) + +function PostDeepComponent() { + const post = Route.useLoaderData() + + return ( +
+ + ← All Posts + +

{post.title}

+
{post.body}
+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/redirect.tsx b/examples/react/start-basic-apollo-client/app/routes/redirect.tsx new file mode 100644 index 0000000000..c9286de13d --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/redirect.tsx @@ -0,0 +1,9 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect')({ + beforeLoad: async () => { + throw redirect({ + to: '/posts', + }) + }, +}) diff --git a/examples/react/start-basic-apollo-client/app/routes/users.$userId.tsx b/examples/react/start-basic-apollo-client/app/routes/users.$userId.tsx new file mode 100644 index 0000000000..03b59efb35 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/users.$userId.tsx @@ -0,0 +1,51 @@ +import { ErrorComponent, createFileRoute } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' +import { NotFound } from '~/components/NotFound' +import { + gql, + TypedDocumentNode, + useSuspenseQuery, +} from '@apollo/client/index.js' + +export const Route = createFileRoute('/users/$userId')({ + errorComponent: UserErrorComponent, + component: UserComponent, + notFoundComponent: () => { + return User not found + }, +}) + +const USER_QUERY: TypedDocumentNode< + { + user: { + id: string + name: string + email: string + } + }, + { id: string } +> = gql` + query GetUser($id: ID!) { + user(id: $id) { + id + name + email + } + } +` + +export function UserErrorComponent({ error }: ErrorComponentProps) { + return +} + +function UserComponent() { + const id = Route.useParams().userId + const user = useSuspenseQuery(USER_QUERY, { variables: { id } }).data.user + + return ( +
+

{user.name}

+
{user.email}
+
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/routes/users.index.tsx b/examples/react/start-basic-apollo-client/app/routes/users.index.tsx new file mode 100644 index 0000000000..b6b0ee67fb --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/users.index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/users/')({ + component: UsersIndexComponent, +}) + +function UsersIndexComponent() { + return
Select a user.
+} diff --git a/examples/react/start-basic-apollo-client/app/routes/users.tsx b/examples/react/start-basic-apollo-client/app/routes/users.tsx new file mode 100644 index 0000000000..8d107aa7d4 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/routes/users.tsx @@ -0,0 +1,55 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { + gql, + TypedDocumentNode, + useSuspenseQuery, +} from '@apollo/client/index.js' + +export const Route = createFileRoute('/users')({ + component: UsersComponent, +}) + +const USERS_QUERY: TypedDocumentNode<{ + users: { data: Array<{ id: string; name: string }> } +}> = gql` + query GetUsers { + users { + data { + id + name + } + } + } +` + +function UsersComponent() { + const users = useSuspenseQuery(USERS_QUERY).data.users.data + + return ( +
+
    + {[ + ...users, + { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, + ].map((user) => { + return ( +
  • + +
    {user.name}
    + +
  • + ) + })} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-apollo-client/app/ssr.tsx b/examples/react/start-basic-apollo-client/app/ssr.tsx new file mode 100644 index 0000000000..f2d33f9030 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/ssr.tsx @@ -0,0 +1,13 @@ +/// +import { + createStartHandler, + defaultStreamHandler, +} from '@tanstack/start/server' +import { getRouterManifest } from '@tanstack/start/router-manifest' + +import { createRouter } from './router' + +export default createStartHandler({ + createRouter, + getRouterManifest, +})(defaultStreamHandler) diff --git a/examples/react/start-basic-apollo-client/app/styles/app.css b/examples/react/start-basic-apollo-client/app/styles/app.css new file mode 100644 index 0000000000..d6426ccb72 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/styles/app.css @@ -0,0 +1,14 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + html, + body { + @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; + } + + .using-mouse * { + outline: none !important; + } +} diff --git a/examples/react/start-basic-apollo-client/app/utils/posts.tsx b/examples/react/start-basic-apollo-client/app/utils/posts.tsx new file mode 100644 index 0000000000..4bf4b8c564 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/utils/posts.tsx @@ -0,0 +1,32 @@ +import { notFound } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/start' +import axios from 'redaxios' + +export type PostType = { + id: string + title: string + body: string +} + +export const fetchPost = createServerFn('GET', async (postId: string) => { + console.info(`Fetching post with id ${postId}...`) + const post = await axios + .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) + .then((r) => r.data) + .catch((err) => { + console.error(err) + if (err.status === 404) { + throw notFound() + } + throw err + }) + + return post +}) + +export const fetchPosts = createServerFn('GET', async () => { + console.info('Fetching posts...') + return axios + .get>('https://jsonplaceholder.typicode.com/posts') + .then((r) => r.data.slice(0, 10)) +}) diff --git a/examples/react/start-basic-apollo-client/app/utils/seo.ts b/examples/react/start-basic-apollo-client/app/utils/seo.ts new file mode 100644 index 0000000000..d18ad84b74 --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string + description?: string + image?: string + keywords?: string +}) => { + const tags = [ + { title }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, + ] + : []), + ] + + return tags +} diff --git a/examples/react/start-basic-apollo-client/app/utils/users.tsx b/examples/react/start-basic-apollo-client/app/utils/users.tsx new file mode 100644 index 0000000000..b810f455fe --- /dev/null +++ b/examples/react/start-basic-apollo-client/app/utils/users.tsx @@ -0,0 +1,7 @@ +export type User = { + id: number + name: string + email: string +} + +export const DEPLOY_URL = 'http://localhost:3000' diff --git a/examples/react/start-basic-apollo-client/package.json b/examples/react/start-basic-apollo-client/package.json new file mode 100644 index 0000000000..6516ddce4e --- /dev/null +++ b/examples/react/start-basic-apollo-client/package.json @@ -0,0 +1,35 @@ +{ + "name": "tanstack-start-example-basic-apollo-client", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "vinxi dev", + "build": "vinxi build", + "start": "vinxi start" + }, + "dependencies": { + "@apollo/client": "^3.11.8", + "@apollo/client-react-streaming": "https://pkg.pr.new/apollographql/apollo-client-nextjs/@apollo/client-react-streaming@93e20db.tgz", + "@tanstack/react-router": "^1.77.8", + "@tanstack/router-devtools": "^1.77.8", + "@tanstack/start": "^1.77.8", + "graphql": "^16.9.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "redaxios": "^0.5.1", + "tailwind-merge": "^2.5.4", + "vinxi": "0.4.3" + }, + "devDependencies": { + "@types/node": "^22.5.4", + "@types/react": "^18.2.65", + "@types/react-dom": "^18.2.21", + "@vitejs/plugin-react": "^4.3.3", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.14", + "typescript": "^5.6.2", + "vite-tsconfig-paths": "^5.0.1" + } +} diff --git a/examples/react/start-basic-apollo-client/postcss.config.cjs b/examples/react/start-basic-apollo-client/postcss.config.cjs new file mode 100644 index 0000000000..8e638a6bcd --- /dev/null +++ b/examples/react/start-basic-apollo-client/postcss.config.cjs @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require('tailwindcss/nesting'), + require('tailwindcss'), + require('autoprefixer'), + ], +} diff --git a/examples/react/start-basic-apollo-client/public/android-chrome-192x192.png b/examples/react/start-basic-apollo-client/public/android-chrome-192x192.png new file mode 100644 index 0000000000..09c8324f8c Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/android-chrome-192x192.png differ diff --git a/examples/react/start-basic-apollo-client/public/android-chrome-512x512.png b/examples/react/start-basic-apollo-client/public/android-chrome-512x512.png new file mode 100644 index 0000000000..11d626ea3d Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/android-chrome-512x512.png differ diff --git a/examples/react/start-basic-apollo-client/public/apple-touch-icon.png b/examples/react/start-basic-apollo-client/public/apple-touch-icon.png new file mode 100644 index 0000000000..5a9423cc02 Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/apple-touch-icon.png differ diff --git a/examples/react/start-basic-apollo-client/public/favicon-16x16.png b/examples/react/start-basic-apollo-client/public/favicon-16x16.png new file mode 100644 index 0000000000..e3389b0044 Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/favicon-16x16.png differ diff --git a/examples/react/start-basic-apollo-client/public/favicon-32x32.png b/examples/react/start-basic-apollo-client/public/favicon-32x32.png new file mode 100644 index 0000000000..900c77d444 Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/favicon-32x32.png differ diff --git a/examples/react/start-basic-apollo-client/public/favicon.ico b/examples/react/start-basic-apollo-client/public/favicon.ico new file mode 100644 index 0000000000..1a1751676f Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/favicon.ico differ diff --git a/examples/react/start-basic-apollo-client/public/favicon.png b/examples/react/start-basic-apollo-client/public/favicon.png new file mode 100644 index 0000000000..1e77bc0609 Binary files /dev/null and b/examples/react/start-basic-apollo-client/public/favicon.png differ diff --git a/examples/react/start-basic-apollo-client/public/site.webmanifest b/examples/react/start-basic-apollo-client/public/site.webmanifest new file mode 100644 index 0000000000..fa99de77db --- /dev/null +++ b/examples/react/start-basic-apollo-client/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/examples/react/start-basic-apollo-client/tailwind.config.cjs b/examples/react/start-basic-apollo-client/tailwind.config.cjs new file mode 100644 index 0000000000..75fe25dbf7 --- /dev/null +++ b/examples/react/start-basic-apollo-client/tailwind.config.cjs @@ -0,0 +1,4 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./app/**/*.{js,ts,jsx,tsx}'], +} diff --git a/examples/react/start-basic-apollo-client/tsconfig.json b/examples/react/start-basic-apollo-client/tsconfig.json new file mode 100644 index 0000000000..d1b5b77660 --- /dev/null +++ b/examples/react/start-basic-apollo-client/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + "noEmit": true + } +} diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index f51c88f322..962fe0f924 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -668,6 +668,10 @@ export type RouterEvents = { pathChanged: boolean hrefChanged: boolean } + onStreamedValue: { + type: 'onStreamedValue' + key: string + } } export type RouterEvent = RouterEvents[keyof RouterEvents] @@ -805,7 +809,12 @@ export class Router< if (typeof document !== 'undefined') { ;(window as any).__TSR__ROUTER__ = this + this.streamedKeys = new Set( + Object.keys(window!.__TSR__?.streamedValues || {}), + ) } + + this.subscribe('onStreamedValue', ({ key }) => this.streamedKeys.add(key)) } // These are default implementations that can optionally be overridden @@ -3103,7 +3112,7 @@ ${script}\`)` 'Key has already been streamed: ' + key, ) - this.streamedKeys.add(key) + this.emit({ type: 'onStreamedValue', key }) this.injectScript( `__TSR__.streamedValues['${key}'] = { value: ${this.serializer?.(this.options.transformer.stringify(value))}}`, ) diff --git a/packages/start/src/client/Meta.tsx b/packages/start/src/client/Meta.tsx index 86464b591b..4a766083fd 100644 --- a/packages/start/src/client/Meta.tsx +++ b/packages/start/src/client/Meta.tsx @@ -126,7 +126,17 @@ export const useMetaElements = () => { // children={` // __TSR__ = { // matches: [], - // streamedValues: {}, + // streamedValues: new Proxy( + // {}, + // { + // set(t, key, v) { + // const res = Reflect.set(t, key, v) + // window.__TSR__ROUTER__ && + // __TSR__ROUTER__.emit({ type: 'onStreamedValue', key }) + // return res + // }, + // }, + // ), // queue: [], // runQueue: () => { // let changed = false @@ -195,7 +205,7 @@ export const useMetaElements = () => { // This is the minified version of the script above, using https://try.terser.org/ // Is this archaic? Probably. But we're not going to edit this much, so it's fine. // In a future world, like bun, we could use a more modern approach, like a compile-time macro minifier. - children={`__TSR__={matches:[],streamedValues:{},queue:[],runQueue:()=>{let e=!1;__TSR__.queue=__TSR__.queue.filter((_=>!_()||(e=!0,!1))),e&&__TSR__.runQueue()},initMatch:e=>{__TSR__.queue.push((()=>(__TSR__.matches[e.index]||(__TSR__.matches[e.index]=e,Object.entries(e.extracted).forEach((([e,_])=>{if("stream"===_.type){let e;_.value=new ReadableStream({start(_){e=_}}),_.value.controller=e}else if("promise"===_.type){let e,t;_.value=new Promise(((_,u)=>{e=_,t=u})),_.resolve=e,_.reject=t}}))),!0))),__TSR__.runQueue()},resolvePromise:e=>{__TSR__.queue.push((()=>{const _=__TSR__.matches[e.matchIndex];if(_){const t=_.extracted[e.id];if(t)return t.resolve(e.value.data),!0}return!1})),__TSR__.runQueue()},cleanScripts:()=>{document.querySelectorAll(".tsr-once").forEach((e=>{e.remove()}))}};`} + children={`__TSR__={matches:[],streamedValues:new Proxy({},{set(e,_,t){const r=Reflect.set(e,_,t);return window.__TSR__ROUTER__&&__TSR__ROUTER__.emit({type:"onStreamedValue",key:_}),r}}),queue:[],runQueue:()=>{let e=!1;__TSR__.queue=__TSR__.queue.filter((_=>!_()||(e=!0,!1))),e&&__TSR__.runQueue()},initMatch:e=>{__TSR__.queue.push((()=>(__TSR__.matches[e.index]||(__TSR__.matches[e.index]=e,Object.entries(e.extracted).forEach((([e,_])=>{if("stream"===_.type){let e;_.value=new ReadableStream({start(_){e=_}}),_.value.controller=e}else if("promise"===_.type){let e,t;_.value=new Promise(((_,r)=>{e=_,t=r})),_.resolve=e,_.reject=t}}))),!0))),__TSR__.runQueue()},resolvePromise:e=>{__TSR__.queue.push((()=>{const _=__TSR__.matches[e.matchIndex];if(_){const t=_.extracted[e.id];if(t)return t.resolve(e.value.data),!0}return!1})),__TSR__.runQueue()},cleanScripts:()=>{document.querySelectorAll(".tsr-once").forEach((e=>{e.remove()}))}};`} /> =19.0.0-rc + + '@apollo/client@3.11.8': + resolution: {integrity: sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==} + peerDependencies: + graphql: ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + '@arethetypeswrong/cli@0.17.1': resolution: {integrity: sha512-WNKTcC7lqWmbRWWku3Xz0hl7zj9szoGzx7gcGaZPxszKcMPiRnKsiLbxMpf1FzA6myIjE1yalqxNCJ0UkCWTXQ==} engines: {node: '>=18'} @@ -4877,6 +4966,11 @@ packages: '@gerrit0/mini-shiki@1.24.1': resolution: {integrity: sha512-PNP/Gjv3VqU7z7DjRgO3F9Ok5frTKqtpV+LJW1RzMcr2zpRk0ulhEWnbcNGXzPC7BZyWMIHrkfQX2GZRfxrn6Q==} + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -6443,6 +6537,26 @@ packages: webpack-dev-server: optional: true + '@wry/caches@1.0.1': + resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} + engines: {node: '>=8'} + + '@wry/context@0.7.4': + resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} + engines: {node: '>=8'} + + '@wry/equality@0.5.7': + resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} + engines: {node: '>=8'} + + '@wry/trie@0.4.3': + resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==} + engines: {node: '>=8'} + + '@wry/trie@0.5.0': + resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} + engines: {node: '>=8'} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -8055,6 +8169,12 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql-tag@2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql@16.9.0: resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -8104,6 +8224,9 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -9094,6 +9217,9 @@ packages: peerDependencies: typescript: ^5.x + optimism@0.18.0: + resolution: {integrity: sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -9528,6 +9654,9 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -9596,6 +9725,9 @@ packages: react: '>=16' react-dom: '>=16' + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -9695,6 +9827,17 @@ packages: regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + rehackt@0.1.0: + resolution: {integrity: sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==} + peerDependencies: + '@types/react': '*' + react: '*' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} @@ -9736,6 +9879,10 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true + response-iterator@0.2.6: + resolution: {integrity: sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==} + engines: {node: '>=0.8'} + restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -10128,6 +10275,10 @@ packages: peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -10309,6 +10460,10 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + ts-pattern@5.5.0: resolution: {integrity: sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==} @@ -11017,6 +11172,12 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} + zen-observable-ts@1.2.5: + resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} + + zen-observable@0.8.15: + resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -11037,6 +11198,35 @@ snapshots: '@andrewbranch/untar.js@1.0.3': {} + '@apollo/client-react-streaming@https://pkg.pr.new/apollographql/apollo-client-nextjs/@apollo/client-react-streaming@93e20db.tgz(@apollo/client@3.11.8(@types/react@18.3.12)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@apollo/client': 3.11.8(@types/react@18.3.12)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + ts-invariant: 0.10.3 + + '@apollo/client@3.11.8(@types/react@18.3.12)(graphql@16.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@wry/caches': 1.0.1 + '@wry/equality': 0.5.7 + '@wry/trie': 0.5.0 + graphql: 16.9.0 + graphql-tag: 2.12.6(graphql@16.9.0) + hoist-non-react-statics: 3.3.2 + optimism: 0.18.0 + prop-types: 15.8.1 + rehackt: 0.1.0(@types/react@18.3.12)(react@18.3.1) + response-iterator: 0.2.6 + symbol-observable: 4.0.0 + ts-invariant: 0.10.3 + tslib: 2.8.1 + zen-observable-ts: 1.2.5 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + '@arethetypeswrong/cli@0.17.1': dependencies: '@arethetypeswrong/core': 0.17.1 @@ -11876,6 +12066,10 @@ snapshots: '@shikijs/types': 1.24.0 '@shikijs/vscode-textmate': 9.3.0 + '@graphql-typed-document-node/core@3.2.0(graphql@16.9.0)': + dependencies: + graphql: 16.9.0 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -13683,6 +13877,26 @@ snapshots: optionalDependencies: webpack-dev-server: 5.1.0(webpack-cli@5.1.4)(webpack@5.97.1) + '@wry/caches@1.0.1': + dependencies: + tslib: 2.8.1 + + '@wry/context@0.7.4': + dependencies: + tslib: 2.8.1 + + '@wry/equality@0.5.7': + dependencies: + tslib: 2.8.1 + + '@wry/trie@0.4.3': + dependencies: + tslib: 2.8.1 + + '@wry/trie@0.5.0': + dependencies: + tslib: 2.8.1 + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -15540,6 +15754,11 @@ snapshots: graphemer@1.4.0: {} + graphql-tag@2.12.6(graphql@16.9.0): + dependencies: + graphql: 16.9.0 + tslib: 2.8.1 + graphql@16.9.0: {} gzip-size@7.0.0: @@ -15600,6 +15819,10 @@ snapshots: highlight.js@10.7.3: {} + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 @@ -16375,6 +16598,98 @@ snapshots: neo-async@2.6.2: {} + nitropack@2.10.4(typescript@5.6.3): + dependencies: + '@cloudflare/kv-asset-handler': 0.3.4 + '@netlify/functions': 2.8.2 + '@rollup/plugin-alias': 5.1.1(rollup@4.28.0) + '@rollup/plugin-commonjs': 28.0.1(rollup@4.28.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.28.0) + '@rollup/plugin-json': 6.1.0(rollup@4.28.0) + '@rollup/plugin-node-resolve': 15.3.0(rollup@4.28.0) + '@rollup/plugin-replace': 6.0.1(rollup@4.28.0) + '@rollup/plugin-terser': 0.4.4(rollup@4.28.0) + '@rollup/pluginutils': 5.1.3(rollup@4.28.0) + '@types/http-proxy': 1.17.15 + '@vercel/nft': 0.27.7(rollup@4.28.0) + archiver: 7.0.1 + c12: 2.0.1(magicast@0.3.5) + chokidar: 3.6.0 + citty: 0.1.6 + compatx: 0.1.8 + confbox: 0.1.8 + consola: 3.2.3 + cookie-es: 1.2.2 + croner: 9.0.0 + crossws: 0.3.1 + db0: 0.2.1 + defu: 6.1.4 + destr: 2.0.3 + dot-prop: 9.0.0 + esbuild: 0.24.0 + escape-string-regexp: 5.0.0 + etag: 1.8.1 + fs-extra: 11.2.0 + globby: 14.0.2 + gzip-size: 7.0.0 + h3: 1.13.0 + hookable: 5.5.3 + httpxy: 0.1.5 + ioredis: 5.4.1 + jiti: 2.4.1 + klona: 2.0.6 + knitwork: 1.1.0 + listhen: 1.9.0 + magic-string: 0.30.14 + magicast: 0.3.5 + mime: 4.0.4 + mlly: 1.7.3 + node-fetch-native: 1.6.4 + ofetch: 1.4.1 + ohash: 1.1.4 + openapi-typescript: 7.4.4(typescript@5.6.3) + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.2.1 + pretty-bytes: 6.1.1 + radix3: 1.1.2 + rollup: 4.28.0 + rollup-plugin-visualizer: 5.12.0(rollup@4.28.0) + scule: 1.3.0 + semver: 7.6.3 + serve-placeholder: 2.0.2 + serve-static: 1.16.2 + std-env: 3.8.0 + ufo: 1.5.4 + uncrypto: 0.1.3 + unctx: 2.3.1 + unenv: 1.10.0 + unimport: 3.14.3(rollup@4.28.0) + unstorage: 1.13.1(ioredis@5.4.1) + untyped: 1.5.1 + unwasm: 0.3.9 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/kv' + - better-sqlite3 + - drizzle-orm + - encoding + - idb-keyval + - mysql2 + - supports-color + - typescript + nitropack@2.10.4(typescript@5.7.2): dependencies: '@cloudflare/kv-asset-handler': 0.3.4 @@ -16666,6 +16981,18 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openapi-typescript@7.4.4(typescript@5.6.3): + dependencies: + '@redocly/openapi-core': 1.25.15(supports-color@9.4.0) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.1.0 + supports-color: 9.4.0 + typescript: 5.6.3 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - encoding + openapi-typescript@7.4.4(typescript@5.7.2): dependencies: '@redocly/openapi-core': 1.25.15(supports-color@9.4.0) @@ -16678,6 +17005,13 @@ snapshots: transitivePeerDependencies: - encoding + optimism@0.18.0: + dependencies: + '@wry/caches': 1.0.1 + '@wry/context': 0.7.4 + '@wry/trie': 0.4.3 + tslib: 2.8.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -17067,6 +17401,12 @@ snapshots: process@0.11.10: {} + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -17134,6 +17474,8 @@ snapshots: transitivePeerDependencies: - csstype + react-is@16.13.1: {} + react-is@17.0.2: {} react-is@18.3.1: {} @@ -17239,6 +17581,11 @@ snapshots: regenerator-runtime@0.14.1: {} + rehackt@0.1.0(@types/react@18.3.12)(react@18.3.1): + optionalDependencies: + '@types/react': 18.3.12 + react: 18.3.1 + relateurl@0.2.7: {} renderkid@3.0.0: @@ -17276,6 +17623,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + response-iterator@0.2.6: {} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 @@ -17714,6 +18063,8 @@ snapshots: react: 18.3.1 use-sync-external-store: 1.2.2(react@18.3.1) + symbol-observable@4.0.0: {} + symbol-tree@3.2.4: {} system-architecture@0.1.0: {} @@ -17910,8 +18261,16 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-invariant@0.10.3: + dependencies: + tslib: 2.8.1 + ts-pattern@5.5.0: {} + tsconfck@3.1.4(typescript@5.6.3): + optionalDependencies: + typescript: 5.6.3 + tsconfck@3.1.4(typescript@5.7.2): optionalDependencies: typescript: 5.7.2 @@ -18195,6 +18554,76 @@ snapshots: vary@1.1.2: {} + vinxi@0.4.3(@types/node@22.10.1)(ioredis@5.4.1)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@types/micromatch': 4.0.9 + '@vinxi/listhen': 1.5.6 + boxen: 7.1.1 + chokidar: 3.6.0 + citty: 0.1.6 + consola: 3.2.3 + crossws: 0.2.4 + dax-sh: 0.39.2 + defu: 6.1.4 + es-module-lexer: 1.5.4 + esbuild: 0.20.2 + fast-glob: 3.3.2 + get-port-please: 3.1.2 + h3: 1.11.1 + hookable: 5.5.3 + http-proxy: 1.18.1 + micromatch: 4.0.8 + nitropack: 2.10.4(typescript@5.6.3) + node-fetch-native: 1.6.4 + path-to-regexp: 6.3.0 + pathe: 1.1.2 + radix3: 1.1.2 + resolve: 1.22.8 + serve-placeholder: 2.0.2 + serve-static: 1.16.2 + ufo: 1.5.4 + unctx: 2.3.1 + unenv: 1.10.0 + unstorage: 1.13.1(ioredis@5.4.1) + vite: 5.4.11(@types/node@22.10.1)(terser@5.36.0) + zod: 3.23.8 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/kv' + - better-sqlite3 + - debug + - drizzle-orm + - encoding + - idb-keyval + - ioredis + - less + - lightningcss + - mysql2 + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - typescript + - uWebSockets.js + - xml2js + vinxi@0.4.3(@types/node@22.10.1)(ioredis@5.4.1)(terser@5.36.0)(typescript@5.7.2): dependencies: '@babel/core': 7.26.0 @@ -18398,6 +18827,17 @@ snapshots: dependencies: vite: 6.0.3(@types/node@22.10.1)(jiti@2.4.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.6.1) + vite-tsconfig-paths@5.1.4(typescript@5.6.3)(vite@6.0.3(@types/node@22.10.1)(jiti@2.4.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.6.1)): + dependencies: + debug: 4.3.7(supports-color@9.4.0) + globrex: 0.1.2 + tsconfck: 3.1.4(typescript@5.6.3) + optionalDependencies: + vite: 6.0.3(@types/node@22.10.1)(jiti@2.4.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.6.1) + transitivePeerDependencies: + - supports-color + - typescript + vite-tsconfig-paths@5.1.4(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.1)(jiti@2.4.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.6.1)): dependencies: debug: 4.3.7(supports-color@9.4.0) @@ -18770,6 +19210,12 @@ snapshots: yoctocolors@2.1.1: {} + zen-observable-ts@1.2.5: + dependencies: + zen-observable: 0.8.15 + + zen-observable@0.8.15: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2