diff --git a/packages/vue-query-nuxt/package.json b/packages/vue-query-nuxt/package.json index 5dbddf51..4184f8a8 100644 --- a/packages/vue-query-nuxt/package.json +++ b/packages/vue-query-nuxt/package.json @@ -62,7 +62,8 @@ "@nuxt/kit": "^3.11.2", "defu": "^6.1.4", "esbuild": "^0.20.2", - "magicast": "^0.3.4" + "magicast": "^0.3.4", + "serialize-error": "^12.0.0" }, "devDependencies": { "@nuxt/module-builder": "^0.5.5" diff --git a/packages/vue-query-nuxt/src/module.ts b/packages/vue-query-nuxt/src/module.ts index 2b70e03d..e04283ed 100644 --- a/packages/vue-query-nuxt/src/module.ts +++ b/packages/vue-query-nuxt/src/module.ts @@ -56,7 +56,7 @@ export default defineNuxtModule({ else { logger.info("No vue-query.config.ts file found.") } - return "export function pluginHook() { return { pluginReturn: null, vueQueryPluginOptions: null}}" + return "export function pluginHook() { return {} }" } }) diff --git a/packages/vue-query-nuxt/src/runtime/plugin.ts b/packages/vue-query-nuxt/src/runtime/plugin.ts index a6562c9a..9bab5d4a 100644 --- a/packages/vue-query-nuxt/src/runtime/plugin.ts +++ b/packages/vue-query-nuxt/src/runtime/plugin.ts @@ -1,5 +1,6 @@ import type { DehydratedState } from "@tanstack/vue-query" import { QueryClient, VueQueryPlugin, dehydrate, hydrate } from "@tanstack/vue-query" +import { isErrorLike, deserializeError, serializeError, type Options } from 'serialize-error' import { getVueQueryOptions } from "./utils" import { pluginHook } from "#build/internal.vue-query-plugin-hook" import { defineNuxtPlugin, useRuntimeConfig, useState } from "#imports" @@ -10,17 +11,62 @@ export default defineNuxtPlugin((nuxt) => { const queryClient = new QueryClient(queryClientOptions) // The plugin hook is replaced by the user provided vue-query.config.ts and allow advanced modifications - const { pluginReturn, vueQueryPluginOptions: hookOptions } = pluginHook({ queryClient, nuxt }) + const { + pluginReturn, + vueQueryPluginOptions: hookOptions, + hydrateOptions, + dehydrateOptions, + ...pluginHookReturn + } = pluginHook({ queryClient, nuxt }) + let { + serializeErrorOptions, + deserializeErrorOptions + } = pluginHookReturn + serializeErrorOptions = { ...serializeErrorOptions, useToJSON: false } as Options + deserializeErrorOptions = { ...deserializeErrorOptions, useToJSON: false } as Options nuxt.vueApp.use(VueQueryPlugin, { queryClient, ...vueQueryPluginOptions, ...hookOptions }) + type Dehydrated = ReturnType + type QueryOrMutations = Dehydrated['queries'] | Dehydrated['mutations'] if (import.meta.server) { nuxt.hooks.hook("app:rendered", () => { - vueQueryState.value = dehydrate(queryClient) + const serializeErrors = (queryOrMutations: T): T => queryOrMutations + .map(queryOrMutation => { + const state = queryOrMutation.state + if (state.error instanceof Error) + state.error = serializeError(state.error, serializeErrorOptions) as Error | null + if ('fetchFailureReason' in state && state.fetchFailureReason instanceof Error) + state.fetchFailureReason = serializeError(state.fetchFailureReason, serializeErrorOptions) as Error | null + if ('failureReason' in state && state.failureReason instanceof Error) + state.failureReason = serializeError(state.failureReason, serializeErrorOptions) as Error | null + return queryOrMutation + }) as T + const dehydrated = dehydrate(queryClient, dehydrateOptions) + dehydrated.queries = serializeErrors(dehydrated.queries) + dehydrated.mutations = serializeErrors(dehydrated.mutations) + vueQueryState.value = dehydrated }) } - if (import.meta.client) hydrate(queryClient, vueQueryState.value) + if (import.meta.client) { + const deserializeErrors = (queryOrMutations: T): T => queryOrMutations + .map(queryOrMutation => { + const state = queryOrMutation.state + if (isErrorLike(state.error)) + state.error = deserializeError(state.error, deserializeErrorOptions) + if ('fetchFailureReason' in state && isErrorLike(state.fetchFailureReason)) + state.fetchFailureReason = deserializeError(state.fetchFailureReason, deserializeErrorOptions) + if ('failureReason' in state && isErrorLike(state.failureReason)) + state.failureReason = deserializeError(state.failureReason, deserializeErrorOptions) + return queryOrMutation + }) as T + const dehydrated = vueQueryState.value + dehydrated.queries = deserializeErrors(dehydrated.queries) + dehydrated.mutations = deserializeErrors(dehydrated.mutations) + hydrate(queryClient, dehydrated, hydrateOptions) + } - return pluginReturn + if (pluginReturn !== undefined) return pluginReturn + return }) diff --git a/packages/vue-query-nuxt/src/runtime/types.ts b/packages/vue-query-nuxt/src/runtime/types.ts index 66369801..57a2619c 100644 --- a/packages/vue-query-nuxt/src/runtime/types.ts +++ b/packages/vue-query-nuxt/src/runtime/types.ts @@ -1,11 +1,8 @@ -import type { NuxtApp } from "nuxt/app" -import type { QueryClient, VueQueryPluginOptions } from "@tanstack/vue-query" +import type { NuxtApp, Plugin } from "nuxt/app" +import type { DehydrateOptions, HydrateOptions, QueryClient, VueQueryPluginOptions } from "@tanstack/vue-query" +import type { Options } from "serialize-error" -export type NuxtPluginReturn = -| void -| Promise -| Promise<{ provide?: Record | undefined }> -| { provide?: Record | undefined } +export type NuxtPluginReturn = ReturnType // NuxtApp & _NuxtApp are different so we use any export interface PluginHookParameters { @@ -13,4 +10,11 @@ export interface PluginHookParameters { queryClient: QueryClient } -export interface PluginHookReturn { pluginReturn: NuxtPluginReturn; vueQueryPluginOptions?: VueQueryPluginOptions } +export interface PluginHookReturn { + pluginReturn?: NuxtPluginReturn, + vueQueryPluginOptions?: VueQueryPluginOptions, + hydrateOptions?: HydrateOptions, + dehydrateOptions?: DehydrateOptions, + serializeErrorOptions?: Omit, + deserializeErrorOptions?: Omit +} diff --git a/packages/vue-query-nuxt/src/runtime/virtual:pluginHook.ts b/packages/vue-query-nuxt/src/runtime/virtual:pluginHook.ts index dc2ee619..2ce92681 100644 --- a/packages/vue-query-nuxt/src/runtime/virtual:pluginHook.ts +++ b/packages/vue-query-nuxt/src/runtime/virtual:pluginHook.ts @@ -6,5 +6,5 @@ import type { PluginHookParameters, PluginHookReturn } from "./types" // eslint-disable-next-line unused-imports/no-unused-vars export function pluginHook(pluginHookParameters: PluginHookParameters): PluginHookReturn { - return { pluginReturn: {}, vueQueryPluginOptions: {} } + return {} }