@effect-app/vue 1.22.5 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/_cjs/errorReporter.cjs +12 -12
- package/_cjs/errorReporter.cjs.map +1 -1
- package/_cjs/internal.cjs +1 -13
- package/_cjs/internal.cjs.map +1 -1
- package/_cjs/makeClient.cjs +8 -8
- package/_cjs/makeClient.cjs.map +1 -1
- package/_cjs/mutate.cjs +79 -75
- package/_cjs/mutate.cjs.map +1 -1
- package/_cjs/query.cjs +86 -83
- package/_cjs/query.cjs.map +1 -1
- package/_cjs/runtime.cjs +0 -9
- package/_cjs/runtime.cjs.map +1 -1
- package/dist/errorReporter.d.ts +3 -3
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +2 -2
- package/dist/form.d.ts +2 -2
- package/dist/form.d.ts.map +1 -1
- package/dist/internal.d.ts +1 -11
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -13
- package/dist/makeClient.d.ts +35 -35
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +8 -7
- package/dist/mutate.d.ts +12 -24
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +67 -63
- package/dist/query.d.ts +14 -31
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +97 -94
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -5
- package/package.json +2 -2
- package/src/errorReporter.ts +3 -3
- package/src/form.ts +2 -2
- package/src/internal.ts +1 -24
- package/src/makeClient.ts +38 -35
- package/src/mutate.ts +123 -118
- package/src/query.ts +172 -167
- package/src/runtime.ts +0 -5
package/src/mutate.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { tuple } from "@effect-app/core/Function"
|
|
3
3
|
import * as Result from "@effect-rx/rx/Result"
|
|
4
|
-
import type * as HttpClient from "@effect/platform/HttpClient"
|
|
5
4
|
import type { InvalidateOptions, InvalidateQueryFilters } from "@tanstack/vue-query"
|
|
6
5
|
import { useQueryClient } from "@tanstack/vue-query"
|
|
7
|
-
import { Cause, Effect, Exit, Option } from "effect-app"
|
|
8
|
-
import type { ApiConfig } from "effect-app/client"
|
|
6
|
+
import { Cause, Effect, Exit, Option, Runtime } from "effect-app"
|
|
9
7
|
import { dropUndefinedT } from "effect-app/utils"
|
|
10
8
|
import { InterruptedException } from "effect/Cause"
|
|
11
9
|
import * as Either from "effect/Either"
|
|
12
10
|
import type { ComputedRef, Ref } from "vue"
|
|
13
11
|
import { computed, ref, shallowRef } from "vue"
|
|
14
|
-
import { makeQueryKey, reportRuntimeError
|
|
12
|
+
import { makeQueryKey, reportRuntimeError } from "./internal.js"
|
|
15
13
|
|
|
16
14
|
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
|
|
17
15
|
export function make<A, E, R>(self: Effect<A, E, R>) {
|
|
@@ -53,12 +51,6 @@ export interface MutationError<E> {
|
|
|
53
51
|
|
|
54
52
|
export type MutationResult<A, E> = MutationInitial | MutationLoading | MutationSuccess<A> | MutationError<E>
|
|
55
53
|
|
|
56
|
-
type HandlerWithInput<I, A, E> = {
|
|
57
|
-
handler: (i: I) => Effect<A, E, ApiConfig | HttpClient.HttpClient>
|
|
58
|
-
name: string
|
|
59
|
-
}
|
|
60
|
-
type Handler<A, E> = { handler: Effect<A, E, ApiConfig | HttpClient.HttpClient>; name: string }
|
|
61
|
-
|
|
62
54
|
export type MaybeRef<T> = Ref<T> | ComputedRef<T> | T
|
|
63
55
|
type MaybeRefDeep<T> = MaybeRef<
|
|
64
56
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
@@ -97,125 +89,138 @@ export const getQueryKey = (name: string) => {
|
|
|
97
89
|
// }
|
|
98
90
|
*/
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*/
|
|
104
|
-
export const useSafeMutation: {
|
|
105
|
-
<I, E, A>(self: HandlerWithInput<I, A, E>, options?: MutationOptions<A, I>): readonly [
|
|
106
|
-
Readonly<Ref<MutationResult<A, E>>>,
|
|
107
|
-
(
|
|
108
|
-
i: I,
|
|
109
|
-
signal?: AbortSignal
|
|
110
|
-
) => Promise<Either.Either<A, E>>
|
|
111
|
-
]
|
|
112
|
-
<E, A>(self: Handler<A, E>, options?: MutationOptions<A>): readonly [
|
|
113
|
-
Readonly<Ref<MutationResult<A, E>>>,
|
|
114
|
-
(
|
|
115
|
-
signal?: AbortSignal
|
|
116
|
-
) => Promise<Either.Either<A, E>>
|
|
117
|
-
]
|
|
118
|
-
} = <I, E, A>(
|
|
119
|
-
self: {
|
|
120
|
-
handler:
|
|
121
|
-
| HandlerWithInput<I, A, E>["handler"]
|
|
122
|
-
| Handler<A, E>["handler"]
|
|
92
|
+
export const makeMutation = <R>(runtime: Runtime.Runtime<R>) => {
|
|
93
|
+
type HandlerWithInput<I, A, E> = {
|
|
94
|
+
handler: (i: I) => Effect<A, E, R>
|
|
123
95
|
name: string
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
options?:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
96
|
+
}
|
|
97
|
+
type Handler<A, E> = { handler: Effect<A, E, R>; name: string }
|
|
98
|
+
|
|
99
|
+
const runPromise = Runtime.runPromise(runtime)
|
|
100
|
+
/**
|
|
101
|
+
* Pass a function that returns an Effect, e.g from a client action, or an Effect
|
|
102
|
+
* Returns a tuple with state ref and execution function which reports errors as Toast.
|
|
103
|
+
*/
|
|
104
|
+
const useSafeMutation: {
|
|
105
|
+
<I, E, A>(self: HandlerWithInput<I, A, E>, options?: MutationOptions<A, I>): readonly [
|
|
106
|
+
Readonly<Ref<MutationResult<A, E>>>,
|
|
107
|
+
(
|
|
108
|
+
i: I,
|
|
109
|
+
signal?: AbortSignal
|
|
110
|
+
) => Promise<Either.Either<A, E>>
|
|
111
|
+
]
|
|
112
|
+
<E, A>(self: Handler<A, E>, options?: MutationOptions<A>): readonly [
|
|
113
|
+
Readonly<Ref<MutationResult<A, E>>>,
|
|
114
|
+
(
|
|
115
|
+
signal?: AbortSignal
|
|
116
|
+
) => Promise<Either.Either<A, E>>
|
|
117
|
+
]
|
|
118
|
+
} = <I, E, A>(
|
|
119
|
+
self: {
|
|
120
|
+
handler:
|
|
121
|
+
| HandlerWithInput<I, A, E>["handler"]
|
|
122
|
+
| Handler<A, E>["handler"]
|
|
123
|
+
name: string
|
|
124
|
+
},
|
|
125
|
+
options?: MutationOptions<A>
|
|
126
|
+
) => {
|
|
127
|
+
const queryClient = useQueryClient()
|
|
128
|
+
const state: Ref<MutationResult<A, E>> = ref<MutationResult<A, E>>({ _tag: "Initial" }) as any
|
|
129
|
+
const onSuccess = options?.onSuccess
|
|
130
|
+
|
|
131
|
+
const invalidateQueries = (
|
|
132
|
+
filters?: MaybeRefDeep<InvalidateQueryFilters>,
|
|
133
|
+
options?: MaybeRefDeep<InvalidateOptions>
|
|
134
|
+
) => Effect.promise(() => queryClient.invalidateQueries(filters, options))
|
|
135
|
+
|
|
136
|
+
function handleExit(exit: Exit.Exit<A, E>): Effect<Either.Either<A, E>, never, never> {
|
|
137
|
+
return Effect.sync(() => {
|
|
138
|
+
if (Exit.isSuccess(exit)) {
|
|
139
|
+
state.value = { _tag: "Success", data: exit.value }
|
|
140
|
+
return Either.right(exit.value)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const err = Cause.failureOption(exit.cause)
|
|
144
|
+
if (Option.isSome(err)) {
|
|
145
|
+
state.value = { _tag: "Error", error: err.value }
|
|
146
|
+
return Either.left(err.value)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const died = Cause.dieOption(exit.cause)
|
|
150
|
+
if (Option.isSome(died)) {
|
|
151
|
+
throw died.value
|
|
152
|
+
}
|
|
153
|
+
const interrupted = Cause.interruptOption(exit.cause)
|
|
154
|
+
if (Option.isSome(interrupted)) {
|
|
155
|
+
throw new InterruptedException()
|
|
156
|
+
}
|
|
157
|
+
throw new Error("Invalid state")
|
|
158
|
+
})
|
|
159
|
+
}
|
|
142
160
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
const exec = (fst?: I | AbortSignal, snd?: AbortSignal) => {
|
|
162
|
+
let effect: Effect<A, E, R>
|
|
163
|
+
let signal: AbortSignal | undefined
|
|
164
|
+
if (Effect.isEffect(self.handler)) {
|
|
165
|
+
effect = self.handler as any
|
|
166
|
+
signal = fst as AbortSignal | undefined
|
|
167
|
+
} else {
|
|
168
|
+
effect = self.handler(fst as I)
|
|
169
|
+
signal = snd
|
|
147
170
|
}
|
|
148
171
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
return runPromise(
|
|
173
|
+
Effect
|
|
174
|
+
.sync(() => {
|
|
175
|
+
state.value = { _tag: "Loading" }
|
|
176
|
+
})
|
|
177
|
+
.pipe(
|
|
178
|
+
Effect.andThen(effect),
|
|
179
|
+
Effect.tap(() =>
|
|
180
|
+
Effect
|
|
181
|
+
.suspend(() => {
|
|
182
|
+
const queryKey = getQueryKey(self.name)
|
|
183
|
+
|
|
184
|
+
if (options?.queryInvalidation) {
|
|
185
|
+
const opts = options.queryInvalidation(queryKey, self.name)
|
|
186
|
+
if (!opts.length) {
|
|
187
|
+
return Effect.void
|
|
188
|
+
}
|
|
189
|
+
return Effect
|
|
190
|
+
.andThen(
|
|
191
|
+
Effect.annotateCurrentSpan({ queryKey, opts }),
|
|
192
|
+
Effect.forEach(opts, (_) => invalidateQueries(_.filters, _.options), { concurrency: "inherit" })
|
|
193
|
+
)
|
|
194
|
+
.pipe(Effect.withSpan("client.query.invalidation", { captureStackTrace: false }))
|
|
195
|
+
}
|
|
160
196
|
|
|
161
|
-
|
|
162
|
-
let effect: Effect<A, E, ApiConfig | HttpClient.HttpClient>
|
|
163
|
-
let signal: AbortSignal | undefined
|
|
164
|
-
if (Effect.isEffect(self.handler)) {
|
|
165
|
-
effect = self.handler as any
|
|
166
|
-
signal = fst as AbortSignal | undefined
|
|
167
|
-
} else {
|
|
168
|
-
effect = self.handler(fst as I)
|
|
169
|
-
signal = snd
|
|
170
|
-
}
|
|
197
|
+
if (!queryKey) return Effect.void
|
|
171
198
|
|
|
172
|
-
return run.value(
|
|
173
|
-
Effect
|
|
174
|
-
.sync(() => {
|
|
175
|
-
state.value = { _tag: "Loading" }
|
|
176
|
-
})
|
|
177
|
-
.pipe(
|
|
178
|
-
Effect.andThen(effect),
|
|
179
|
-
Effect.tap(() =>
|
|
180
|
-
Effect
|
|
181
|
-
.suspend(() => {
|
|
182
|
-
const queryKey = getQueryKey(self.name)
|
|
183
|
-
|
|
184
|
-
if (options?.queryInvalidation) {
|
|
185
|
-
const opts = options.queryInvalidation(queryKey, self.name)
|
|
186
|
-
if (!opts.length) {
|
|
187
|
-
return Effect.void
|
|
188
|
-
}
|
|
189
199
|
return Effect
|
|
190
200
|
.andThen(
|
|
191
|
-
Effect.annotateCurrentSpan({ queryKey
|
|
192
|
-
|
|
201
|
+
Effect.annotateCurrentSpan({ queryKey }),
|
|
202
|
+
invalidateQueries({ queryKey })
|
|
193
203
|
)
|
|
194
204
|
.pipe(Effect.withSpan("client.query.invalidation", { captureStackTrace: false }))
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
invalidateQueries({ queryKey })
|
|
203
|
-
)
|
|
204
|
-
.pipe(Effect.withSpan("client.query.invalidation", { captureStackTrace: false }))
|
|
205
|
-
})
|
|
205
|
+
})
|
|
206
|
+
),
|
|
207
|
+
Effect.tapDefect(reportRuntimeError),
|
|
208
|
+
Effect.tap((i) => onSuccess ? Effect.promise(() => onSuccess(i)) : Effect.void),
|
|
209
|
+
Effect.exit,
|
|
210
|
+
Effect.flatMap(handleExit),
|
|
211
|
+
Effect.withSpan(`mutation ${self.name}`, { captureStackTrace: false })
|
|
206
212
|
),
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
dropUndefinedT({ signal })
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return tuple(
|
|
218
|
+
state,
|
|
219
|
+
exec
|
|
214
220
|
)
|
|
215
221
|
}
|
|
216
|
-
|
|
217
|
-
return tuple(
|
|
218
|
-
state,
|
|
219
|
-
exec
|
|
220
|
-
)
|
|
222
|
+
return useSafeMutation
|
|
221
223
|
}
|
|
224
|
+
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
226
|
+
export interface MakeMutation<R> extends ReturnType<typeof makeMutation<R>> {}
|
package/src/query.ts
CHANGED
|
@@ -13,11 +13,10 @@ import type {
|
|
|
13
13
|
} from "@tanstack/vue-query"
|
|
14
14
|
import { useQuery } from "@tanstack/vue-query"
|
|
15
15
|
import { Cause, Effect, Option, Runtime, S } from "effect-app"
|
|
16
|
-
import {
|
|
17
|
-
import type { HttpClient } from "effect-app/http"
|
|
16
|
+
import { ServiceUnavailableError } from "effect-app/client"
|
|
18
17
|
import { computed, ref } from "vue"
|
|
19
18
|
import type { ComputedRef, WatchSource } from "vue"
|
|
20
|
-
import { makeQueryKey, reportRuntimeError
|
|
19
|
+
import { makeQueryKey, reportRuntimeError } from "./internal.js"
|
|
21
20
|
|
|
22
21
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
23
22
|
export interface QueryObserverOptionsCustom<
|
|
@@ -31,190 +30,196 @@ export interface QueryObserverOptionsCustom<
|
|
|
31
30
|
Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TPageParam>, "queryKey" | "queryFn">
|
|
32
31
|
{}
|
|
33
32
|
|
|
34
|
-
export function useSafeQuery<E, A>(
|
|
35
|
-
self: {
|
|
36
|
-
handler: Effect<A, E, ApiConfig | HttpClient.HttpClient>
|
|
37
|
-
mapPath: string
|
|
38
|
-
name: string
|
|
39
|
-
},
|
|
40
|
-
options?: QueryObserverOptionsCustom // TODO
|
|
41
|
-
): readonly [
|
|
42
|
-
ComputedRef<Result.Result<A, E>>,
|
|
43
|
-
ComputedRef<A | undefined>,
|
|
44
|
-
(options?: RefetchOptions) => Promise<QueryObserverResult<any, any>>,
|
|
45
|
-
UseQueryReturnType<any, any>
|
|
46
|
-
]
|
|
47
|
-
export function useSafeQuery<Arg, E, A>(
|
|
48
|
-
self: {
|
|
49
|
-
handler: (arg: Arg) => Effect<A, E, ApiConfig | HttpClient.HttpClient>
|
|
50
|
-
mapPath: (arg: Arg) => string
|
|
51
|
-
name: string
|
|
52
|
-
},
|
|
53
|
-
arg: Arg | WatchSource<Arg>,
|
|
54
|
-
options?: QueryObserverOptionsCustom // TODO
|
|
55
|
-
): readonly [
|
|
56
|
-
ComputedRef<Result.Result<A, E>>,
|
|
57
|
-
ComputedRef<A | undefined>,
|
|
58
|
-
(options?: RefetchOptions) => Promise<QueryObserverResult<any, any>>,
|
|
59
|
-
UseQueryReturnType<any, any>
|
|
60
|
-
]
|
|
61
|
-
export function useSafeQuery(
|
|
62
|
-
self: any,
|
|
63
|
-
/*
|
|
64
|
-
q:
|
|
65
|
-
| {
|
|
66
|
-
handler: (
|
|
67
|
-
req: I
|
|
68
|
-
) => Effect<
|
|
69
|
-
A,
|
|
70
|
-
E,
|
|
71
|
-
ApiConfig | HttpClient.HttpClient
|
|
72
|
-
>
|
|
73
|
-
mapPath: (req: I) => string
|
|
74
|
-
name: string
|
|
75
|
-
}
|
|
76
|
-
| {
|
|
77
|
-
handler: Effect<
|
|
78
|
-
A,
|
|
79
|
-
E,
|
|
80
|
-
ApiConfig | HttpClient.HttpClient
|
|
81
|
-
>
|
|
82
|
-
mapPath: string
|
|
83
|
-
name: string
|
|
84
|
-
},
|
|
85
|
-
*/
|
|
86
|
-
argOrOptions?: any,
|
|
87
|
-
options?: any
|
|
88
|
-
) {
|
|
89
|
-
return Effect.isEffect(self.handler)
|
|
90
|
-
? useSafeQuery_(self, undefined, argOrOptions)
|
|
91
|
-
: useSafeQuery_(self, argOrOptions, options)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
33
|
export interface KnownFiberFailure<E> extends Runtime.FiberFailure {
|
|
95
34
|
readonly [Runtime.FiberFailureCauseId]: Cause.Cause<E>
|
|
96
35
|
}
|
|
97
36
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
// declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options:
|
|
102
|
-
|
|
37
|
+
export const makeQuery = <R>(runtime: Runtime.Runtime<R>) => {
|
|
38
|
+
const runPromise = Runtime.runPromise(runtime)
|
|
39
|
+
// TODO: options
|
|
40
|
+
// declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): UseQueryReturnType<TData, TError>;
|
|
41
|
+
// declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): UseQueryDefinedReturnType<TData, TError>;
|
|
42
|
+
// declare function useQuery<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: UseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, queryClient?: QueryClient): UseQueryReturnType<TData, TError>;
|
|
43
|
+
const useSafeQuery_ = <I, A, E>(
|
|
44
|
+
q:
|
|
45
|
+
| {
|
|
46
|
+
readonly handler: (
|
|
47
|
+
req: I
|
|
48
|
+
) => Effect<
|
|
49
|
+
A,
|
|
50
|
+
E,
|
|
51
|
+
R
|
|
52
|
+
>
|
|
53
|
+
mapPath: (req: I) => string
|
|
54
|
+
name: string
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
readonly handler: Effect<
|
|
58
|
+
A,
|
|
59
|
+
E,
|
|
60
|
+
R
|
|
61
|
+
>
|
|
62
|
+
mapPath: string
|
|
63
|
+
name: string
|
|
64
|
+
},
|
|
65
|
+
arg?: I | WatchSource<I>,
|
|
66
|
+
options: QueryObserverOptionsCustom<unknown, KnownFiberFailure<E>, A> = {} // TODO
|
|
67
|
+
) => {
|
|
68
|
+
const arr = arg
|
|
69
|
+
const req: { value: I } = !arg
|
|
70
|
+
? undefined
|
|
71
|
+
: typeof arr === "function"
|
|
72
|
+
? ({
|
|
73
|
+
get value() {
|
|
74
|
+
return (arr as any)()
|
|
75
|
+
}
|
|
76
|
+
} as any)
|
|
77
|
+
: ref(arg)
|
|
78
|
+
const queryKey = makeQueryKey(q.name)
|
|
79
|
+
const handler = q.handler
|
|
80
|
+
const r = useQuery<unknown, KnownFiberFailure<E>, A>(
|
|
81
|
+
Effect.isEffect(handler)
|
|
82
|
+
? {
|
|
83
|
+
...options,
|
|
84
|
+
retry: (retryCount, error) => {
|
|
85
|
+
if (Runtime.isFiberFailure(error)) {
|
|
86
|
+
const cause = error[Runtime.FiberFailureCauseId]
|
|
87
|
+
const sq = Cause.squash(cause)
|
|
88
|
+
if (!isHttpRequestError(sq) && !isHttpResponseError(sq) && !S.is(ServiceUnavailableError)(sq)) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return retryCount < 5
|
|
94
|
+
},
|
|
95
|
+
queryKey,
|
|
96
|
+
queryFn: ({ signal }) =>
|
|
97
|
+
runPromise(
|
|
98
|
+
handler
|
|
99
|
+
.pipe(
|
|
100
|
+
Effect.tapDefect(reportRuntimeError),
|
|
101
|
+
Effect.withSpan(`query ${q.name}`, { captureStackTrace: false })
|
|
102
|
+
),
|
|
103
|
+
{ signal }
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
...options,
|
|
108
|
+
retry: (retryCount, error) => {
|
|
109
|
+
if (Runtime.isFiberFailure(error)) {
|
|
110
|
+
const cause = error[Runtime.FiberFailureCauseId]
|
|
111
|
+
const sq = Cause.squash(cause)
|
|
112
|
+
if (!isHttpRequestError(sq) && !isHttpResponseError(sq) && !S.is(ServiceUnavailableError)(sq)) {
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return retryCount < 5
|
|
118
|
+
},
|
|
119
|
+
queryKey: [...queryKey, req],
|
|
120
|
+
queryFn: ({ signal }) =>
|
|
121
|
+
runPromise(
|
|
122
|
+
handler(req.value)
|
|
123
|
+
.pipe(
|
|
124
|
+
Effect.tapDefect(reportRuntimeError),
|
|
125
|
+
Effect.withSpan(`query ${q.name}`, { captureStackTrace: false })
|
|
126
|
+
),
|
|
127
|
+
{ signal }
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
const result = computed(() =>
|
|
133
|
+
swrToQuery({
|
|
134
|
+
error: r.error.value ?? undefined,
|
|
135
|
+
data: r.data.value,
|
|
136
|
+
isValidating: r.isFetching.value
|
|
137
|
+
})
|
|
138
|
+
)
|
|
139
|
+
const latestSuccess = computed(() => Option.getOrUndefined(Result.value(result.value)))
|
|
140
|
+
return [result, latestSuccess, r.refetch, r] as const
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function swrToQuery<E, A>(r: {
|
|
144
|
+
error: KnownFiberFailure<E> | undefined
|
|
145
|
+
data: A | undefined
|
|
146
|
+
isValidating: boolean
|
|
147
|
+
}): Result.Result<A, E> {
|
|
148
|
+
if (r.error) {
|
|
149
|
+
return Result.failureWithPrevious(
|
|
150
|
+
r.error[Runtime.FiberFailureCauseId],
|
|
151
|
+
r.data === undefined ? Option.none() : Option.some(Result.success(r.data)),
|
|
152
|
+
r.isValidating
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
if (r.data !== undefined) {
|
|
156
|
+
return Result.success<A, E>(r.data, r.isValidating)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return Result.initial(r.isValidating)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function useSafeQuery<E, A>(
|
|
163
|
+
self: {
|
|
164
|
+
handler: Effect<A, E, R>
|
|
165
|
+
mapPath: string
|
|
166
|
+
name: string
|
|
167
|
+
},
|
|
168
|
+
options?: QueryObserverOptionsCustom // TODO
|
|
169
|
+
): readonly [
|
|
170
|
+
ComputedRef<Result.Result<A, E>>,
|
|
171
|
+
ComputedRef<A | undefined>,
|
|
172
|
+
(options?: RefetchOptions) => Promise<QueryObserverResult<any, any>>,
|
|
173
|
+
UseQueryReturnType<any, any>
|
|
174
|
+
]
|
|
175
|
+
function useSafeQuery<Arg, E, A>(
|
|
176
|
+
self: {
|
|
177
|
+
handler: (arg: Arg) => Effect<A, E, R>
|
|
178
|
+
mapPath: (arg: Arg) => string
|
|
179
|
+
name: string
|
|
180
|
+
},
|
|
181
|
+
arg: Arg | WatchSource<Arg>,
|
|
182
|
+
options?: QueryObserverOptionsCustom // TODO
|
|
183
|
+
): readonly [
|
|
184
|
+
ComputedRef<Result.Result<A, E>>,
|
|
185
|
+
ComputedRef<A | undefined>,
|
|
186
|
+
(options?: RefetchOptions) => Promise<QueryObserverResult<any, any>>,
|
|
187
|
+
UseQueryReturnType<any, any>
|
|
188
|
+
]
|
|
189
|
+
function useSafeQuery(
|
|
190
|
+
self: any,
|
|
191
|
+
/*
|
|
103
192
|
q:
|
|
104
193
|
| {
|
|
105
|
-
|
|
194
|
+
handler: (
|
|
106
195
|
req: I
|
|
107
196
|
) => Effect<
|
|
108
197
|
A,
|
|
109
198
|
E,
|
|
110
|
-
|
|
199
|
+
R
|
|
111
200
|
>
|
|
112
201
|
mapPath: (req: I) => string
|
|
113
202
|
name: string
|
|
114
203
|
}
|
|
115
204
|
| {
|
|
116
|
-
|
|
205
|
+
handler: Effect<
|
|
117
206
|
A,
|
|
118
207
|
E,
|
|
119
|
-
|
|
208
|
+
R
|
|
120
209
|
>
|
|
121
210
|
mapPath: string
|
|
122
211
|
name: string
|
|
123
212
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
? ({
|
|
132
|
-
get value() {
|
|
133
|
-
return (arr as any)()
|
|
134
|
-
}
|
|
135
|
-
} as any)
|
|
136
|
-
: ref(arg)
|
|
137
|
-
const queryKey = makeQueryKey(q.name)
|
|
138
|
-
const handler = q.handler
|
|
139
|
-
const r = useQuery<unknown, KnownFiberFailure<E>, A>(
|
|
140
|
-
Effect.isEffect(handler)
|
|
141
|
-
? {
|
|
142
|
-
...options,
|
|
143
|
-
retry: (retryCount, error) => {
|
|
144
|
-
if (Runtime.isFiberFailure(error)) {
|
|
145
|
-
const cause = error[Runtime.FiberFailureCauseId]
|
|
146
|
-
const sq = Cause.squash(cause)
|
|
147
|
-
if (!isHttpRequestError(sq) && !isHttpResponseError(sq) && !S.is(ServiceUnavailableError)(sq)) {
|
|
148
|
-
return false
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return retryCount < 5
|
|
153
|
-
},
|
|
154
|
-
queryKey,
|
|
155
|
-
queryFn: ({ signal }) =>
|
|
156
|
-
run.value(
|
|
157
|
-
handler
|
|
158
|
-
.pipe(
|
|
159
|
-
Effect.tapDefect(reportRuntimeError),
|
|
160
|
-
Effect.withSpan(`query ${q.name}`, { captureStackTrace: false })
|
|
161
|
-
),
|
|
162
|
-
{ signal }
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
: {
|
|
166
|
-
...options,
|
|
167
|
-
retry: (retryCount, error) => {
|
|
168
|
-
if (Runtime.isFiberFailure(error)) {
|
|
169
|
-
const cause = error[Runtime.FiberFailureCauseId]
|
|
170
|
-
const sq = Cause.squash(cause)
|
|
171
|
-
if (!isHttpRequestError(sq) && !isHttpResponseError(sq) && !S.is(ServiceUnavailableError)(sq)) {
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return retryCount < 5
|
|
177
|
-
},
|
|
178
|
-
queryKey: [...queryKey, req],
|
|
179
|
-
queryFn: ({ signal }) =>
|
|
180
|
-
run
|
|
181
|
-
.value(
|
|
182
|
-
handler(req.value)
|
|
183
|
-
.pipe(
|
|
184
|
-
Effect.tapDefect(reportRuntimeError),
|
|
185
|
-
Effect.withSpan(`query ${q.name}`, { captureStackTrace: false })
|
|
186
|
-
),
|
|
187
|
-
{ signal }
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
const result = computed(() =>
|
|
193
|
-
swrToQuery({
|
|
194
|
-
error: r.error.value ?? undefined,
|
|
195
|
-
data: r.data.value,
|
|
196
|
-
isValidating: r.isFetching.value
|
|
197
|
-
})
|
|
198
|
-
)
|
|
199
|
-
const latestSuccess = computed(() => Option.getOrUndefined(Result.value(result.value)))
|
|
200
|
-
return [result, latestSuccess, r.refetch, r] as const
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function swrToQuery<E, A>(r: {
|
|
204
|
-
error: KnownFiberFailure<E> | undefined
|
|
205
|
-
data: A | undefined
|
|
206
|
-
isValidating: boolean
|
|
207
|
-
}): Result.Result<A, E> {
|
|
208
|
-
if (r.error) {
|
|
209
|
-
return Result.failureWithPrevious(
|
|
210
|
-
r.error[Runtime.FiberFailureCauseId],
|
|
211
|
-
r.data === undefined ? Option.none() : Option.some(Result.success(r.data)),
|
|
212
|
-
r.isValidating
|
|
213
|
-
)
|
|
214
|
-
}
|
|
215
|
-
if (r.data !== undefined) {
|
|
216
|
-
return Result.success<A, E>(r.data, r.isValidating)
|
|
213
|
+
*/
|
|
214
|
+
argOrOptions?: any,
|
|
215
|
+
options?: any
|
|
216
|
+
) {
|
|
217
|
+
return Effect.isEffect(self.handler)
|
|
218
|
+
? useSafeQuery_(self, undefined, argOrOptions)
|
|
219
|
+
: useSafeQuery_(self, argOrOptions, options)
|
|
217
220
|
}
|
|
218
|
-
|
|
219
|
-
return Result.initial(r.isValidating)
|
|
221
|
+
return useSafeQuery
|
|
220
222
|
}
|
|
223
|
+
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
225
|
+
export interface MakeQuery<R> extends ReturnType<typeof makeQuery<R>> {}
|
package/src/runtime.ts
CHANGED
|
@@ -4,9 +4,6 @@ import { Config, Exit, Runtime } from "effect"
|
|
|
4
4
|
import { Effect, Layer, Logger } from "effect-app"
|
|
5
5
|
import { ApiConfig } from "effect-app/client"
|
|
6
6
|
import * as Scope from "effect/Scope"
|
|
7
|
-
import { initRuntime } from "./internal.js"
|
|
8
|
-
|
|
9
|
-
export { initRuntime } from "./internal.js"
|
|
10
7
|
|
|
11
8
|
export const DefaultApiConfig = Config.all({
|
|
12
9
|
apiUrl: Config.string("apiUrl").pipe(Config.withDefault("/api")),
|
|
@@ -48,7 +45,6 @@ export function makeAppRuntime<R, E, A>(layer: Layer<A, E, R>) {
|
|
|
48
45
|
|
|
49
46
|
export function initializeSync<E, A>(layer: Layer<A | ApiConfig | HttpClient.HttpClient, E, never>) {
|
|
50
47
|
const { clean, runtime } = Effect.runSync(makeAppRuntime(layer))
|
|
51
|
-
initRuntime(runtime)
|
|
52
48
|
return {
|
|
53
49
|
runtime,
|
|
54
50
|
clean: () => Effect.runSync(clean)
|
|
@@ -59,7 +55,6 @@ export function initializeAsync<E, A>(layer: Layer<A | ApiConfig | HttpClient.Ht
|
|
|
59
55
|
return Effect
|
|
60
56
|
.runPromise(makeAppRuntime(layer))
|
|
61
57
|
.then(({ clean, runtime }) => {
|
|
62
|
-
initRuntime(runtime)
|
|
63
58
|
return {
|
|
64
59
|
runtime,
|
|
65
60
|
clean: () => Effect.runPromise(clean)
|