@tanstack/router-ssr-query-core
Advanced tools
+108
-133
@@ -1,138 +0,113 @@ | ||
| "use strict"; | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const queryCore = require("@tanstack/query-core"); | ||
| const routerCore = require("@tanstack/router-core"); | ||
| const isServer = require("@tanstack/router-core/isServer"); | ||
| function setupCoreRouterSsrQueryIntegration({ | ||
| router, | ||
| queryClient, | ||
| handleRedirects = true | ||
| }) { | ||
| const ogHydrate = router.options.hydrate; | ||
| const ogDehydrate = router.options.dehydrate; | ||
| if (isServer.isServer ?? router.isServer) { | ||
| const sentQueries = /* @__PURE__ */ new Set(); | ||
| const queryStream = createPushableStream(); | ||
| let unsubscribe = void 0; | ||
| router.options.dehydrate = async () => { | ||
| router.serverSsr.onRenderFinished(() => { | ||
| queryStream.close(); | ||
| unsubscribe?.(); | ||
| unsubscribe = void 0; | ||
| }); | ||
| const ogDehydrated = await ogDehydrate?.(); | ||
| const dehydratedRouter = { | ||
| ...ogDehydrated, | ||
| // prepare the stream for queries coming up during rendering | ||
| queryStream: queryStream.stream | ||
| }; | ||
| const dehydratedQueryClient = queryCore.dehydrate(queryClient); | ||
| if (dehydratedQueryClient.queries.length > 0) { | ||
| dehydratedQueryClient.queries.forEach((query) => { | ||
| sentQueries.add(query.queryHash); | ||
| }); | ||
| dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient; | ||
| } | ||
| return dehydratedRouter; | ||
| }; | ||
| const ogClientOptions = queryClient.getDefaultOptions(); | ||
| queryClient.setDefaultOptions({ | ||
| ...ogClientOptions, | ||
| dehydrate: { | ||
| shouldDehydrateQuery: () => true, | ||
| ...ogClientOptions.dehydrate | ||
| } | ||
| }); | ||
| unsubscribe = queryClient.getQueryCache().subscribe((event) => { | ||
| if (!router.serverSsr?.isDehydrated()) { | ||
| return; | ||
| } | ||
| if (sentQueries.has(event.query.queryHash)) { | ||
| return; | ||
| } | ||
| if (!event.query.promise) { | ||
| return; | ||
| } | ||
| if (queryStream.isClosed()) { | ||
| console.warn( | ||
| `tried to stream query ${event.query.queryHash} after stream was already closed` | ||
| ); | ||
| return; | ||
| } | ||
| sentQueries.add(event.query.queryHash); | ||
| queryStream.enqueue( | ||
| queryCore.dehydrate(queryClient, { | ||
| shouldDehydrateQuery: (query) => { | ||
| if (query.queryHash === event.query.queryHash) { | ||
| return ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true; | ||
| } | ||
| return false; | ||
| } | ||
| }) | ||
| ); | ||
| }); | ||
| } else { | ||
| router.options.hydrate = async (dehydrated) => { | ||
| await ogHydrate?.(dehydrated); | ||
| if (dehydrated.dehydratedQueryClient) { | ||
| queryCore.hydrate(queryClient, dehydrated.dehydratedQueryClient); | ||
| } | ||
| const reader = dehydrated.queryStream.getReader(); | ||
| reader.read().then(async function handle({ done, value }) { | ||
| queryCore.hydrate(queryClient, value); | ||
| if (done) { | ||
| return; | ||
| } | ||
| const result = await reader.read(); | ||
| return handle(result); | ||
| }).catch((err) => { | ||
| console.error("Error reading query stream:", err); | ||
| }); | ||
| }; | ||
| if (handleRedirects) { | ||
| const ogMutationCacheConfig = queryClient.getMutationCache().config; | ||
| queryClient.getMutationCache().config = { | ||
| ...ogMutationCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (routerCore.isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogMutationCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| const ogQueryCacheConfig = queryClient.getQueryCache().config; | ||
| queryClient.getQueryCache().config = { | ||
| ...ogQueryCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (routerCore.isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogQueryCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| let _tanstack_query_core = require("@tanstack/query-core"); | ||
| let _tanstack_router_core = require("@tanstack/router-core"); | ||
| let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer"); | ||
| //#region src/index.ts | ||
| function setupCoreRouterSsrQueryIntegration({ router, queryClient, handleRedirects = true }) { | ||
| const ogHydrate = router.options.hydrate; | ||
| const ogDehydrate = router.options.dehydrate; | ||
| if (_tanstack_router_core_isServer.isServer ?? router.isServer) { | ||
| const sentQueries = /* @__PURE__ */ new Set(); | ||
| const queryStream = createPushableStream(); | ||
| let unsubscribe = void 0; | ||
| router.options.dehydrate = async () => { | ||
| router.serverSsr.onRenderFinished(() => { | ||
| queryStream.close(); | ||
| unsubscribe?.(); | ||
| unsubscribe = void 0; | ||
| }); | ||
| const dehydratedRouter = { | ||
| ...await ogDehydrate?.(), | ||
| queryStream: queryStream.stream | ||
| }; | ||
| const dehydratedQueryClient = (0, _tanstack_query_core.dehydrate)(queryClient); | ||
| if (dehydratedQueryClient.queries.length > 0) { | ||
| dehydratedQueryClient.queries.forEach((query) => { | ||
| sentQueries.add(query.queryHash); | ||
| }); | ||
| dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient; | ||
| } | ||
| return dehydratedRouter; | ||
| }; | ||
| const ogClientOptions = queryClient.getDefaultOptions(); | ||
| queryClient.setDefaultOptions({ | ||
| ...ogClientOptions, | ||
| dehydrate: { | ||
| shouldDehydrateQuery: () => true, | ||
| ...ogClientOptions.dehydrate | ||
| } | ||
| }); | ||
| unsubscribe = queryClient.getQueryCache().subscribe((event) => { | ||
| if (!router.serverSsr?.isDehydrated()) return; | ||
| if (sentQueries.has(event.query.queryHash)) return; | ||
| if (!event.query.promise) return; | ||
| if (queryStream.isClosed()) { | ||
| console.warn(`tried to stream query ${event.query.queryHash} after stream was already closed`); | ||
| return; | ||
| } | ||
| sentQueries.add(event.query.queryHash); | ||
| queryStream.enqueue((0, _tanstack_query_core.dehydrate)(queryClient, { shouldDehydrateQuery: (query) => { | ||
| if (query.queryHash === event.query.queryHash) return ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true; | ||
| return false; | ||
| } })); | ||
| }); | ||
| } else { | ||
| router.options.hydrate = async (dehydrated) => { | ||
| await ogHydrate?.(dehydrated); | ||
| if (dehydrated.dehydratedQueryClient) (0, _tanstack_query_core.hydrate)(queryClient, dehydrated.dehydratedQueryClient); | ||
| const reader = dehydrated.queryStream.getReader(); | ||
| reader.read().then(async function handle({ done, value }) { | ||
| (0, _tanstack_query_core.hydrate)(queryClient, value); | ||
| if (done) return; | ||
| return handle(await reader.read()); | ||
| }).catch((err) => { | ||
| console.error("Error reading query stream:", err); | ||
| }); | ||
| }; | ||
| if (handleRedirects) { | ||
| const ogMutationCacheConfig = queryClient.getMutationCache().config; | ||
| queryClient.getMutationCache().config = { | ||
| ...ogMutationCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if ((0, _tanstack_router_core.isRedirect)(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogMutationCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| const ogQueryCacheConfig = queryClient.getQueryCache().config; | ||
| queryClient.getQueryCache().config = { | ||
| ...ogQueryCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if ((0, _tanstack_router_core.isRedirect)(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogQueryCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| function createPushableStream() { | ||
| let controllerRef; | ||
| const stream = new ReadableStream({ | ||
| start(controller) { | ||
| controllerRef = controller; | ||
| } | ||
| }); | ||
| let _isClosed = false; | ||
| return { | ||
| stream, | ||
| enqueue: (chunk) => controllerRef.enqueue(chunk), | ||
| close: () => { | ||
| controllerRef.close(); | ||
| _isClosed = true; | ||
| }, | ||
| isClosed: () => _isClosed, | ||
| error: (err) => controllerRef.error(err) | ||
| }; | ||
| let controllerRef; | ||
| const stream = new ReadableStream({ start(controller) { | ||
| controllerRef = controller; | ||
| } }); | ||
| let _isClosed = false; | ||
| return { | ||
| stream, | ||
| enqueue: (chunk) => controllerRef.enqueue(chunk), | ||
| close: () => { | ||
| controllerRef.close(); | ||
| _isClosed = true; | ||
| }, | ||
| isClosed: () => _isClosed, | ||
| error: (err) => controllerRef.error(err) | ||
| }; | ||
| } | ||
| //#endregion | ||
| exports.setupCoreRouterSsrQueryIntegration = setupCoreRouterSsrQueryIntegration; | ||
| //# sourceMappingURL=index.cjs.map | ||
| //# sourceMappingURL=index.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import {\n dehydrate as queryDehydrate,\n hydrate as queryHydrate,\n} from '@tanstack/query-core'\nimport { isRedirect } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type {\n QueryClient,\n DehydratedState as QueryDehydratedState,\n} from '@tanstack/query-core'\n\nexport type RouterSsrQueryOptions<TRouter extends AnyRouter> = {\n router: TRouter\n queryClient: QueryClient\n\n /**\n * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.\n *\n * @default true\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)\n */\n handleRedirects?: boolean\n}\n\ntype DehydratedRouterQueryState = {\n dehydratedQueryClient?: QueryDehydratedState\n queryStream: ReadableStream<QueryDehydratedState>\n}\n\nexport function setupCoreRouterSsrQueryIntegration<TRouter extends AnyRouter>({\n router,\n queryClient,\n handleRedirects = true,\n}: RouterSsrQueryOptions<TRouter>) {\n const ogHydrate = router.options.hydrate\n const ogDehydrate = router.options.dehydrate\n\n if (isServer ?? router.isServer) {\n const sentQueries = new Set<string>()\n const queryStream = createPushableStream()\n let unsubscribe: (() => void) | undefined = undefined\n router.options.dehydrate =\n async (): Promise<DehydratedRouterQueryState> => {\n router.serverSsr!.onRenderFinished(() => {\n queryStream.close()\n unsubscribe?.()\n unsubscribe = undefined\n })\n const ogDehydrated = await ogDehydrate?.()\n\n const dehydratedRouter = {\n ...ogDehydrated,\n // prepare the stream for queries coming up during rendering\n queryStream: queryStream.stream,\n }\n\n const dehydratedQueryClient = queryDehydrate(queryClient)\n if (dehydratedQueryClient.queries.length > 0) {\n dehydratedQueryClient.queries.forEach((query) => {\n sentQueries.add(query.queryHash)\n })\n dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient\n }\n\n return dehydratedRouter\n }\n\n const ogClientOptions = queryClient.getDefaultOptions()\n queryClient.setDefaultOptions({\n ...ogClientOptions,\n dehydrate: {\n shouldDehydrateQuery: () => true,\n ...ogClientOptions.dehydrate,\n },\n })\n\n unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n // before rendering starts, we do not stream individual queries\n // instead we dehydrate the entire query client in router's dehydrate()\n // if attachRouterServerSsrUtils() has not been called yet, `router.serverSsr` will be undefined and we also do not stream\n if (!router.serverSsr?.isDehydrated()) {\n return\n }\n if (sentQueries.has(event.query.queryHash)) {\n return\n }\n // promise not yet set on the query, so we cannot stream it yet\n if (!event.query.promise) {\n return\n }\n if (queryStream.isClosed()) {\n console.warn(\n `tried to stream query ${event.query.queryHash} after stream was already closed`,\n )\n return\n }\n sentQueries.add(event.query.queryHash)\n queryStream.enqueue(\n queryDehydrate(queryClient, {\n shouldDehydrateQuery: (query) => {\n if (query.queryHash === event.query.queryHash) {\n return (\n ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true\n )\n }\n return false\n },\n }),\n )\n })\n // on the client\n } else {\n router.options.hydrate = async (dehydrated: DehydratedRouterQueryState) => {\n await ogHydrate?.(dehydrated)\n // hydrate the query client with the dehydrated data (if it was dehydrated on the server)\n if (dehydrated.dehydratedQueryClient) {\n queryHydrate(queryClient, dehydrated.dehydratedQueryClient)\n }\n\n // read the query stream and hydrate the queries as they come in\n const reader = dehydrated.queryStream.getReader()\n reader\n .read()\n .then(async function handle({ done, value }) {\n queryHydrate(queryClient, value)\n if (done) {\n return\n }\n const result = await reader.read()\n return handle(result)\n })\n .catch((err) => {\n console.error('Error reading query stream:', err)\n })\n }\n if (handleRedirects) {\n const ogMutationCacheConfig = queryClient.getMutationCache().config\n queryClient.getMutationCache().config = {\n ...ogMutationCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogMutationCacheConfig.onError?.(error, ...rest)\n },\n }\n\n const ogQueryCacheConfig = queryClient.getQueryCache().config\n queryClient.getQueryCache().config = {\n ...ogQueryCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogQueryCacheConfig.onError?.(error, ...rest)\n },\n }\n }\n }\n}\n\ntype PushableStream = {\n stream: ReadableStream\n enqueue: (chunk: unknown) => void\n close: () => void\n isClosed: () => boolean\n error: (err: unknown) => void\n}\n\nfunction createPushableStream(): PushableStream {\n let controllerRef: ReadableStreamDefaultController\n const stream = new ReadableStream({\n start(controller) {\n controllerRef = controller\n },\n })\n let _isClosed = false\n\n return {\n stream,\n enqueue: (chunk) => controllerRef.enqueue(chunk),\n close: () => {\n controllerRef.close()\n _isClosed = true\n },\n isClosed: () => _isClosed,\n error: (err: unknown) => controllerRef.error(err),\n }\n}\n"],"names":["isServer","queryDehydrate","queryHydrate","isRedirect"],"mappings":"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"} | ||
| {"version":3,"file":"index.cjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import {\n dehydrate as queryDehydrate,\n hydrate as queryHydrate,\n} from '@tanstack/query-core'\nimport { isRedirect } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type {\n QueryClient,\n DehydratedState as QueryDehydratedState,\n} from '@tanstack/query-core'\n\nexport type RouterSsrQueryOptions<TRouter extends AnyRouter> = {\n router: TRouter\n queryClient: QueryClient\n\n /**\n * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.\n *\n * @default true\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)\n */\n handleRedirects?: boolean\n}\n\ntype DehydratedRouterQueryState = {\n dehydratedQueryClient?: QueryDehydratedState\n queryStream: ReadableStream<QueryDehydratedState>\n}\n\nexport function setupCoreRouterSsrQueryIntegration<TRouter extends AnyRouter>({\n router,\n queryClient,\n handleRedirects = true,\n}: RouterSsrQueryOptions<TRouter>) {\n const ogHydrate = router.options.hydrate\n const ogDehydrate = router.options.dehydrate\n\n if (isServer ?? router.isServer) {\n const sentQueries = new Set<string>()\n const queryStream = createPushableStream()\n let unsubscribe: (() => void) | undefined = undefined\n router.options.dehydrate =\n async (): Promise<DehydratedRouterQueryState> => {\n router.serverSsr!.onRenderFinished(() => {\n queryStream.close()\n unsubscribe?.()\n unsubscribe = undefined\n })\n const ogDehydrated = await ogDehydrate?.()\n\n const dehydratedRouter = {\n ...ogDehydrated,\n // prepare the stream for queries coming up during rendering\n queryStream: queryStream.stream,\n }\n\n const dehydratedQueryClient = queryDehydrate(queryClient)\n if (dehydratedQueryClient.queries.length > 0) {\n dehydratedQueryClient.queries.forEach((query) => {\n sentQueries.add(query.queryHash)\n })\n dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient\n }\n\n return dehydratedRouter\n }\n\n const ogClientOptions = queryClient.getDefaultOptions()\n queryClient.setDefaultOptions({\n ...ogClientOptions,\n dehydrate: {\n shouldDehydrateQuery: () => true,\n ...ogClientOptions.dehydrate,\n },\n })\n\n unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n // before rendering starts, we do not stream individual queries\n // instead we dehydrate the entire query client in router's dehydrate()\n // if attachRouterServerSsrUtils() has not been called yet, `router.serverSsr` will be undefined and we also do not stream\n if (!router.serverSsr?.isDehydrated()) {\n return\n }\n if (sentQueries.has(event.query.queryHash)) {\n return\n }\n // promise not yet set on the query, so we cannot stream it yet\n if (!event.query.promise) {\n return\n }\n if (queryStream.isClosed()) {\n console.warn(\n `tried to stream query ${event.query.queryHash} after stream was already closed`,\n )\n return\n }\n sentQueries.add(event.query.queryHash)\n queryStream.enqueue(\n queryDehydrate(queryClient, {\n shouldDehydrateQuery: (query) => {\n if (query.queryHash === event.query.queryHash) {\n return (\n ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true\n )\n }\n return false\n },\n }),\n )\n })\n // on the client\n } else {\n router.options.hydrate = async (dehydrated: DehydratedRouterQueryState) => {\n await ogHydrate?.(dehydrated)\n // hydrate the query client with the dehydrated data (if it was dehydrated on the server)\n if (dehydrated.dehydratedQueryClient) {\n queryHydrate(queryClient, dehydrated.dehydratedQueryClient)\n }\n\n // read the query stream and hydrate the queries as they come in\n const reader = dehydrated.queryStream.getReader()\n reader\n .read()\n .then(async function handle({ done, value }) {\n queryHydrate(queryClient, value)\n if (done) {\n return\n }\n const result = await reader.read()\n return handle(result)\n })\n .catch((err) => {\n console.error('Error reading query stream:', err)\n })\n }\n if (handleRedirects) {\n const ogMutationCacheConfig = queryClient.getMutationCache().config\n queryClient.getMutationCache().config = {\n ...ogMutationCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogMutationCacheConfig.onError?.(error, ...rest)\n },\n }\n\n const ogQueryCacheConfig = queryClient.getQueryCache().config\n queryClient.getQueryCache().config = {\n ...ogQueryCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogQueryCacheConfig.onError?.(error, ...rest)\n },\n }\n }\n }\n}\n\ntype PushableStream = {\n stream: ReadableStream\n enqueue: (chunk: unknown) => void\n close: () => void\n isClosed: () => boolean\n error: (err: unknown) => void\n}\n\nfunction createPushableStream(): PushableStream {\n let controllerRef: ReadableStreamDefaultController\n const stream = new ReadableStream({\n start(controller) {\n controllerRef = controller\n },\n })\n let _isClosed = false\n\n return {\n stream,\n enqueue: (chunk) => controllerRef.enqueue(chunk),\n close: () => {\n controllerRef.close()\n _isClosed = true\n },\n isClosed: () => _isClosed,\n error: (err: unknown) => controllerRef.error(err),\n }\n}\n"],"mappings":"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"} |
+106
-132
| import { dehydrate, hydrate } from "@tanstack/query-core"; | ||
| import { isRedirect } from "@tanstack/router-core"; | ||
| import { isServer } from "@tanstack/router-core/isServer"; | ||
| function setupCoreRouterSsrQueryIntegration({ | ||
| router, | ||
| queryClient, | ||
| handleRedirects = true | ||
| }) { | ||
| const ogHydrate = router.options.hydrate; | ||
| const ogDehydrate = router.options.dehydrate; | ||
| if (isServer ?? router.isServer) { | ||
| const sentQueries = /* @__PURE__ */ new Set(); | ||
| const queryStream = createPushableStream(); | ||
| let unsubscribe = void 0; | ||
| router.options.dehydrate = async () => { | ||
| router.serverSsr.onRenderFinished(() => { | ||
| queryStream.close(); | ||
| unsubscribe?.(); | ||
| unsubscribe = void 0; | ||
| }); | ||
| const ogDehydrated = await ogDehydrate?.(); | ||
| const dehydratedRouter = { | ||
| ...ogDehydrated, | ||
| // prepare the stream for queries coming up during rendering | ||
| queryStream: queryStream.stream | ||
| }; | ||
| const dehydratedQueryClient = dehydrate(queryClient); | ||
| if (dehydratedQueryClient.queries.length > 0) { | ||
| dehydratedQueryClient.queries.forEach((query) => { | ||
| sentQueries.add(query.queryHash); | ||
| }); | ||
| dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient; | ||
| } | ||
| return dehydratedRouter; | ||
| }; | ||
| const ogClientOptions = queryClient.getDefaultOptions(); | ||
| queryClient.setDefaultOptions({ | ||
| ...ogClientOptions, | ||
| dehydrate: { | ||
| shouldDehydrateQuery: () => true, | ||
| ...ogClientOptions.dehydrate | ||
| } | ||
| }); | ||
| unsubscribe = queryClient.getQueryCache().subscribe((event) => { | ||
| if (!router.serverSsr?.isDehydrated()) { | ||
| return; | ||
| } | ||
| if (sentQueries.has(event.query.queryHash)) { | ||
| return; | ||
| } | ||
| if (!event.query.promise) { | ||
| return; | ||
| } | ||
| if (queryStream.isClosed()) { | ||
| console.warn( | ||
| `tried to stream query ${event.query.queryHash} after stream was already closed` | ||
| ); | ||
| return; | ||
| } | ||
| sentQueries.add(event.query.queryHash); | ||
| queryStream.enqueue( | ||
| dehydrate(queryClient, { | ||
| shouldDehydrateQuery: (query) => { | ||
| if (query.queryHash === event.query.queryHash) { | ||
| return ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true; | ||
| } | ||
| return false; | ||
| } | ||
| }) | ||
| ); | ||
| }); | ||
| } else { | ||
| router.options.hydrate = async (dehydrated) => { | ||
| await ogHydrate?.(dehydrated); | ||
| if (dehydrated.dehydratedQueryClient) { | ||
| hydrate(queryClient, dehydrated.dehydratedQueryClient); | ||
| } | ||
| const reader = dehydrated.queryStream.getReader(); | ||
| reader.read().then(async function handle({ done, value }) { | ||
| hydrate(queryClient, value); | ||
| if (done) { | ||
| return; | ||
| } | ||
| const result = await reader.read(); | ||
| return handle(result); | ||
| }).catch((err) => { | ||
| console.error("Error reading query stream:", err); | ||
| }); | ||
| }; | ||
| if (handleRedirects) { | ||
| const ogMutationCacheConfig = queryClient.getMutationCache().config; | ||
| queryClient.getMutationCache().config = { | ||
| ...ogMutationCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogMutationCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| const ogQueryCacheConfig = queryClient.getQueryCache().config; | ||
| queryClient.getQueryCache().config = { | ||
| ...ogQueryCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogQueryCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| //#region src/index.ts | ||
| function setupCoreRouterSsrQueryIntegration({ router, queryClient, handleRedirects = true }) { | ||
| const ogHydrate = router.options.hydrate; | ||
| const ogDehydrate = router.options.dehydrate; | ||
| if (isServer ?? router.isServer) { | ||
| const sentQueries = /* @__PURE__ */ new Set(); | ||
| const queryStream = createPushableStream(); | ||
| let unsubscribe = void 0; | ||
| router.options.dehydrate = async () => { | ||
| router.serverSsr.onRenderFinished(() => { | ||
| queryStream.close(); | ||
| unsubscribe?.(); | ||
| unsubscribe = void 0; | ||
| }); | ||
| const dehydratedRouter = { | ||
| ...await ogDehydrate?.(), | ||
| queryStream: queryStream.stream | ||
| }; | ||
| const dehydratedQueryClient = dehydrate(queryClient); | ||
| if (dehydratedQueryClient.queries.length > 0) { | ||
| dehydratedQueryClient.queries.forEach((query) => { | ||
| sentQueries.add(query.queryHash); | ||
| }); | ||
| dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient; | ||
| } | ||
| return dehydratedRouter; | ||
| }; | ||
| const ogClientOptions = queryClient.getDefaultOptions(); | ||
| queryClient.setDefaultOptions({ | ||
| ...ogClientOptions, | ||
| dehydrate: { | ||
| shouldDehydrateQuery: () => true, | ||
| ...ogClientOptions.dehydrate | ||
| } | ||
| }); | ||
| unsubscribe = queryClient.getQueryCache().subscribe((event) => { | ||
| if (!router.serverSsr?.isDehydrated()) return; | ||
| if (sentQueries.has(event.query.queryHash)) return; | ||
| if (!event.query.promise) return; | ||
| if (queryStream.isClosed()) { | ||
| console.warn(`tried to stream query ${event.query.queryHash} after stream was already closed`); | ||
| return; | ||
| } | ||
| sentQueries.add(event.query.queryHash); | ||
| queryStream.enqueue(dehydrate(queryClient, { shouldDehydrateQuery: (query) => { | ||
| if (query.queryHash === event.query.queryHash) return ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true; | ||
| return false; | ||
| } })); | ||
| }); | ||
| } else { | ||
| router.options.hydrate = async (dehydrated) => { | ||
| await ogHydrate?.(dehydrated); | ||
| if (dehydrated.dehydratedQueryClient) hydrate(queryClient, dehydrated.dehydratedQueryClient); | ||
| const reader = dehydrated.queryStream.getReader(); | ||
| reader.read().then(async function handle({ done, value }) { | ||
| hydrate(queryClient, value); | ||
| if (done) return; | ||
| return handle(await reader.read()); | ||
| }).catch((err) => { | ||
| console.error("Error reading query stream:", err); | ||
| }); | ||
| }; | ||
| if (handleRedirects) { | ||
| const ogMutationCacheConfig = queryClient.getMutationCache().config; | ||
| queryClient.getMutationCache().config = { | ||
| ...ogMutationCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogMutationCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| const ogQueryCacheConfig = queryClient.getQueryCache().config; | ||
| queryClient.getQueryCache().config = { | ||
| ...ogQueryCacheConfig, | ||
| onError: (error, ...rest) => { | ||
| if (isRedirect(error)) { | ||
| error.options._fromLocation = router.state.location; | ||
| return router.navigate(router.resolveRedirect(error).options); | ||
| } | ||
| return ogQueryCacheConfig.onError?.(error, ...rest); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| function createPushableStream() { | ||
| let controllerRef; | ||
| const stream = new ReadableStream({ | ||
| start(controller) { | ||
| controllerRef = controller; | ||
| } | ||
| }); | ||
| let _isClosed = false; | ||
| return { | ||
| stream, | ||
| enqueue: (chunk) => controllerRef.enqueue(chunk), | ||
| close: () => { | ||
| controllerRef.close(); | ||
| _isClosed = true; | ||
| }, | ||
| isClosed: () => _isClosed, | ||
| error: (err) => controllerRef.error(err) | ||
| }; | ||
| let controllerRef; | ||
| const stream = new ReadableStream({ start(controller) { | ||
| controllerRef = controller; | ||
| } }); | ||
| let _isClosed = false; | ||
| return { | ||
| stream, | ||
| enqueue: (chunk) => controllerRef.enqueue(chunk), | ||
| close: () => { | ||
| controllerRef.close(); | ||
| _isClosed = true; | ||
| }, | ||
| isClosed: () => _isClosed, | ||
| error: (err) => controllerRef.error(err) | ||
| }; | ||
| } | ||
| export { | ||
| setupCoreRouterSsrQueryIntegration | ||
| }; | ||
| //# sourceMappingURL=index.js.map | ||
| //#endregion | ||
| export { setupCoreRouterSsrQueryIntegration }; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import {\n dehydrate as queryDehydrate,\n hydrate as queryHydrate,\n} from '@tanstack/query-core'\nimport { isRedirect } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type {\n QueryClient,\n DehydratedState as QueryDehydratedState,\n} from '@tanstack/query-core'\n\nexport type RouterSsrQueryOptions<TRouter extends AnyRouter> = {\n router: TRouter\n queryClient: QueryClient\n\n /**\n * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.\n *\n * @default true\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)\n */\n handleRedirects?: boolean\n}\n\ntype DehydratedRouterQueryState = {\n dehydratedQueryClient?: QueryDehydratedState\n queryStream: ReadableStream<QueryDehydratedState>\n}\n\nexport function setupCoreRouterSsrQueryIntegration<TRouter extends AnyRouter>({\n router,\n queryClient,\n handleRedirects = true,\n}: RouterSsrQueryOptions<TRouter>) {\n const ogHydrate = router.options.hydrate\n const ogDehydrate = router.options.dehydrate\n\n if (isServer ?? router.isServer) {\n const sentQueries = new Set<string>()\n const queryStream = createPushableStream()\n let unsubscribe: (() => void) | undefined = undefined\n router.options.dehydrate =\n async (): Promise<DehydratedRouterQueryState> => {\n router.serverSsr!.onRenderFinished(() => {\n queryStream.close()\n unsubscribe?.()\n unsubscribe = undefined\n })\n const ogDehydrated = await ogDehydrate?.()\n\n const dehydratedRouter = {\n ...ogDehydrated,\n // prepare the stream for queries coming up during rendering\n queryStream: queryStream.stream,\n }\n\n const dehydratedQueryClient = queryDehydrate(queryClient)\n if (dehydratedQueryClient.queries.length > 0) {\n dehydratedQueryClient.queries.forEach((query) => {\n sentQueries.add(query.queryHash)\n })\n dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient\n }\n\n return dehydratedRouter\n }\n\n const ogClientOptions = queryClient.getDefaultOptions()\n queryClient.setDefaultOptions({\n ...ogClientOptions,\n dehydrate: {\n shouldDehydrateQuery: () => true,\n ...ogClientOptions.dehydrate,\n },\n })\n\n unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n // before rendering starts, we do not stream individual queries\n // instead we dehydrate the entire query client in router's dehydrate()\n // if attachRouterServerSsrUtils() has not been called yet, `router.serverSsr` will be undefined and we also do not stream\n if (!router.serverSsr?.isDehydrated()) {\n return\n }\n if (sentQueries.has(event.query.queryHash)) {\n return\n }\n // promise not yet set on the query, so we cannot stream it yet\n if (!event.query.promise) {\n return\n }\n if (queryStream.isClosed()) {\n console.warn(\n `tried to stream query ${event.query.queryHash} after stream was already closed`,\n )\n return\n }\n sentQueries.add(event.query.queryHash)\n queryStream.enqueue(\n queryDehydrate(queryClient, {\n shouldDehydrateQuery: (query) => {\n if (query.queryHash === event.query.queryHash) {\n return (\n ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true\n )\n }\n return false\n },\n }),\n )\n })\n // on the client\n } else {\n router.options.hydrate = async (dehydrated: DehydratedRouterQueryState) => {\n await ogHydrate?.(dehydrated)\n // hydrate the query client with the dehydrated data (if it was dehydrated on the server)\n if (dehydrated.dehydratedQueryClient) {\n queryHydrate(queryClient, dehydrated.dehydratedQueryClient)\n }\n\n // read the query stream and hydrate the queries as they come in\n const reader = dehydrated.queryStream.getReader()\n reader\n .read()\n .then(async function handle({ done, value }) {\n queryHydrate(queryClient, value)\n if (done) {\n return\n }\n const result = await reader.read()\n return handle(result)\n })\n .catch((err) => {\n console.error('Error reading query stream:', err)\n })\n }\n if (handleRedirects) {\n const ogMutationCacheConfig = queryClient.getMutationCache().config\n queryClient.getMutationCache().config = {\n ...ogMutationCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogMutationCacheConfig.onError?.(error, ...rest)\n },\n }\n\n const ogQueryCacheConfig = queryClient.getQueryCache().config\n queryClient.getQueryCache().config = {\n ...ogQueryCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogQueryCacheConfig.onError?.(error, ...rest)\n },\n }\n }\n }\n}\n\ntype PushableStream = {\n stream: ReadableStream\n enqueue: (chunk: unknown) => void\n close: () => void\n isClosed: () => boolean\n error: (err: unknown) => void\n}\n\nfunction createPushableStream(): PushableStream {\n let controllerRef: ReadableStreamDefaultController\n const stream = new ReadableStream({\n start(controller) {\n controllerRef = controller\n },\n })\n let _isClosed = false\n\n return {\n stream,\n enqueue: (chunk) => controllerRef.enqueue(chunk),\n close: () => {\n controllerRef.close()\n _isClosed = true\n },\n isClosed: () => _isClosed,\n error: (err: unknown) => controllerRef.error(err),\n }\n}\n"],"names":["queryDehydrate","queryHydrate"],"mappings":";;;AA8BO,SAAS,mCAA8D;AAAA,EAC5E;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAAmC;AACjC,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,cAAc,OAAO,QAAQ;AAEnC,MAAI,YAAY,OAAO,UAAU;AAC/B,UAAM,kCAAkB,IAAA;AACxB,UAAM,cAAc,qBAAA;AACpB,QAAI,cAAwC;AAC5C,WAAO,QAAQ,YACb,YAAiD;AAC/C,aAAO,UAAW,iBAAiB,MAAM;AACvC,oBAAY,MAAA;AACZ,sBAAA;AACA,sBAAc;AAAA,MAChB,CAAC;AACD,YAAM,eAAe,MAAM,cAAA;AAE3B,YAAM,mBAAmB;AAAA,QACvB,GAAG;AAAA;AAAA,QAEH,aAAa,YAAY;AAAA,MAAA;AAG3B,YAAM,wBAAwBA,UAAe,WAAW;AACxD,UAAI,sBAAsB,QAAQ,SAAS,GAAG;AAC5C,8BAAsB,QAAQ,QAAQ,CAAC,UAAU;AAC/C,sBAAY,IAAI,MAAM,SAAS;AAAA,QACjC,CAAC;AACD,yBAAiB,wBAAwB;AAAA,MAC3C;AAEA,aAAO;AAAA,IACT;AAEF,UAAM,kBAAkB,YAAY,kBAAA;AACpC,gBAAY,kBAAkB;AAAA,MAC5B,GAAG;AAAA,MACH,WAAW;AAAA,QACT,sBAAsB,MAAM;AAAA,QAC5B,GAAG,gBAAgB;AAAA,MAAA;AAAA,IACrB,CACD;AAED,kBAAc,YAAY,cAAA,EAAgB,UAAU,CAAC,UAAU;AAI7D,UAAI,CAAC,OAAO,WAAW,gBAAgB;AACrC;AAAA,MACF;AACA,UAAI,YAAY,IAAI,MAAM,MAAM,SAAS,GAAG;AAC1C;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,MAAM,SAAS;AACxB;AAAA,MACF;AACA,UAAI,YAAY,YAAY;AAC1B,gBAAQ;AAAA,UACN,yBAAyB,MAAM,MAAM,SAAS;AAAA,QAAA;AAEhD;AAAA,MACF;AACA,kBAAY,IAAI,MAAM,MAAM,SAAS;AACrC,kBAAY;AAAA,QACVA,UAAe,aAAa;AAAA,UAC1B,sBAAsB,CAAC,UAAU;AAC/B,gBAAI,MAAM,cAAc,MAAM,MAAM,WAAW;AAC7C,qBACE,gBAAgB,WAAW,uBAAuB,KAAK,KAAK;AAAA,YAEhE;AACA,mBAAO;AAAA,UACT;AAAA,QAAA,CACD;AAAA,MAAA;AAAA,IAEL,CAAC;AAAA,EAEH,OAAO;AACL,WAAO,QAAQ,UAAU,OAAO,eAA2C;AACzE,YAAM,YAAY,UAAU;AAE5B,UAAI,WAAW,uBAAuB;AACpCC,gBAAa,aAAa,WAAW,qBAAqB;AAAA,MAC5D;AAGA,YAAM,SAAS,WAAW,YAAY,UAAA;AACtC,aACG,KAAA,EACA,KAAK,eAAe,OAAO,EAAE,MAAM,SAAS;AAC3CA,gBAAa,aAAa,KAAK;AAC/B,YAAI,MAAM;AACR;AAAA,QACF;AACA,cAAM,SAAS,MAAM,OAAO,KAAA;AAC5B,eAAO,OAAO,MAAM;AAAA,MACtB,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,gBAAQ,MAAM,+BAA+B,GAAG;AAAA,MAClD,CAAC;AAAA,IACL;AACA,QAAI,iBAAiB;AACnB,YAAM,wBAAwB,YAAY,iBAAA,EAAmB;AAC7D,kBAAY,iBAAA,EAAmB,SAAS;AAAA,QACtC,GAAG;AAAA,QACH,SAAS,CAAC,UAAU,SAAS;AAC3B,cAAI,WAAW,KAAK,GAAG;AACrB,kBAAM,QAAQ,gBAAgB,OAAO,MAAM;AAC3C,mBAAO,OAAO,SAAS,OAAO,gBAAgB,KAAK,EAAE,OAAO;AAAA,UAC9D;AAEA,iBAAO,sBAAsB,UAAU,OAAO,GAAG,IAAI;AAAA,QACvD;AAAA,MAAA;AAGF,YAAM,qBAAqB,YAAY,cAAA,EAAgB;AACvD,kBAAY,cAAA,EAAgB,SAAS;AAAA,QACnC,GAAG;AAAA,QACH,SAAS,CAAC,UAAU,SAAS;AAC3B,cAAI,WAAW,KAAK,GAAG;AACrB,kBAAM,QAAQ,gBAAgB,OAAO,MAAM;AAC3C,mBAAO,OAAO,SAAS,OAAO,gBAAgB,KAAK,EAAE,OAAO;AAAA,UAC9D;AAEA,iBAAO,mBAAmB,UAAU,OAAO,GAAG,IAAI;AAAA,QACpD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAUA,SAAS,uBAAuC;AAC9C,MAAI;AACJ,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,YAAY;AAChB,sBAAgB;AAAA,IAClB;AAAA,EAAA,CACD;AACD,MAAI,YAAY;AAEhB,SAAO;AAAA,IACL;AAAA,IACA,SAAS,CAAC,UAAU,cAAc,QAAQ,KAAK;AAAA,IAC/C,OAAO,MAAM;AACX,oBAAc,MAAA;AACd,kBAAY;AAAA,IACd;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,OAAO,CAAC,QAAiB,cAAc,MAAM,GAAG;AAAA,EAAA;AAEpD;"} | ||
| {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import {\n dehydrate as queryDehydrate,\n hydrate as queryHydrate,\n} from '@tanstack/query-core'\nimport { isRedirect } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type {\n QueryClient,\n DehydratedState as QueryDehydratedState,\n} from '@tanstack/query-core'\n\nexport type RouterSsrQueryOptions<TRouter extends AnyRouter> = {\n router: TRouter\n queryClient: QueryClient\n\n /**\n * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.\n *\n * @default true\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)\n */\n handleRedirects?: boolean\n}\n\ntype DehydratedRouterQueryState = {\n dehydratedQueryClient?: QueryDehydratedState\n queryStream: ReadableStream<QueryDehydratedState>\n}\n\nexport function setupCoreRouterSsrQueryIntegration<TRouter extends AnyRouter>({\n router,\n queryClient,\n handleRedirects = true,\n}: RouterSsrQueryOptions<TRouter>) {\n const ogHydrate = router.options.hydrate\n const ogDehydrate = router.options.dehydrate\n\n if (isServer ?? router.isServer) {\n const sentQueries = new Set<string>()\n const queryStream = createPushableStream()\n let unsubscribe: (() => void) | undefined = undefined\n router.options.dehydrate =\n async (): Promise<DehydratedRouterQueryState> => {\n router.serverSsr!.onRenderFinished(() => {\n queryStream.close()\n unsubscribe?.()\n unsubscribe = undefined\n })\n const ogDehydrated = await ogDehydrate?.()\n\n const dehydratedRouter = {\n ...ogDehydrated,\n // prepare the stream for queries coming up during rendering\n queryStream: queryStream.stream,\n }\n\n const dehydratedQueryClient = queryDehydrate(queryClient)\n if (dehydratedQueryClient.queries.length > 0) {\n dehydratedQueryClient.queries.forEach((query) => {\n sentQueries.add(query.queryHash)\n })\n dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient\n }\n\n return dehydratedRouter\n }\n\n const ogClientOptions = queryClient.getDefaultOptions()\n queryClient.setDefaultOptions({\n ...ogClientOptions,\n dehydrate: {\n shouldDehydrateQuery: () => true,\n ...ogClientOptions.dehydrate,\n },\n })\n\n unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n // before rendering starts, we do not stream individual queries\n // instead we dehydrate the entire query client in router's dehydrate()\n // if attachRouterServerSsrUtils() has not been called yet, `router.serverSsr` will be undefined and we also do not stream\n if (!router.serverSsr?.isDehydrated()) {\n return\n }\n if (sentQueries.has(event.query.queryHash)) {\n return\n }\n // promise not yet set on the query, so we cannot stream it yet\n if (!event.query.promise) {\n return\n }\n if (queryStream.isClosed()) {\n console.warn(\n `tried to stream query ${event.query.queryHash} after stream was already closed`,\n )\n return\n }\n sentQueries.add(event.query.queryHash)\n queryStream.enqueue(\n queryDehydrate(queryClient, {\n shouldDehydrateQuery: (query) => {\n if (query.queryHash === event.query.queryHash) {\n return (\n ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true\n )\n }\n return false\n },\n }),\n )\n })\n // on the client\n } else {\n router.options.hydrate = async (dehydrated: DehydratedRouterQueryState) => {\n await ogHydrate?.(dehydrated)\n // hydrate the query client with the dehydrated data (if it was dehydrated on the server)\n if (dehydrated.dehydratedQueryClient) {\n queryHydrate(queryClient, dehydrated.dehydratedQueryClient)\n }\n\n // read the query stream and hydrate the queries as they come in\n const reader = dehydrated.queryStream.getReader()\n reader\n .read()\n .then(async function handle({ done, value }) {\n queryHydrate(queryClient, value)\n if (done) {\n return\n }\n const result = await reader.read()\n return handle(result)\n })\n .catch((err) => {\n console.error('Error reading query stream:', err)\n })\n }\n if (handleRedirects) {\n const ogMutationCacheConfig = queryClient.getMutationCache().config\n queryClient.getMutationCache().config = {\n ...ogMutationCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogMutationCacheConfig.onError?.(error, ...rest)\n },\n }\n\n const ogQueryCacheConfig = queryClient.getQueryCache().config\n queryClient.getQueryCache().config = {\n ...ogQueryCacheConfig,\n onError: (error, ...rest) => {\n if (isRedirect(error)) {\n error.options._fromLocation = router.state.location\n return router.navigate(router.resolveRedirect(error).options)\n }\n\n return ogQueryCacheConfig.onError?.(error, ...rest)\n },\n }\n }\n }\n}\n\ntype PushableStream = {\n stream: ReadableStream\n enqueue: (chunk: unknown) => void\n close: () => void\n isClosed: () => boolean\n error: (err: unknown) => void\n}\n\nfunction createPushableStream(): PushableStream {\n let controllerRef: ReadableStreamDefaultController\n const stream = new ReadableStream({\n start(controller) {\n controllerRef = controller\n },\n })\n let _isClosed = false\n\n return {\n stream,\n enqueue: (chunk) => controllerRef.enqueue(chunk),\n close: () => {\n controllerRef.close()\n _isClosed = true\n },\n isClosed: () => _isClosed,\n error: (err: unknown) => controllerRef.error(err),\n }\n}\n"],"mappings":";;;;AA8BA,SAAgB,mCAA8D,EAC5E,QACA,aACA,kBAAkB,QACe;CACjC,MAAM,YAAY,OAAO,QAAQ;CACjC,MAAM,cAAc,OAAO,QAAQ;AAEnC,KAAI,YAAY,OAAO,UAAU;EAC/B,MAAM,8BAAc,IAAI,KAAa;EACrC,MAAM,cAAc,sBAAsB;EAC1C,IAAI,cAAwC,KAAA;AAC5C,SAAO,QAAQ,YACb,YAAiD;AAC/C,UAAO,UAAW,uBAAuB;AACvC,gBAAY,OAAO;AACnB,mBAAe;AACf,kBAAc,KAAA;KACd;GAGF,MAAM,mBAAmB;IACvB,GAHmB,MAAM,eAAe;IAKxC,aAAa,YAAY;IAC1B;GAED,MAAM,wBAAwB,UAAe,YAAY;AACzD,OAAI,sBAAsB,QAAQ,SAAS,GAAG;AAC5C,0BAAsB,QAAQ,SAAS,UAAU;AAC/C,iBAAY,IAAI,MAAM,UAAU;MAChC;AACF,qBAAiB,wBAAwB;;AAG3C,UAAO;;EAGX,MAAM,kBAAkB,YAAY,mBAAmB;AACvD,cAAY,kBAAkB;GAC5B,GAAG;GACH,WAAW;IACT,4BAA4B;IAC5B,GAAG,gBAAgB;IACpB;GACF,CAAC;AAEF,gBAAc,YAAY,eAAe,CAAC,WAAW,UAAU;AAI7D,OAAI,CAAC,OAAO,WAAW,cAAc,CACnC;AAEF,OAAI,YAAY,IAAI,MAAM,MAAM,UAAU,CACxC;AAGF,OAAI,CAAC,MAAM,MAAM,QACf;AAEF,OAAI,YAAY,UAAU,EAAE;AAC1B,YAAQ,KACN,yBAAyB,MAAM,MAAM,UAAU,kCAChD;AACD;;AAEF,eAAY,IAAI,MAAM,MAAM,UAAU;AACtC,eAAY,QACV,UAAe,aAAa,EAC1B,uBAAuB,UAAU;AAC/B,QAAI,MAAM,cAAc,MAAM,MAAM,UAClC,QACE,gBAAgB,WAAW,uBAAuB,MAAM,IAAI;AAGhE,WAAO;MAEV,CAAC,CACH;IACD;QAEG;AACL,SAAO,QAAQ,UAAU,OAAO,eAA2C;AACzE,SAAM,YAAY,WAAW;AAE7B,OAAI,WAAW,sBACb,SAAa,aAAa,WAAW,sBAAsB;GAI7D,MAAM,SAAS,WAAW,YAAY,WAAW;AACjD,UACG,MAAM,CACN,KAAK,eAAe,OAAO,EAAE,MAAM,SAAS;AAC3C,YAAa,aAAa,MAAM;AAChC,QAAI,KACF;AAGF,WAAO,OADQ,MAAM,OAAO,MAAM,CACb;KACrB,CACD,OAAO,QAAQ;AACd,YAAQ,MAAM,+BAA+B,IAAI;KACjD;;AAEN,MAAI,iBAAiB;GACnB,MAAM,wBAAwB,YAAY,kBAAkB,CAAC;AAC7D,eAAY,kBAAkB,CAAC,SAAS;IACtC,GAAG;IACH,UAAU,OAAO,GAAG,SAAS;AAC3B,SAAI,WAAW,MAAM,EAAE;AACrB,YAAM,QAAQ,gBAAgB,OAAO,MAAM;AAC3C,aAAO,OAAO,SAAS,OAAO,gBAAgB,MAAM,CAAC,QAAQ;;AAG/D,YAAO,sBAAsB,UAAU,OAAO,GAAG,KAAK;;IAEzD;GAED,MAAM,qBAAqB,YAAY,eAAe,CAAC;AACvD,eAAY,eAAe,CAAC,SAAS;IACnC,GAAG;IACH,UAAU,OAAO,GAAG,SAAS;AAC3B,SAAI,WAAW,MAAM,EAAE;AACrB,YAAM,QAAQ,gBAAgB,OAAO,MAAM;AAC3C,aAAO,OAAO,SAAS,OAAO,gBAAgB,MAAM,CAAC,QAAQ;;AAG/D,YAAO,mBAAmB,UAAU,OAAO,GAAG,KAAK;;IAEtD;;;;AAaP,SAAS,uBAAuC;CAC9C,IAAI;CACJ,MAAM,SAAS,IAAI,eAAe,EAChC,MAAM,YAAY;AAChB,kBAAgB;IAEnB,CAAC;CACF,IAAI,YAAY;AAEhB,QAAO;EACL;EACA,UAAU,UAAU,cAAc,QAAQ,MAAM;EAChD,aAAa;AACX,iBAAc,OAAO;AACrB,eAAY;;EAEd,gBAAgB;EAChB,QAAQ,QAAiB,cAAc,MAAM,IAAI;EAClD"} |
+2
-2
| { | ||
| "name": "@tanstack/router-ssr-query-core", | ||
| "version": "1.166.8", | ||
| "version": "1.166.9", | ||
| "description": "Modern and scalable routing for React applications", | ||
@@ -52,3 +52,3 @@ "author": "Tanner Linsley", | ||
| "devDependencies": { | ||
| "@tanstack/router-core": ">=1.167.1", | ||
| "@tanstack/router-core": ">=1.167.2", | ||
| "@tanstack/query-core": ">=5.90.0", | ||
@@ -55,0 +55,0 @@ "vite": "*" |
35923
-5.77%412
-11.78%