@effect-app/vue 2.14.0 → 2.15.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 +15 -0
- package/_cjs/index.cjs +0 -22
- package/_cjs/index.cjs.map +1 -1
- package/_cjs/makeClient.cjs +169 -72
- package/_cjs/makeClient.cjs.map +1 -1
- package/_cjs/mutate.cjs +84 -101
- package/_cjs/mutate.cjs.map +1 -1
- package/_cjs/query.cjs +9 -52
- package/_cjs/query.cjs.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/makeClient.d.ts +87 -70
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +164 -84
- package/dist/mutate.d.ts +18 -26
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +67 -84
- package/dist/query.d.ts +10 -24
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +13 -55
- package/package.json +8 -48
- package/src/index.ts +0 -2
- package/src/makeClient.ts +463 -227
- package/src/mutate.ts +111 -141
- package/src/query.ts +30 -116
- package/_cjs/hooks.cjs +0 -28
- package/_cjs/hooks.cjs.map +0 -1
- package/_cjs/makeClient2.cjs +0 -221
- package/_cjs/makeClient2.cjs.map +0 -1
- package/_cjs/mutate2.cjs +0 -118
- package/_cjs/mutate2.cjs.map +0 -1
- package/_cjs/query2.cjs +0 -105
- package/_cjs/query2.cjs.map +0 -1
- package/dist/hooks.d.ts +0 -3
- package/dist/hooks.d.ts.map +0 -1
- package/dist/hooks.js +0 -4
- package/dist/makeClient2.d.ts +0 -74
- package/dist/makeClient2.d.ts.map +0 -1
- package/dist/makeClient2.js +0 -187
- package/dist/mutate2.d.ts +0 -42
- package/dist/mutate2.d.ts.map +0 -1
- package/dist/mutate2.js +0 -88
- package/dist/query2.d.ts +0 -23
- package/dist/query2.d.ts.map +0 -1
- package/dist/query2.js +0 -97
- package/src/hooks.ts +0 -4
- package/src/makeClient2.ts +0 -353
- package/src/mutate2.ts +0 -197
- package/src/query2.ts +0 -205
package/src/makeClient2.ts
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import * as Sentry from "@sentry/browser"
|
|
3
|
-
import { Cause, Effect, Exit, Match, Option, Runtime, S, Struct } from "effect-app"
|
|
4
|
-
import type { RequestHandler, RequestHandlerWithInput, TaggedRequestClassAny } from "effect-app/client/clientFor"
|
|
5
|
-
import { flow, pipe, tuple } from "effect-app/Function"
|
|
6
|
-
import { OperationSuccess } from "effect-app/Operations"
|
|
7
|
-
import type { Schema } from "effect-app/Schema"
|
|
8
|
-
import { dropUndefinedT } from "effect-app/utils"
|
|
9
|
-
import type { ComputedRef, Ref, ShallowRef } from "vue"
|
|
10
|
-
import { computed, ref, watch } from "vue"
|
|
11
|
-
import { buildFieldInfoFromFieldsRoot } from "./form.js"
|
|
12
|
-
import { getRuntime } from "./lib.js"
|
|
13
|
-
import type { Opts, ResponseErrors } from "./makeClient.js"
|
|
14
|
-
import type { MakeIntlReturn } from "./makeIntl.js"
|
|
15
|
-
import { mutationResultToVue } from "./mutate.js"
|
|
16
|
-
import type { Res } from "./mutate.js"
|
|
17
|
-
import { makeMutation2 } from "./mutate2.js"
|
|
18
|
-
import { makeQuery2 } from "./query2.js"
|
|
19
|
-
|
|
20
|
-
type WithAction<A> = A & {
|
|
21
|
-
action: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// computed() takes a getter function and returns a readonly reactive ref
|
|
25
|
-
// object for the returned value from the getter.
|
|
26
|
-
type Resp<I, A, E, R> = readonly [
|
|
27
|
-
ComputedRef<Res<A, E>>,
|
|
28
|
-
WithAction<(I: I) => Effect<Exit<A, E>, never, R>>
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
type ActResp<A, E, R> = readonly [
|
|
32
|
-
ComputedRef<Res<A, E>>,
|
|
33
|
-
WithAction<Effect<Exit<A, E>, never, R>>
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
export const makeClient2 = <Locale extends string, R>(
|
|
37
|
-
useIntl: MakeIntlReturn<Locale>["useIntl"],
|
|
38
|
-
useToast: () => {
|
|
39
|
-
error: (message: string) => void
|
|
40
|
-
warning: (message: string) => void
|
|
41
|
-
success: (message: string) => void
|
|
42
|
-
},
|
|
43
|
-
runtime: ShallowRef<Runtime.Runtime<R> | undefined>,
|
|
44
|
-
messages: Record<string, string | undefined> = {}
|
|
45
|
-
) => {
|
|
46
|
-
const useSafeMutation = makeMutation2()
|
|
47
|
-
const useSafeQuery = makeQuery2(runtime)
|
|
48
|
-
const useHandleRequestWithToast = () => {
|
|
49
|
-
const toast = useToast()
|
|
50
|
-
const { intl } = useIntl()
|
|
51
|
-
|
|
52
|
-
return handleRequestWithToast
|
|
53
|
-
/**
|
|
54
|
-
* Pass a function that returns a Promise.
|
|
55
|
-
* Returns an execution function which reports errors as Toast.
|
|
56
|
-
*/
|
|
57
|
-
function handleRequestWithToast<
|
|
58
|
-
E extends ResponseErrors,
|
|
59
|
-
A,
|
|
60
|
-
R,
|
|
61
|
-
Args extends unknown[]
|
|
62
|
-
>(
|
|
63
|
-
f: Effect<A, E, R> | ((...args: Args) => Effect<A, E, R>),
|
|
64
|
-
action: string,
|
|
65
|
-
options: Opts<A> = { suppressErrorToast: false }
|
|
66
|
-
) {
|
|
67
|
-
const message = messages[action] ?? action
|
|
68
|
-
const warnMessage = intl.value.formatMessage(
|
|
69
|
-
{ id: "handle.with_warnings" },
|
|
70
|
-
{ action: message }
|
|
71
|
-
)
|
|
72
|
-
const successMessage = intl.value.formatMessage(
|
|
73
|
-
{ id: "handle.success" },
|
|
74
|
-
{ action: message }
|
|
75
|
-
)
|
|
76
|
-
const errorMessage = intl.value.formatMessage(
|
|
77
|
-
{ id: "handle.with_errors" },
|
|
78
|
-
{ action: message }
|
|
79
|
-
)
|
|
80
|
-
const handleEffect = (self: Effect<A, E, R>) =>
|
|
81
|
-
self.pipe(
|
|
82
|
-
Effect.exit,
|
|
83
|
-
Effect.tap(
|
|
84
|
-
Exit.matchEffect({
|
|
85
|
-
onSuccess: (r) =>
|
|
86
|
-
Effect.gen(function*() {
|
|
87
|
-
if (options.suppressSuccessToast) {
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
toast.success(
|
|
91
|
-
successMessage
|
|
92
|
-
+ (S.is(OperationSuccess)(r) && r.message
|
|
93
|
-
? "\n" + r.message
|
|
94
|
-
: "")
|
|
95
|
-
)
|
|
96
|
-
}),
|
|
97
|
-
onFailure: (err) =>
|
|
98
|
-
Effect.gen(function*() {
|
|
99
|
-
if (Cause.isInterruptedOnly(err)) {
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const fail = Cause.failureOption(err)
|
|
104
|
-
if (Option.isSome(fail)) {
|
|
105
|
-
if (fail.value._tag === "SuppressErrors") {
|
|
106
|
-
return Effect.succeed(void 0)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (fail.value._tag === "OperationFailure") {
|
|
110
|
-
toast.warning(
|
|
111
|
-
warnMessage + fail.value.message
|
|
112
|
-
? "\n" + fail.value.message
|
|
113
|
-
: ""
|
|
114
|
-
)
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!options.suppressErrorToast) {
|
|
119
|
-
toast.error(`${errorMessage}:\n` + renderError(fail.value))
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
console.warn(fail, fail.toString())
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const extra = {
|
|
127
|
-
action,
|
|
128
|
-
message: `Unexpected Error trying to ${action}`
|
|
129
|
-
}
|
|
130
|
-
Sentry.captureException(err, { extra })
|
|
131
|
-
console.error(err, extra)
|
|
132
|
-
|
|
133
|
-
toast.error(
|
|
134
|
-
intl.value.formatMessage(
|
|
135
|
-
{ id: "handle.unexpected_error" },
|
|
136
|
-
{
|
|
137
|
-
action: message,
|
|
138
|
-
error: JSON.stringify(err, undefined, 2)
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
)
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
return Object.assign(
|
|
147
|
-
Effect.isEffect(f)
|
|
148
|
-
? pipe(
|
|
149
|
-
f,
|
|
150
|
-
handleEffect
|
|
151
|
-
)
|
|
152
|
-
: flow(
|
|
153
|
-
f,
|
|
154
|
-
handleEffect
|
|
155
|
-
),
|
|
156
|
-
{ action }
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function renderError(e: ResponseErrors): string {
|
|
161
|
-
return Match.value(e).pipe(
|
|
162
|
-
Match.tags({
|
|
163
|
-
// HttpErrorRequest: e =>
|
|
164
|
-
// intl.value.formatMessage(
|
|
165
|
-
// { id: "handle.request_error" },
|
|
166
|
-
// { error: `${e.error}` },
|
|
167
|
-
// ),
|
|
168
|
-
// HttpErrorResponse: e =>
|
|
169
|
-
// e.response.status >= 500 ||
|
|
170
|
-
// e.response.body._tag !== "Some" ||
|
|
171
|
-
// !e.response.body.value
|
|
172
|
-
// ? intl.value.formatMessage(
|
|
173
|
-
// { id: "handle.error_response" },
|
|
174
|
-
// {
|
|
175
|
-
// error: `${
|
|
176
|
-
// e.response.body._tag === "Some" && e.response.body.value
|
|
177
|
-
// ? parseError(e.response.body.value)
|
|
178
|
-
// : "Unknown"
|
|
179
|
-
// } (${e.response.status})`,
|
|
180
|
-
// },
|
|
181
|
-
// )
|
|
182
|
-
// : intl.value.formatMessage(
|
|
183
|
-
// { id: "handle.unexpected_error" },
|
|
184
|
-
// {
|
|
185
|
-
// error:
|
|
186
|
-
// JSON.stringify(e.response.body, undefined, 2) +
|
|
187
|
-
// "( " +
|
|
188
|
-
// e.response.status +
|
|
189
|
-
// ")",
|
|
190
|
-
// },
|
|
191
|
-
// ),
|
|
192
|
-
// ResponseError: e =>
|
|
193
|
-
// intl.value.formatMessage(
|
|
194
|
-
// { id: "handle.response_error" },
|
|
195
|
-
// { error: `${e.error}` },
|
|
196
|
-
// ),
|
|
197
|
-
ParseError: (e) => {
|
|
198
|
-
console.warn(e.toString())
|
|
199
|
-
return intl.value.formatMessage({ id: "validation.failed" })
|
|
200
|
-
}
|
|
201
|
-
}),
|
|
202
|
-
Match.orElse((e) =>
|
|
203
|
-
intl.value.formatMessage(
|
|
204
|
-
{ id: "handle.unexpected_error" },
|
|
205
|
-
{
|
|
206
|
-
error: `${e.message ?? e._tag ?? e}`
|
|
207
|
-
}
|
|
208
|
-
)
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name, and optionally pass an onOperationSuccess callback.
|
|
216
|
-
* Returns a tuple with state ref and execution function which reports errors as Toast.
|
|
217
|
-
*/
|
|
218
|
-
const useAndHandleMutation: {
|
|
219
|
-
<I, E extends ResponseErrors, A, R, Request extends TaggedRequestClassAny>(
|
|
220
|
-
self: RequestHandlerWithInput<I, A, E, R, Request>,
|
|
221
|
-
action: string,
|
|
222
|
-
options?: Opts<A>
|
|
223
|
-
): Resp<I, void, never, R>
|
|
224
|
-
<E extends ResponseErrors, A, R, Request extends TaggedRequestClassAny>(
|
|
225
|
-
self: RequestHandler<A, E, R, Request>,
|
|
226
|
-
action: string,
|
|
227
|
-
options?: Opts<A>
|
|
228
|
-
): ActResp<void, never, R>
|
|
229
|
-
} = (self: any, action: any, options?: Opts<any>): any => {
|
|
230
|
-
const handleRequestWithToast = useHandleRequestWithToast()
|
|
231
|
-
const [a, b] = useSafeMutation(
|
|
232
|
-
{
|
|
233
|
-
...self,
|
|
234
|
-
handler: Effect.isEffect(self.handler)
|
|
235
|
-
? (pipe(
|
|
236
|
-
Effect.annotateCurrentSpan({ action }),
|
|
237
|
-
Effect.andThen(self.handler)
|
|
238
|
-
) as any)
|
|
239
|
-
: (...args: any[]) =>
|
|
240
|
-
pipe(
|
|
241
|
-
Effect.annotateCurrentSpan({ action }),
|
|
242
|
-
Effect.andThen(self.handler(...args))
|
|
243
|
-
)
|
|
244
|
-
},
|
|
245
|
-
options ? dropUndefinedT(options) : undefined
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
return tuple(
|
|
249
|
-
computed(() => mutationResultToVue(a.value)),
|
|
250
|
-
handleRequestWithToast(b as any, action, options)
|
|
251
|
-
)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function makeUseAndHandleMutation(
|
|
255
|
-
defaultOptions?: Opts<any>
|
|
256
|
-
) {
|
|
257
|
-
return ((self: any, action: any, options: any) => {
|
|
258
|
-
return useAndHandleMutation(
|
|
259
|
-
self,
|
|
260
|
-
action,
|
|
261
|
-
{ ...defaultOptions, ...options }
|
|
262
|
-
)
|
|
263
|
-
}) as unknown as {
|
|
264
|
-
<I, E extends ResponseErrors, A, R, Request extends TaggedRequestClassAny>(
|
|
265
|
-
self: RequestHandlerWithInput<I, A, E, R, Request>,
|
|
266
|
-
action: string,
|
|
267
|
-
options?: Opts<A>
|
|
268
|
-
): Resp<I, A, E, R>
|
|
269
|
-
<E extends ResponseErrors, A, Request extends TaggedRequestClassAny>(
|
|
270
|
-
self: RequestHandler<A, E, R, Request>,
|
|
271
|
-
action: string,
|
|
272
|
-
options?: Opts<A>
|
|
273
|
-
): ActResp<A, E, R>
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const useSafeMutationWithState = <I, E, A, Request extends TaggedRequestClassAny>(
|
|
278
|
-
self: RequestHandlerWithInput<I, A, E, R, Request>
|
|
279
|
-
) => {
|
|
280
|
-
const [a, b] = useSafeMutation(self)
|
|
281
|
-
|
|
282
|
-
return tuple(
|
|
283
|
-
computed(() => mutationResultToVue(a.value)),
|
|
284
|
-
b
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const buildFormFromSchema = <
|
|
289
|
-
From extends Record<PropertyKey, any>,
|
|
290
|
-
To extends Record<PropertyKey, any>,
|
|
291
|
-
C extends Record<PropertyKey, any>,
|
|
292
|
-
OnSubmitA
|
|
293
|
-
>(
|
|
294
|
-
s:
|
|
295
|
-
& Schema<
|
|
296
|
-
To,
|
|
297
|
-
From,
|
|
298
|
-
R
|
|
299
|
-
>
|
|
300
|
-
& { new(c: C): any; fields: S.Struct.Fields },
|
|
301
|
-
state: Ref<Omit<From, "_tag">>,
|
|
302
|
-
onSubmit: (a: To) => Effect<OnSubmitA, never, R>
|
|
303
|
-
) => {
|
|
304
|
-
const fields = buildFieldInfoFromFieldsRoot(s).fields
|
|
305
|
-
const schema = S.Struct(Struct.omit(s.fields, "_tag")) as any
|
|
306
|
-
const parse = S.decodeUnknown<any, any, R>(schema)
|
|
307
|
-
const isDirty = ref(false)
|
|
308
|
-
const isValid = ref(true)
|
|
309
|
-
const runPromise = Runtime.runPromise(getRuntime(runtime))
|
|
310
|
-
|
|
311
|
-
const submit1 =
|
|
312
|
-
(onSubmit: (a: To) => Effect<OnSubmitA, never, R>) => async <T extends Promise<{ valid: boolean }>>(e: T) => {
|
|
313
|
-
const r = await e
|
|
314
|
-
if (!r.valid) return
|
|
315
|
-
return runPromise(onSubmit(new s(await runPromise(parse(state.value)))))
|
|
316
|
-
}
|
|
317
|
-
const submit = submit1(onSubmit)
|
|
318
|
-
|
|
319
|
-
watch(
|
|
320
|
-
state,
|
|
321
|
-
(v) => {
|
|
322
|
-
// TODO: do better
|
|
323
|
-
isDirty.value = JSON.stringify(v) !== JSON.stringify(state.value)
|
|
324
|
-
},
|
|
325
|
-
{ deep: true }
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
const submitFromState = Effect.gen(function*() {
|
|
329
|
-
if (!isValid.value) return
|
|
330
|
-
return yield* onSubmit(yield* parse(state.value))
|
|
331
|
-
}) // () => submit(Promise.resolve({ valid: isValid.value }))
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
fields,
|
|
335
|
-
/** optimized for Vuetify v-form submit callback */
|
|
336
|
-
submit,
|
|
337
|
-
/** optimized for Native form submit callback or general use */
|
|
338
|
-
submitFromState,
|
|
339
|
-
isDirty,
|
|
340
|
-
isValid
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
useSafeMutationWithState,
|
|
346
|
-
useAndHandleMutation,
|
|
347
|
-
makeUseAndHandleMutation,
|
|
348
|
-
useHandleRequestWithToast,
|
|
349
|
-
buildFormFromSchema,
|
|
350
|
-
useSafeQuery,
|
|
351
|
-
useSafeMutation
|
|
352
|
-
}
|
|
353
|
-
}
|
package/src/mutate2.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import * as Result from "@effect-rx/rx/Result"
|
|
3
|
-
import type { InvalidateOptions, InvalidateQueryFilters } from "@tanstack/vue-query"
|
|
4
|
-
import { useQueryClient } from "@tanstack/vue-query"
|
|
5
|
-
import { Cause, Effect, Exit, Option } from "effect-app"
|
|
6
|
-
import type { RequestHandler, RequestHandlerWithInput, TaggedRequestClassAny } from "effect-app/client/clientFor"
|
|
7
|
-
import { tuple } from "effect-app/Function"
|
|
8
|
-
import type { ComputedRef, Ref } from "vue"
|
|
9
|
-
import { computed, ref, shallowRef } from "vue"
|
|
10
|
-
import { reportRuntimeError } from "./lib.js"
|
|
11
|
-
import { getQueryKey } from "./mutate.js"
|
|
12
|
-
|
|
13
|
-
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
|
|
14
|
-
export function make<A, E, R>(self: Effect<A, E, R>) {
|
|
15
|
-
const result = shallowRef(Result.initial() as Result.Result<A, E>)
|
|
16
|
-
|
|
17
|
-
const execute = Effect
|
|
18
|
-
.sync(() => {
|
|
19
|
-
result.value = Result.waiting(result.value)
|
|
20
|
-
})
|
|
21
|
-
.pipe(
|
|
22
|
-
Effect.andThen(self),
|
|
23
|
-
Effect.exit,
|
|
24
|
-
Effect.andThen(Result.fromExit),
|
|
25
|
-
Effect.flatMap((r) => Effect.sync(() => result.value = r))
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const latestSuccess = computed(() => Option.getOrUndefined(Result.value(result.value)))
|
|
29
|
-
|
|
30
|
-
return tuple(result, latestSuccess, execute)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface MutationInitial {
|
|
34
|
-
readonly _tag: "Initial"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface MutationLoading {
|
|
38
|
-
readonly _tag: "Loading"
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface MutationSuccess<A> {
|
|
42
|
-
readonly _tag: "Success"
|
|
43
|
-
readonly data: A
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface MutationError<E> {
|
|
47
|
-
readonly _tag: "Error"
|
|
48
|
-
readonly error: E
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type MutationResult<A, E> = MutationInitial | MutationLoading | MutationSuccess<A> | MutationError<E>
|
|
52
|
-
|
|
53
|
-
export type MaybeRef<T> = Ref<T> | ComputedRef<T> | T
|
|
54
|
-
type MaybeRefDeep<T> = MaybeRef<
|
|
55
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
56
|
-
T extends Function ? T
|
|
57
|
-
: T extends object ? {
|
|
58
|
-
[Property in keyof T]: MaybeRefDeep<T[Property]>
|
|
59
|
-
}
|
|
60
|
-
: T
|
|
61
|
-
>
|
|
62
|
-
|
|
63
|
-
export interface MutationOptions<A, I = void> {
|
|
64
|
-
queryInvalidation?: (defaultKey: string[], name: string) => {
|
|
65
|
-
filters?: MaybeRefDeep<InvalidateQueryFilters> | undefined
|
|
66
|
-
options?: MaybeRefDeep<InvalidateOptions> | undefined
|
|
67
|
-
}[]
|
|
68
|
-
/** @deprecated use mapHandler with Effect.andThen/tap accordingly */
|
|
69
|
-
onSuccess?: (a: A, i: I) => Promise<unknown>
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// TODO: more efficient invalidation, including args etc
|
|
73
|
-
// return Effect.promise(() => queryClient.invalidateQueries({
|
|
74
|
-
// predicate: (_) => nses.includes(_.queryKey.filter((_) => _.startsWith("$")).join("/"))
|
|
75
|
-
// }))
|
|
76
|
-
/*
|
|
77
|
-
// const nses: string[] = []
|
|
78
|
-
// for (let i = 0; i < ns.length; i++) {
|
|
79
|
-
// nses.push(ns.slice(0, i + 1).join("/"))
|
|
80
|
-
// }
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
export const makeMutation2 = () => {
|
|
84
|
-
/**
|
|
85
|
-
* Pass a function that returns an Effect, e.g from a client action, or an Effect
|
|
86
|
-
* Returns a tuple with state ref and execution function which reports errors as Toast.
|
|
87
|
-
*/
|
|
88
|
-
const useSafeMutation: {
|
|
89
|
-
<I, E, A, R, Request extends TaggedRequestClassAny>(
|
|
90
|
-
self: RequestHandlerWithInput<I, A, E, R, Request>,
|
|
91
|
-
options?: MutationOptions<A, I>
|
|
92
|
-
): readonly [
|
|
93
|
-
Readonly<Ref<MutationResult<A, E>>>,
|
|
94
|
-
(i: I) => Effect<A, E, R>
|
|
95
|
-
]
|
|
96
|
-
<E, A, R, Request extends TaggedRequestClassAny>(
|
|
97
|
-
self: RequestHandler<A, E, R, Request>,
|
|
98
|
-
options?: MutationOptions<A>
|
|
99
|
-
): readonly [
|
|
100
|
-
Readonly<Ref<MutationResult<A, E>>>,
|
|
101
|
-
Effect<A, E, R>
|
|
102
|
-
]
|
|
103
|
-
} = <I, E, A, R, Request extends TaggedRequestClassAny>(
|
|
104
|
-
self: RequestHandlerWithInput<I, A, E, R, Request> | RequestHandler<A, E, R, Request>,
|
|
105
|
-
options?: MutationOptions<A, I>
|
|
106
|
-
) => {
|
|
107
|
-
const queryClient = useQueryClient()
|
|
108
|
-
const state: Ref<MutationResult<A, E>> = ref<MutationResult<A, E>>({ _tag: "Initial" }) as any
|
|
109
|
-
|
|
110
|
-
const invalidateQueries = (
|
|
111
|
-
filters?: MaybeRefDeep<InvalidateQueryFilters>,
|
|
112
|
-
options?: MaybeRefDeep<InvalidateOptions>
|
|
113
|
-
) => Effect.promise(() => queryClient.invalidateQueries(filters, options))
|
|
114
|
-
|
|
115
|
-
function handleExit(exit: Exit.Exit<A, E>) {
|
|
116
|
-
return Effect.sync(() => {
|
|
117
|
-
if (Exit.isSuccess(exit)) {
|
|
118
|
-
state.value = { _tag: "Success", data: exit.value }
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const err = Cause.failureOption(exit.cause)
|
|
123
|
-
if (Option.isSome(err)) {
|
|
124
|
-
state.value = { _tag: "Error", error: err.value }
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const invalidateCache = Effect.suspend(() => {
|
|
131
|
-
const queryKey = getQueryKey(self)
|
|
132
|
-
|
|
133
|
-
if (options?.queryInvalidation) {
|
|
134
|
-
const opts = options.queryInvalidation(queryKey, self.name)
|
|
135
|
-
if (!opts.length) {
|
|
136
|
-
return Effect.void
|
|
137
|
-
}
|
|
138
|
-
return Effect
|
|
139
|
-
.andThen(
|
|
140
|
-
Effect.annotateCurrentSpan({ queryKey, opts }),
|
|
141
|
-
Effect.forEach(opts, (_) => invalidateQueries(_.filters, _.options), { concurrency: "inherit" })
|
|
142
|
-
)
|
|
143
|
-
.pipe(Effect.withSpan("client.query.invalidation", { captureStackTrace: false }))
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!queryKey) return Effect.void
|
|
147
|
-
|
|
148
|
-
return Effect
|
|
149
|
-
.andThen(
|
|
150
|
-
Effect.annotateCurrentSpan({ queryKey }),
|
|
151
|
-
invalidateQueries({ queryKey })
|
|
152
|
-
)
|
|
153
|
-
.pipe(Effect.withSpan("client.query.invalidation", { captureStackTrace: false }))
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
const onSuccess = options?.onSuccess
|
|
157
|
-
|
|
158
|
-
const handler = self.handler
|
|
159
|
-
const r = Effect.isEffect(handler)
|
|
160
|
-
? tuple(
|
|
161
|
-
state,
|
|
162
|
-
Effect
|
|
163
|
-
.sync(() => {
|
|
164
|
-
state.value = { _tag: "Loading" }
|
|
165
|
-
})
|
|
166
|
-
.pipe(
|
|
167
|
-
Effect.zipRight(handler),
|
|
168
|
-
Effect.tap(invalidateCache),
|
|
169
|
-
Effect.tap((a) => onSuccess ? Effect.promise(() => onSuccess(a, undefined as I)) : Effect.void),
|
|
170
|
-
Effect.tapDefect(reportRuntimeError),
|
|
171
|
-
Effect.onExit(handleExit),
|
|
172
|
-
Effect.withSpan(`mutation ${self.name}`, { captureStackTrace: false })
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
: tuple(state, (fst: I) => {
|
|
176
|
-
const effect = handler(fst)
|
|
177
|
-
return Effect
|
|
178
|
-
.sync(() => {
|
|
179
|
-
state.value = { _tag: "Loading" }
|
|
180
|
-
})
|
|
181
|
-
.pipe(
|
|
182
|
-
Effect.zipRight(effect),
|
|
183
|
-
Effect.tapBoth({ onFailure: () => invalidateCache, onSuccess: () => invalidateCache }),
|
|
184
|
-
Effect.tap((a) => onSuccess ? Effect.promise(() => onSuccess(a, fst)) : Effect.void),
|
|
185
|
-
Effect.tapDefect(reportRuntimeError),
|
|
186
|
-
Effect.onExit(handleExit),
|
|
187
|
-
Effect.withSpan(`mutation ${self.name}`, { captureStackTrace: false })
|
|
188
|
-
)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
return r as any
|
|
192
|
-
}
|
|
193
|
-
return useSafeMutation
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
197
|
-
export interface MakeMutation2 extends ReturnType<typeof makeMutation2> {}
|