diff --git a/.changeset/query-optional-mode.md b/.changeset/query-optional-mode.md new file mode 100644 index 000000000..c28dbb23a --- /dev/null +++ b/.changeset/query-optional-mode.md @@ -0,0 +1,5 @@ +--- +"@effect-app/vue": minor +--- + +Add `mode: "optional"` overload for queries. When `mode: "optional"` is set, the `arg` must be a `WatchSource>`. The query is disabled (`enabled: false`) when the option is `None`, and enabled with the unwrapped value when `Some`. diff --git a/packages/vue/src/query.ts b/packages/vue/src/query.ts index 0ce2314d5..02c59bbcf 100644 --- a/packages/vue/src/query.ts +++ b/packages/vue/src/query.ts @@ -149,6 +149,16 @@ export const makeQuery = (getRuntime: () => Context.Context) => { ( q: RequestHandlerWithInput ): { + ( + arg: WatchSource>, + options: Omit, "enabled"> & { mode: "optional" } + ): readonly [ + ComputedRef>, + ComputedRef, + (options?: RefetchOptions) => Effect.Effect>>, + UseQueryDefinedReturnType> + ] + ( arg: I | WatchSource | undefined, options?: CustomUndefinedInitialQueryOptions @@ -183,7 +193,7 @@ export const makeQuery = (getRuntime: () => Context.Context) => { q: RequestHandlerWithInput ) => ( - arg: I | WatchSource | undefined, + arg: I | WatchSource | undefined | WatchSource>, // todo QueryKey type would be [string, ...string[]], but with I it would be [string, ...string[], I] options?: any // TODO @@ -191,15 +201,34 @@ export const makeQuery = (getRuntime: () => Context.Context) => { // we wrap into CauseException because we want to keep the full cause of the failure. const runPromise = makeRunPromise(getRuntime()) const arr = arg - const req: { value: I } | undefined = !arg - ? undefined - : typeof arr === "function" - ? ({ + + let req: { value: I } | undefined + let callerOptions: any = options + + if (options?.mode === "optional") { + const getOption: () => Option.Option = typeof arr === "function" + ? arr as () => Option.Option + : () => (arr as { value: Option.Option }).value + req = { get value() { - return (arr as any)() + // getOrUndefined returns undefined when None, but queryFn is only called when enabled (Some) + return Option.getOrUndefined(getOption()) as I } - }) - : ref(arg) as any + } + const { mode: _mode, enabled: _enabled, ...rest } = options ?? {} + callerOptions = { ...rest, enabled: computed(() => Option.isSome(getOption())) } + } else { + req = !arg + ? undefined + : typeof arr === "function" + ? ({ + get value() { + return (arr as any)() + } + }) + : ref(arg) as any + } + const queryKey = makeQueryKey(q) const projectionHash = (q as { queryKeyProjectionHash?: string }).queryKeyProjectionHash @@ -215,7 +244,7 @@ export const makeQuery = (getRuntime: () => Context.Context) => { const r = useTanstackQuery, TData>({ ...defaultOptions, - ...options, + ...callerOptions, retry: (retryCount, error) => { if (error instanceof CauseException) { if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) { @@ -274,6 +303,15 @@ export const makeQuery = (getRuntime: () => Context.Context) => { ( self: RequestHandlerWithInput ): { + ( + arg: WatchSource>, + options: Omit, TData>, "enabled"> & { mode: "optional" } + ): readonly [ + ComputedRef>, + ComputedRef, + (options?: RefetchOptions) => Effect.Effect>>, + UseQueryReturnType + ] ( arg: I | WatchSource, options: CustomDefinedInitialQueryOptions, TData>