From 2e820eecf354f56f1ae44f2764af87f02d96f222 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Fri, 21 Jul 2023 23:14:09 -0600 Subject: [PATCH] fix: (de)hydration changes --- docs/config.json | 4 + docs/guide/ssr-and-streaming.md | 410 ++++++++++++++++++ .../basic-ssr-streaming/src/routes/root.tsx | 4 +- examples/react/basic-ssr/src/entry-client.tsx | 1 - examples/react/basic-ssr/src/routes/root.tsx | 3 +- examples/react/basic/src/main.tsx | 13 +- packages/react-start/src/client.tsx | 2 +- packages/react-start/src/server.tsx | 12 +- packages/router/package.json | 3 +- packages/router/src/react.tsx | 13 +- packages/router/src/route.ts | 12 +- packages/router/src/routeMatch.ts | 86 ++-- packages/router/src/router.ts | 45 +- pnpm-lock.yaml | 118 +++-- 14 files changed, 590 insertions(+), 136 deletions(-) create mode 100644 docs/guide/ssr-and-streaming.md diff --git a/docs/config.json b/docs/config.json index 6624f3f65f..e3952d55ee 100644 --- a/docs/config.json +++ b/docs/config.json @@ -92,6 +92,10 @@ { "label": "Router Context", "to": "guide/router-context" + }, + { + "label": "SSR & Streaming", + "to": "guide/ssr-and-streaming" } ] }, diff --git a/docs/guide/ssr-and-streaming.md b/docs/guide/ssr-and-streaming.md new file mode 100644 index 0000000000..90db70cd61 --- /dev/null +++ b/docs/guide/ssr-and-streaming.md @@ -0,0 +1,410 @@ +--- +id: ssr-and-streaming +title: SSR & Streaming +--- + +Server Side Rendering (SSR) and Streaming are two different concepts that are often confused. This guide will explain the difference between the two and how to use one or both of them. + +## Server Side Rendering + +Server Side Rendering (SSR) is the process of rendering a component on the server and sending the HTML markup to the client. The client then hydrates the markup into a fully interactive component. + +In traditional SSR configurations, the entire page is rendered on the server and sent to the client in one single HTML request, including the serialized data the application needs to hydrate on the client. + +To make this process simple, use the following utilities: + +- The `@tanstack/react-start/server` package + - `StartServer` + - e.g. `` + - Rendering this component in your server entry will render your application and also automatically handle application-level hydration/dehydration and implement the `Wrap` component option on `Router` +- The `@tanstack/react-start/client` package + + - `StartClient` + - e.g. `` + - Rendering this component in your client entry will render your application and also automatically implement the `Wrap` component option on `Router` + - `DehydrateRouter` + - e.g. `` + - Render this component **inside your application** to embed the router's dehydrated data into the application. + +### Router Creation + +Since your router will exist both on the server and the client, it's important that you create your router in a way that is consistent between both of these environments. The easiest way to do this is to expose a `createRouter` function in a shared file that can be imported and called by both your server and client entry files. + +```js +import * as React from 'react' +import { Router } from '@tanstack/router' +import { rootRoute } from './routes/root' +import { indexRoute } from './routes/index' +import { postsRoute } from './routes/posts' +import { postsIndexRoute } from './routes/posts/index' +import { postIdRoute } from './routes/posts/$postId' + +export const routeTree = rootRoute.addChildren([ + indexRoute, + postsRoute.addChildren([postsIndexRoute, postIdRoute]), +]) + +export function createRouter() { + return new Router({ routeTree,}) +} + +declare module '@tanstack/router' { + interface Register { + router: ReturnType + } +} +``` + +Now you can import this function in both your server and client entry files and create your router. + +```js +// src/entry-server.tsx + +import { createRouter } from './router' + +export async function render(req, res) { + const router = createRouter() +} +``` + +```js +// src/entry-client.tsx + +import { createRouter } from './router' + +const router = createRouter() +``` + +### Server History + +On the client, Router defaults to using an instance of `createBrowserHistory`, which is the preferred type of history to use on the client. On the server, however, you will want to use an instance of `createMemoryHistory` instead. This is because `createBrowserHistory` uses the `window` object, which does not exist on the server. + +> 🧠 Make sure you initialize your memory history with the server URL that is being rendered. + +```tsx +// src/entry-server.tsx + +const router = createRouter() + +const memoryHistory = createMemoryHistory({ + initialEntries: [opts.url], +}) +``` + +After creating the memory history instance, you can update the router to use it. + +```tsx +// src/entry-server.tsx + +router.update({ + history: memoryHistory, +}) +``` + +### Loading Critical Router Data + +In order to render your application on the server, you will need to ensure that the router has loaded any critical data via it's route loaders. To do this, you can `await router.load()` before rendering your application. This will quite literally wait for each of the matching route matches found for this url to run their route's `loader` functions in parallel. + +```tsx +// src/entry-server.tsx + +await router.load() +``` + +### Rendering the Application on the Server + +Now that you have a router instance that has loaded all of the critical data for the current URL, you can render your application. + +```tsx +// src/entry-server.tsx + +const app = ( + + + +) + +const html = ReactDOMServer.renderToString(app) +``` + +### All Together Now! + +Here is a complete example of a server entry file that uses all of the concepts discussed above. + +```tsx +import * as React from 'react' +import ReactDOMServer from 'react-dom/server' +import { createMemoryHistory } from '@tanstack/router' +import { StartServer } from '@tanstack/react-start/server' +import { createRouter } from './router' + +export async function render(req, res) { + const router = createRouter() + + const memoryHistory = createMemoryHistory({ + initialEntries: [req.originalUrl], + }) + + router.update({ + history: memoryHistory, + }) + + await router.load() + + const appHtml = ReactDOMServer.renderToString() + + res.statusCode = 200 + res.setHeader('Content-Type', 'text/html') + res.end(`${appHtml}`) +} +``` + +### Application Dehydration/Hydration + +TanStack Router is **not a data store**, and because of this it is unaware of any data that your application may need to dehydrate and hydrate between the server and client. It does however provide convenient mechanisms to shuttle your application data between the server and client. + +The **`dehydrate` and `hydrate`** options on `Router` are functions that are automatically called on the server and client when the router is ready to dehydrate and hydrate any application data it may need on the client. + +The `dehydrate` function can return any serializable JSON data which will get injected into the `DehydrateRouter` component, wherever it's rendered and that same data will be provided back to you in the `hydrate` function on the client. + +For example, let's dehydrate and hydrate a `LoaderClient` instance from `@tanstack/react-loaders` so that our loader data we fetched on the server in our router loaders will be available for hydration on the client. + +```tsx +// src/router.tsx + +export function createRouter() { + // Make sure you create your loader client or similar data + // stores inside of your `createRouter` function. This ensures + // that your data stores are unique to each request and + // always present on both server and client. + const loaderClient = createLoaderClient() + + return new Router({ + routeTree, + // Optionally provide your loaderClient to the router context for + // convenience (you can provide anything you want to the router + // context!) + context: { + loaderClient, + }, + // On the server, dehydrate the loader client and return it + // to the router to get injected into `` + dehydrate: () => { + return { + loaderClient: loaderClient.dehydrate(), + } + }, + // On the client, hydrate the loader client with the data + // we dehydrated on the server + hydrate: (dehydrated) => { + loaderClient.hydrate(dehydrated.loaderClient) + }, + // Optionally, we can use `Wrap` to wrap our router in the loader client provider + Wrap: ({ children }) => { + return ( + + {children} + + ) + }, + }) +} +``` + +### `DehydrateRouter` + +To dehydrate your application data, make sure you are rendering the `` component inside your application. This component will render a `')` + - This function can be called multiple times during rendering to inject arbitrary HTML markup into the stream. + - Use it to inject things like `') + return null +} +``` + +### Dehydrating and Hydrating Data + +Injecting HTML is pretty low-level, so let's use the `router.dehydrateData` and `router.hydrateData` functions to inject and retrieve some JSON data instead. + +```tsx +function Custom() { + return ( + Loading...}> + + + ) +} + +let testCache: string + +function Test() { + const router = useRouter() + + // Attempt to rehydrate our data on the client + // On the server, this is a no=op and + // will return undefined + const data = router.hydrateData('testCache') + + // Suspend and load our fake data + if (!testCache) { + throw new Promise((resolve) => { + setTimeout(() => { + testCache = Date.now() + resolve() + }, 1000) + }) + } + + // Dehydrate our data on the server so it can + // be rehydrated on the client + router.dehydrateData('testCache', testCache) + + return data +} +``` + +### Providing Dehydration/Hydration utilities to other tools + +The `router.dehydrateData` and `router.hydrateData` functions are designed to be used by other tools to dehydrate and hydrate data. For example, the `@tanstack/react-loaders` package can use generic `dehydrate`/`hydrate` options to dehydrate and hydrate each loader as it is fetch on the server and rendered on the client: + +```tsx +// src/router.tsx + +export function createRouter() { + const loaderClient = createLoaderClient() + + const router = new Router({ + ... + }) + + // Provide hydration and dehydration functions to loader instances + loaderClient.options = { + ...loaderClient.options, + hydrateLoaderInstanceFn: (instance) => + router.hydrateData(instance.hashedKey) as any, + dehydrateLoaderInstanceFn: (instance) => + router.dehydrateData(instance.hashedKey, () => instance.state), + } + + return router +} +``` + +This allows the loader client to automatically dehydrate and hydrate each loader instance as it is fetched on the server and rendered on the client, leaving your application code free of any boilerplate or knowledge of the hydration/dehydration process: + +```tsx +// src/components/MyComponent.tsx + +import * as React from 'react' +import { Loader } from '@tanstack/react-loaders' + +const testLoader = new Loader({ + fn: async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)) + return 'Hello World!' + }, +}) + +export function Test() { + return ( + Loading...}> + + + ) +} + +export function Inner() { + const instance = testLoader.useLoader() + + return instance.state.data +} +``` diff --git a/examples/react/basic-ssr-streaming/src/routes/root.tsx b/examples/react/basic-ssr-streaming/src/routes/root.tsx index c9b61b04ed..0c808edb08 100644 --- a/examples/react/basic-ssr-streaming/src/routes/root.tsx +++ b/examples/react/basic-ssr-streaming/src/routes/root.tsx @@ -2,7 +2,7 @@ import { TanStackRouterDevtools } from '@tanstack/router-devtools' import * as React from 'react' import { Link, Outlet, RootRoute } from '@tanstack/router' import { RouterContext } from '../router' -import { RouterScripts } from '@tanstack/react-start/client' +import { DehydrateRouter } from '@tanstack/react-start/client' export const rootRoute = RootRoute.withRouterContext()({ component: Root, @@ -55,7 +55,7 @@ function Root() {
{/* Start rendering router matches */} - + ) diff --git a/examples/react/basic-ssr/src/entry-client.tsx b/examples/react/basic-ssr/src/entry-client.tsx index c45d518ab3..045e49f7b8 100644 --- a/examples/react/basic-ssr/src/entry-client.tsx +++ b/examples/react/basic-ssr/src/entry-client.tsx @@ -5,6 +5,5 @@ import { StartClient } from '@tanstack/react-start/client' import { createRouter } from './router' const router = createRouter() -router.hydrate() ReactDOM.hydrateRoot(document, ) diff --git a/examples/react/basic-ssr/src/routes/root.tsx b/examples/react/basic-ssr/src/routes/root.tsx index 189c079586..226655b4d3 100644 --- a/examples/react/basic-ssr/src/routes/root.tsx +++ b/examples/react/basic-ssr/src/routes/root.tsx @@ -2,7 +2,7 @@ import { TanStackRouterDevtools } from '@tanstack/router-devtools' import * as React from 'react' import { Link, Outlet, RootRoute } from '@tanstack/router' import { RouterContext } from '../router' -import { RouterScripts } from '@tanstack/react-start/client' +import { DehydrateRouter } from '@tanstack/react-start/client' export const rootRoute = RootRoute.withRouterContext()({ component: Root, @@ -55,7 +55,6 @@ function Root() {
{/* Start rendering router matches */} - ) diff --git a/examples/react/basic/src/main.tsx b/examples/react/basic/src/main.tsx index 29f411e3b4..bec121b059 100644 --- a/examples/react/basic/src/main.tsx +++ b/examples/react/basic/src/main.tsx @@ -126,13 +126,16 @@ const postsRoute = new Route({ await postsLoader.load() - return () => - useLoader({ - loader: postsLoader, - }) + return { + promise: new Promise((r) => setTimeout(r, 500)), + useLoader: () => + useLoader({ + loader: postsLoader, + }), + } }, component: ({ useLoader }) => { - const postsLoader = useLoader()() + const postsLoader = useLoader().useLoader() return (
diff --git a/packages/react-start/src/client.tsx b/packages/react-start/src/client.tsx index 0fd6a01373..014e9a30dc 100644 --- a/packages/react-start/src/client.tsx +++ b/packages/react-start/src/client.tsx @@ -13,7 +13,7 @@ export function StartClient(props: { router: AnyRouter }) { ) } -export function RouterScripts() { +export function DehydrateRouter() { const dehydrated = React.useContext( cprc.getContext('TanStackRouterHydrationContext', {}), ) diff --git a/packages/react-start/src/server.tsx b/packages/react-start/src/server.tsx index d3ae56f5c5..be22d86b12 100644 --- a/packages/react-start/src/server.tsx +++ b/packages/react-start/src/server.tsx @@ -106,14 +106,8 @@ export function StartServer(props: { [], ) - // React.useState(() => { - // if (hydrationCtxValue) { - // props.router.hydrate(hydrationCtxValue) - // } - // }) - return ( - // Provide the hydration context still, since `` needs it. + // Provide the hydration context still, since `` needs it. @@ -124,7 +118,9 @@ export function StartServer(props: { export function transformStreamWithRouter(router: AnyRouter) { return transformStreamHtmlCallback(async () => { - const injectorPromises = router.injectedHtml.map((d) => d()) + const injectorPromises = router.injectedHtml.map((d) => + typeof d === 'function' ? d() : d, + ) const injectors = await Promise.all(injectorPromises) router.injectedHtml = [] return injectors.join('') diff --git a/packages/router/package.json b/packages/router/package.json index 3d600212d7..ba37c80d7c 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -47,6 +47,7 @@ "@babel/runtime": "^7.16.7", "tiny-invariant": "^1.3.1", "tiny-warning": "^1.0.3", - "@tanstack/react-store": "workspace:*" + "@tanstack/react-store": "workspace:*", + "@gisatcz/cross-package-react-context": "^0.2.0" } } diff --git a/packages/router/src/react.tsx b/packages/router/src/react.tsx index 3ae9ee48de..39d0af19bc 100644 --- a/packages/router/src/react.tsx +++ b/packages/router/src/react.tsx @@ -606,9 +606,12 @@ function SubOutlet({ export function useInjectHtml() { const router = useRouterContext() - return React.useCallback((getHtml: () => Promise | string) => { - router.injectHtml(getHtml) - }, []) + return React.useCallback( + (html: string | (() => Promise | string)) => { + router.injectHtml(html) + }, + [], + ) } export function useDehydrate() { @@ -616,9 +619,9 @@ export function useDehydrate() { return React.useCallback(function dehydrate( key: any, - getData: () => Promise | T, + data: T | (() => Promise | T), ) { - return router.dehydrateData(key, getData) + return router.dehydrateData(key, data) }, []) } diff --git a/packages/router/src/route.ts b/packages/router/src/route.ts index 9caeb54cae..3702042903 100644 --- a/packages/router/src/route.ts +++ b/packages/router/src/route.ts @@ -471,9 +471,15 @@ export interface AnyRoute type MergeFromParent = IsAny -export type UseLoaderResult = { - [K in keyof T]: T[K] extends Promise ? StreamedPromise : T[K] -} +export type UseLoaderResult = T extends Record + ? { + [K in keyof T]: UseLoaderResultPromise + } + : UseLoaderResultPromise + +export type UseLoaderResultPromise = T extends Promise + ? StreamedPromise + : T export type StreamedPromise = { promise: Promise diff --git a/packages/router/src/routeMatch.ts b/packages/router/src/routeMatch.ts index 668ff11468..4c9aff04cc 100644 --- a/packages/router/src/routeMatch.ts +++ b/packages/router/src/routeMatch.ts @@ -64,8 +64,8 @@ export class RouteMatch< parentMatch?: RouteMatch pendingInfo?: PendingRouteMatchInfo - __promiseKeys: string[] = [] - __promisesByKey: Record> = {} + // __promiseKeys: string[] = [] + // __promisesByKey: Record> = {} __loadPromise?: Promise __loadPromiseResolve?: () => void __onExit?: @@ -303,47 +303,47 @@ export class RouteMatch< ]) if ((latestPromise = checkLatest())) return await latestPromise - Object.keys(loader ?? {}).forEach((key) => { - const value = loader[key] - if (value instanceof Promise || value?.then) { - // if (this.__promisesByKey[key]) { - // return - // } - - if (typeof document === 'undefined') { - this.__promisesByKey[key] = { - status: 'pending', - promise: value, - data: undefined, - resolve: () => {}, - } - - value.then((d: any) => { - this.__promisesByKey[key]!.status = 'resolved' - this.__promisesByKey[key]!.data = d - }) - } else { - const promise = createPromise() - this.__promisesByKey[key] = { - status: 'pending', - promise, - data: undefined, - resolve: (d: any) => { - // @ts-ignore - promise.resolve() - this.__promisesByKey[key]!.status = 'resolved' - this.__promisesByKey[key]!.data = d - }, - } - - if (!this.__promiseKeys.includes(key)) { - value.then(this.__promisesByKey[key]!.resolve) - } - } - - loader[key] = this.__promisesByKey[key] - } - }) + // Object.keys(loader ?? {}).forEach((key) => { + // const value = loader[key] + // if (value instanceof Promise || value?.then) { + // // if (this.__promisesByKey[key]) { + // // return + // // } + + // if (typeof document === 'undefined') { + // this.__promisesByKey[key] = { + // status: 'pending', + // promise: value, + // data: undefined, + // resolve: () => {}, + // } + + // value.then((d: any) => { + // this.__promisesByKey[key]!.status = 'resolved' + // this.__promisesByKey[key]!.data = d + // }) + // } else { + // const promise = createPromise() + // this.__promisesByKey[key] = { + // status: 'pending', + // promise, + // data: undefined, + // resolve: (d: any) => { + // // @ts-ignore + // promise.resolve() + // this.__promisesByKey[key]!.status = 'resolved' + // this.__promisesByKey[key]!.data = d + // }, + // } + + // if (!this.__promiseKeys.includes(key)) { + // value.then(this.__promisesByKey[key]!.resolve) + // } + // } + + // loader[key] = this.__promisesByKey[key] + // } + // }) this.__store.setState((s) => ({ ...s, diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index d498280610..9d3f149f4a 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -200,7 +200,7 @@ type LinkCurrentTargetElement = { export interface DehydratedRouterState extends Pick { - matches: DehydratedRouteMatch[] + // matches: DehydratedRouteMatch[] } export interface DehydratedRouter { @@ -310,6 +310,10 @@ export class Router< if (this.state.location.href !== next.href) { this.#commitLocation({ ...next, replace: true }) } + + if (typeof document !== 'undefined') { + this.hydrate() + } } reset = () => { @@ -1014,10 +1018,10 @@ export class Router< return { state: { ...pick(this.state, ['location', 'status', 'lastUpdated']), - matches: this.state.matches.map((m) => ({ - id: m.id, - promiseKeys: Object.keys(m.__promisesByKey), - })), + // matches: this.state.matches.map((m) => ({ + // id: m.id, + // promiseKeys: Object.keys(m.__promisesByKey), + // })), }, } } @@ -1031,7 +1035,7 @@ export class Router< invariant( _ctx, - 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render in your app?', + 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render in your app?', ) const ctx = _ctx @@ -1049,26 +1053,27 @@ export class Router< await this.load() - this.state.matches.forEach((m) => { - m.__promiseKeys = - ctx.router.state.matches.find((d) => d.id === m.id)?.promiseKeys ?? [] - }) + // this.state.matches.forEach((m) => { + // m.__promiseKeys = + // ctx.router.state.matches.find((d) => d.id === m.id)?.promiseKeys ?? [] + // }) return } - injectedHtml: (() => Promise | string)[] = [] + injectedHtml: (string | (() => Promise | string))[] = [] - injectHtml = async (getHtml: () => Promise | string) => { - this.injectedHtml.push(getHtml) + injectHtml = async (html: string | (() => Promise | string)) => { + this.injectedHtml.push(html) } - dehydrateData = (key: any, getData: () => Promise | T) => { + dehydrateData = (key: any, getData: T | (() => Promise | T)) => { if (typeof document === 'undefined') { const strKey = typeof key === 'string' ? key : JSON.stringify(key) this.injectHtml(async () => { - const data = await getData() + const data = + typeof getData === 'function' ? await (getData as any)() : getData return `` @@ -1090,11 +1095,11 @@ export class Router< return undefined } - resolveMatchPromise = (matchId: string, key: string, value: any) => { - this.state.matches - .find((d) => d.id === matchId) - ?.__promisesByKey[key]?.resolve(value) - } + // resolveMatchPromise = (matchId: string, key: string, value: any) => { + // this.state.matches + // .find((d) => d.id === matchId) + // ?.__promisesByKey[key]?.resolve(value) + // } #buildRouteTree = (routeTree: AnyRoute) => { const recurseRoutes = (routes: Route[], parentRoute: Route | undefined) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 107b1b9561..6e86b3c05d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: false - excludeLinksFromLockfile: false - overrides: '@tanstack/store': workspace:* '@tanstack/router': workspace:* @@ -68,7 +64,7 @@ importers: version: 6.3.1 '@nrwl/nx-cloud': specifier: latest - version: 16.1.1 + version: 16.0.5 '@rollup/plugin-babel': specifier: ^5.3.1 version: 5.3.1(@babel/core@7.20.5)(@types/babel__core@7.1.20)(rollup@2.79.1) @@ -782,6 +778,12 @@ importers: '@tanstack/react-store': specifier: workspace:* version: link:../react-store + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) tiny-invariant: specifier: ^1.0.5 version: 1.3.1 @@ -794,6 +796,12 @@ importers: '@tanstack/react-store': specifier: workspace:* version: link:../react-store + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) tiny-invariant: specifier: ^1.0.5 version: 1.3.1 @@ -809,6 +817,12 @@ importers: '@tanstack/router': specifier: workspace:* version: link:../router + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) devDependencies: babel-plugin-transform-async-to-promises: specifier: ^0.8.18 @@ -821,10 +835,10 @@ importers: version: 7.21.0 '@gisatcz/cross-package-react-context': specifier: ^0.2.0 - version: 0.2.0 + version: 0.2.0(react@18.2.0) '@tanstack/bling': specifier: ^0.0.3 - version: 0.0.3 + version: 0.0.3(vite@4.4.4) '@tanstack/react-loaders': specifier: workspace:* version: link:../react-loaders @@ -840,6 +854,12 @@ importers: jsesc: specifier: ^3.0.2 version: 3.0.2 + react: + specifier: '>=18' + version: 18.2.0 + react-dom: + specifier: '>=18' + version: 18.2.0(react@18.2.0) tiny-invariant: specifier: ^1.3.1 version: 1.3.1 @@ -849,7 +869,7 @@ importers: devDependencies: astro: specifier: ^1.9.2 - version: 1.9.2 + version: 1.9.2(@types/node@17.0.45)(ts-node@10.9.1) esbuild: specifier: ^0.17.10 version: 0.17.10 @@ -859,9 +879,15 @@ importers: '@tanstack/store': specifier: workspace:* version: link:../store + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) use-sync-external-store: specifier: ^1.2.0 - version: 1.2.0 + version: 1.2.0(react@18.2.0) devDependencies: '@types/use-sync-external-store': specifier: ^0.0.3 @@ -872,9 +898,18 @@ importers: '@babel/runtime': specifier: ^7.16.7 version: 7.20.6 + '@gisatcz/cross-package-react-context': + specifier: ^0.2.0 + version: 0.2.0(react@18.2.0) '@tanstack/react-store': specifier: workspace:* version: link:../react-store + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) tiny-invariant: specifier: ^1.3.1 version: 1.3.1 @@ -893,6 +928,12 @@ importers: date-fns: specifier: ^2.29.1 version: 2.29.3 + react: + specifier: '>=16' + version: 18.2.0 + react-dom: + specifier: '>=16' + version: 18.2.0(react@18.2.0) packages/store: {} @@ -3218,10 +3259,12 @@ packages: engines: {node: '>=14.0.0', npm: '>=6.0.0'} dev: true - /@gisatcz/cross-package-react-context@0.2.0: + /@gisatcz/cross-package-react-context@0.2.0(react@18.2.0): resolution: {integrity: sha512-E7aR/o1ArpXETO8bQi+Hltj3duHeZiLjR+x1VZrCDs3jplSLGoy+oqCcqNNE1BamTBa0YQTs8Go1Q1dtt3LEUg==} peerDependencies: react: ^16.13.1 || ^17.0.2 || ^18.1.0 + dependencies: + react: 18.2.0 dev: false /@jest/schemas@29.6.0: @@ -3326,10 +3369,10 @@ packages: - debug dev: true - /@nrwl/nx-cloud@16.1.1: - resolution: {integrity: sha512-iJIPP46+saFZK748FKU4u4YZH+Sv3ZvZPbMwGVMhwqhOYcrlO5aSa0lpilyoN8WuhooKNqcCfiqshx6V577fTg==} + /@nrwl/nx-cloud@16.0.5: + resolution: {integrity: sha512-1p82ym8WE9ziejwgPslstn19iV/VkHfHfKr/5YOnfCHQS+NxUf92ogcYhHXtqWLblVZ9Zs4W4pkSXK4e04wCmQ==} dependencies: - nx-cloud: 16.1.1 + nx-cloud: 16.0.5 transitivePeerDependencies: - debug dev: true @@ -3589,10 +3632,10 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: false - /@tanstack/bling@0.0.3: + /@tanstack/bling@0.0.3(vite@4.4.4): resolution: {integrity: sha512-6cmmp3cX9e9DZEkJ7F9vwEaU5RmjNYWcY69A1cx4FyGsuNryqumXCWuVItBsrIM0npqqCRa09BQ+y45LxiL6ng==} dependencies: - '@vitejs/plugin-react': 3.1.0 + '@vitejs/plugin-react': 3.1.0(vite@4.4.4) transitivePeerDependencies: - supports-color - vite @@ -4108,21 +4151,6 @@ packages: - supports-color dev: true - /@vitejs/plugin-react@3.1.0: - resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.1.0-beta.0 - dependencies: - '@babel/core': 7.21.0 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.0) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.0) - magic-string: 0.27.0 - react-refresh: 0.14.0 - transitivePeerDependencies: - - supports-color - dev: false - /@vitejs/plugin-react@3.1.0(vite@4.4.4): resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4449,7 +4477,7 @@ packages: tslib: 2.5.0 dev: true - /astro@1.9.2: + /astro@1.9.2(@types/node@17.0.45)(ts-node@10.9.1): resolution: {integrity: sha512-L+Ma0eR0Aa6QZg7RF0lEs+106Ye1/zukvtq3KtsYIogAojltlwllwU9X5CwMBzFwA55NxpNp4gSRh5US/xb+8Q==} engines: {node: ^14.18.0 || >=16.12.0, npm: '>=6.14.0'} hasBin: true @@ -4495,7 +4523,7 @@ packages: path-browserify: 1.0.1 path-to-regexp: 6.2.1 postcss: 8.4.21 - postcss-load-config: 3.1.4(postcss@8.4.21) + postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.9.1) preferred-pm: 3.0.3 prompts: 2.4.2 recast: 0.20.5 @@ -4513,7 +4541,7 @@ packages: typescript: 5.0.4 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 3.2.7 + vite: 3.2.7(@types/node@17.0.45) vitefu: 0.2.4(vite@3.2.7) yargs-parser: 21.1.1 zod: 3.20.6 @@ -8166,11 +8194,11 @@ packages: - debug dev: true - /nx-cloud@16.1.1: - resolution: {integrity: sha512-Rq7ynvkYzAJ67N3pDqU6cMqwvWP7WXJGP4EFjLxgUrRHNCccqDPggeAqePodfk3nZEUrZB8F5QBKZuuw1DR3oA==} + /nx-cloud@16.0.5: + resolution: {integrity: sha512-13P7r0aKikjBtmdZrNorwXzVPeVIV4MLEwqGY+DEG6doLBtI5KqEQk/d5B5l2dCF2BEi/LXEmLYCmf9gwbOJ+Q==} hasBin: true dependencies: - '@nrwl/nx-cloud': 16.1.1 + '@nrwl/nx-cloud': 16.0.5 axios: 1.1.3 chalk: 4.1.2 dotenv: 10.0.0 @@ -8486,7 +8514,7 @@ packages: pathe: 1.1.1 dev: false - /postcss-load-config@3.1.4(postcss@8.4.21): + /postcss-load-config@3.1.4(postcss@8.4.21)(ts-node@10.9.1): resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} peerDependencies: @@ -8500,6 +8528,7 @@ packages: dependencies: lilconfig: 2.0.6 postcss: 8.4.21 + ts-node: 10.9.1(@types/node@17.0.45)(typescript@4.9.5) yaml: 1.10.2 dev: true @@ -10025,12 +10054,6 @@ packages: escalade: 3.1.1 picocolors: 1.0.0 - /use-sync-external-store@1.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dev: false - /use-sync-external-store@1.2.0(react@18.2.0): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -10220,7 +10243,7 @@ packages: fsevents: 2.3.2 dev: true - /vite@3.2.7: + /vite@3.2.7(@types/node@17.0.45): resolution: {integrity: sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -10245,6 +10268,7 @@ packages: terser: optional: true dependencies: + '@types/node': 17.0.45 esbuild: 0.15.18 postcss: 8.4.21 resolve: 1.22.1 @@ -10333,7 +10357,7 @@ packages: vite: optional: true dependencies: - vite: 3.2.7 + vite: 3.2.7(@types/node@17.0.45) dev: true /vitefu@0.2.4(vite@4.4.1): @@ -10659,3 +10683,7 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false