@effect-app/vue 4.0.0-beta.19 → 4.0.0-beta.190
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 +1383 -0
- package/dist/commander.d.ts +620 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +1056 -0
- package/dist/confirm.d.ts +19 -0
- package/dist/confirm.d.ts.map +1 -0
- package/dist/confirm.js +24 -0
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -18
- package/dist/form.d.ts +13 -4
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +41 -12
- package/dist/index.d.ts +1 -1
- package/dist/intl.d.ts +15 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +9 -0
- package/dist/lib.d.ts +6 -8
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +34 -7
- package/dist/makeClient.d.ts +191 -292
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +217 -369
- package/dist/makeContext.d.ts +1 -1
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/makeUseCommand.d.ts +8 -0
- package/dist/makeUseCommand.d.ts.map +1 -0
- package/dist/makeUseCommand.js +13 -0
- package/dist/mutate.d.ts +56 -25
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +132 -33
- package/dist/query.d.ts +24 -16
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +119 -37
- package/dist/routeParams.d.ts +1 -1
- package/dist/runtime.d.ts +5 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +27 -17
- package/dist/toast.d.ts +46 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +32 -0
- package/dist/withToast.d.ts +26 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +54 -0
- package/eslint.config.mjs +2 -2
- package/examples/streamMutation.ts +70 -0
- package/package.json +48 -48
- package/src/commander.ts +3378 -0
- package/src/{experimental/confirm.ts → confirm.ts} +10 -14
- package/src/errorReporter.ts +62 -74
- package/src/form.ts +55 -16
- package/src/intl.ts +12 -0
- package/src/lib.ts +46 -13
- package/src/makeClient.ts +623 -1043
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +6 -4
- package/src/mutate.ts +273 -72
- package/src/query.ts +181 -68
- package/src/runtime.ts +39 -18
- package/src/{experimental/toast.ts → toast.ts} +11 -25
- package/src/{experimental/withToast.ts → withToast.ts} +28 -10
- package/test/Mutation.test.ts +105 -11
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/streamFinal.test.d.ts.map +1 -0
- package/test/dist/streamFn.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +3289 -114
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +152 -25
- package/test/form-validation-errors.test.ts +23 -19
- package/test/form.test.ts +20 -2
- package/test/lib.test.ts +240 -0
- package/test/makeClient.test.ts +286 -38
- package/test/streamFinal.test.ts +63 -0
- package/test/streamFn.test.ts +436 -0
- package/test/stubs.ts +192 -42
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +5 -2
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/dist/experimental/commander.d.ts +0 -359
- package/dist/experimental/commander.d.ts.map +0 -1
- package/dist/experimental/commander.js +0 -557
- package/dist/experimental/confirm.d.ts +0 -19
- package/dist/experimental/confirm.d.ts.map +0 -1
- package/dist/experimental/confirm.js +0 -28
- package/dist/experimental/intl.d.ts +0 -16
- package/dist/experimental/intl.d.ts.map +0 -1
- package/dist/experimental/intl.js +0 -5
- package/dist/experimental/makeUseCommand.d.ts +0 -8
- package/dist/experimental/makeUseCommand.d.ts.map +0 -1
- package/dist/experimental/makeUseCommand.js +0 -13
- package/dist/experimental/toast.d.ts +0 -47
- package/dist/experimental/toast.d.ts.map +0 -1
- package/dist/experimental/toast.js +0 -41
- package/dist/experimental/withToast.d.ts +0 -25
- package/dist/experimental/withToast.d.ts.map +0 -1
- package/dist/experimental/withToast.js +0 -45
- package/src/experimental/commander.ts +0 -1835
- package/src/experimental/intl.ts +0 -9
|
@@ -5,22 +5,24 @@ type X<X> = X
|
|
|
5
5
|
|
|
6
6
|
// helps retain JSDoc
|
|
7
7
|
export interface CommanderResolved<RT, RTHooks>
|
|
8
|
-
extends
|
|
8
|
+
extends
|
|
9
|
+
X<typeof CommanderStatic>,
|
|
10
|
+
Pick<CommanderImpl<RT, RTHooks>, "fn" | "wrap" | "streamWrap" | "streamFn" | "alt" | "alt2">
|
|
9
11
|
{
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export const makeUseCommand = Effect.fnUntraced(
|
|
13
15
|
function*<R = never, RTHooks = never>(rtHooks: Layer.Layer<RTHooks, never, R>) {
|
|
14
16
|
const cmndr = yield* Commander
|
|
15
|
-
const runtime = yield* Effect.
|
|
17
|
+
const runtime = yield* Effect.context<R>()
|
|
16
18
|
|
|
17
19
|
const comm = cmndr(runtime, rtHooks)
|
|
18
20
|
|
|
19
|
-
const command = {
|
|
21
|
+
const command: CommanderResolved<R, RTHooks> = {
|
|
20
22
|
...comm,
|
|
21
23
|
...CommanderStatic
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
return command
|
|
26
|
+
return command
|
|
25
27
|
}
|
|
26
28
|
)
|
package/src/mutate.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { matchQuery } from "@tanstack/query-core"
|
|
2
3
|
import { type InvalidateOptions, type InvalidateQueryFilters, type QueryClient, useQueryClient } from "@tanstack/vue-query"
|
|
3
|
-
import { type Cause, Effect,
|
|
4
|
-
import { type Req } from "effect-app/client"
|
|
4
|
+
import { type Cause, Effect, Exit, Option } from "effect-app"
|
|
5
|
+
import { type InvalidationKey, InvalidationKeysFromServer, makeInvalidationKeysService, makeQueryKey, type Req } from "effect-app/client"
|
|
5
6
|
import type { ClientForOptions, RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
|
|
6
7
|
import { tuple } from "effect-app/Function"
|
|
8
|
+
import * as Ref from "effect/Ref"
|
|
9
|
+
import * as Stream from "effect/Stream"
|
|
7
10
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
8
11
|
import { computed, type ComputedRef, shallowRef } from "vue"
|
|
9
|
-
import { makeQueryKey } from "./lib.js"
|
|
10
12
|
|
|
11
13
|
export const getQueryKey = (h: { id: string; options?: ClientForOptions }) => {
|
|
12
14
|
const key = makeQueryKey(h)
|
|
@@ -67,39 +69,33 @@ export function make<A, E, R>(self: Effect.Effect<A, E, R>) {
|
|
|
67
69
|
return tuple(result, latestSuccess, execute)
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
export interface MutationOptionsBase {
|
|
72
|
+
export interface MutationOptionsBase<A = unknown, B = A, E2 = never, R2 = never> {
|
|
71
73
|
/**
|
|
72
74
|
* By default we invalidate one level of the query key, e.g $project/$configuration.get, we invalidate $project.
|
|
73
75
|
* This can be overridden by providing a function that returns an array of filters and options.
|
|
74
76
|
*/
|
|
75
|
-
queryInvalidation?: (defaultKey: string[], name: string) => {
|
|
77
|
+
queryInvalidation?: (defaultKey: string[], name: string, input?: unknown, output?: Exit.Exit<unknown, unknown>) => {
|
|
76
78
|
filters?: InvalidateQueryFilters | undefined
|
|
77
79
|
options?: InvalidateOptions | undefined
|
|
78
80
|
}[]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** @deprecated prefer more basic @see MutationOptionsBase and separate useMutation from Command.fn */
|
|
82
|
-
export interface MutationOptions<A, E, R, A2 = A, E2 = E, R2 = R, I = void> extends MutationOptionsBase {
|
|
83
81
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
82
|
+
* Run an additional Effect after the mutation succeeds. Its output becomes the
|
|
83
|
+
* final result returned to the caller. Query cache is invalidated once on
|
|
84
|
+
* mutation exit and again after this Effect completes. Useful for long-running
|
|
85
|
+
* operations (e.g. polling a background job) where you want the caller to
|
|
86
|
+
* receive the downstream result and the cache to refresh once it is ready.
|
|
86
87
|
*
|
|
87
|
-
* @
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* useMutation(startExportCommand, {
|
|
91
|
+
* select: (result) => pollUntilDone(result.jobId)
|
|
92
|
+
* // caller receives the pollUntilDone output, not the original result
|
|
93
|
+
* })
|
|
94
|
+
* ```
|
|
88
95
|
*/
|
|
89
|
-
|
|
96
|
+
select?: (result: A) => Effect.Effect<B, E2, R2>
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
// TODO: more efficient invalidation, including args etc
|
|
93
|
-
// return Effect.promise(() => queryClient.invalidateQueries({
|
|
94
|
-
// predicate: (_) => nses.includes(_.queryKey.filter((_) => _.startsWith("$")).join("/"))
|
|
95
|
-
// }))
|
|
96
|
-
/*
|
|
97
|
-
// const nses: string[] = []`
|
|
98
|
-
// for (let i = 0; i < ns.length; i++) {
|
|
99
|
-
// nses.push(ns.slice(0, i + 1).join("/"))
|
|
100
|
-
// }
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
99
|
export const asResult: {
|
|
104
100
|
<A, E, R>(
|
|
105
101
|
handler: Effect.Effect<A, E, R>
|
|
@@ -142,12 +138,73 @@ export const asResult: {
|
|
|
142
138
|
return tuple(computed(() => state.value), act) as any
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Like `asResult`, but for streams. The ref is updated with each emitted value
|
|
143
|
+
* (keeping `waiting: true`) and is finalised (with `waiting: false`) once the
|
|
144
|
+
* stream terminates successfully. Errors are surfaced as `AsyncResult.failure`.
|
|
145
|
+
*/
|
|
146
|
+
export const asStreamResult: {
|
|
147
|
+
<A, E, R>(
|
|
148
|
+
handler: Stream.Stream<A, E, R>
|
|
149
|
+
): readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, Effect.Effect<void, never, R>]
|
|
150
|
+
<Args extends readonly any[], A, E, R>(
|
|
151
|
+
handler: (...args: Args) => Stream.Stream<A, E, R>
|
|
152
|
+
): readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, (...args: Args) => Effect.Effect<void, never, R>]
|
|
153
|
+
} = <Args extends readonly any[], A, E, R>(
|
|
154
|
+
handler: Stream.Stream<A, E, R> | ((...args: Args) => Stream.Stream<A, E, R>)
|
|
155
|
+
) => {
|
|
156
|
+
const state = shallowRef<AsyncResult.AsyncResult<A, E>>(AsyncResult.initial())
|
|
157
|
+
|
|
158
|
+
const runStream = (stream: Stream.Stream<A, E, R>): Effect.Effect<void, never, R> =>
|
|
159
|
+
Effect
|
|
160
|
+
.sync(() => {
|
|
161
|
+
state.value = AsyncResult.initial(true)
|
|
162
|
+
})
|
|
163
|
+
.pipe(
|
|
164
|
+
Effect.andThen(
|
|
165
|
+
stream.pipe(
|
|
166
|
+
Stream.runForEach((value) =>
|
|
167
|
+
Effect.sync(() => {
|
|
168
|
+
state.value = AsyncResult.success(value, { waiting: true })
|
|
169
|
+
})
|
|
170
|
+
),
|
|
171
|
+
Effect.exit,
|
|
172
|
+
Effect.flatMap((exit) =>
|
|
173
|
+
Effect.sync(() => {
|
|
174
|
+
if (exit._tag === "Success") {
|
|
175
|
+
const current = state.value
|
|
176
|
+
if (AsyncResult.isSuccess(current)) {
|
|
177
|
+
state.value = AsyncResult.success(current.value, { waiting: false })
|
|
178
|
+
} else {
|
|
179
|
+
state.value = AsyncResult.initial(false)
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
state.value = AsyncResult.failure(exit.cause)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
const act = Stream.isStream(handler)
|
|
191
|
+
? runStream(handler)
|
|
192
|
+
: (...args: Args) => runStream(handler(...args))
|
|
193
|
+
|
|
194
|
+
return tuple(computed(() => state.value), act) as any
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const buildInvalidateCache = (
|
|
146
198
|
queryClient: QueryClient,
|
|
147
199
|
self: { id: string; options?: ClientForOptions },
|
|
148
|
-
|
|
200
|
+
queryInvalidation?: MutationOptionsBase["queryInvalidation"]
|
|
149
201
|
) => {
|
|
150
|
-
|
|
202
|
+
type InvalidationTarget = {
|
|
203
|
+
readonly filters: InvalidateQueryFilters | undefined
|
|
204
|
+
readonly options: InvalidateOptions | undefined
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const invalidateQueriesFn = (
|
|
151
208
|
filters?: InvalidateQueryFilters,
|
|
152
209
|
options?: InvalidateOptions
|
|
153
210
|
) =>
|
|
@@ -158,44 +215,142 @@ export const invalidateQueries = (
|
|
|
158
215
|
)
|
|
159
216
|
)
|
|
160
217
|
|
|
161
|
-
const
|
|
218
|
+
const getClientInvalidationTargets = (
|
|
219
|
+
input: unknown,
|
|
220
|
+
output: Exit.Exit<unknown, unknown>
|
|
221
|
+
): ReadonlyArray<InvalidationTarget> => {
|
|
162
222
|
const queryKey = getQueryKey(self)
|
|
163
223
|
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
224
|
+
if (queryInvalidation) {
|
|
225
|
+
return queryInvalidation(queryKey, self.id, input, output).map((_) => ({
|
|
226
|
+
filters: _.filters,
|
|
227
|
+
options: _.options
|
|
228
|
+
}))
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!queryKey) {
|
|
232
|
+
return []
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return [{ filters: { queryKey }, options: undefined }]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const invalidateCache = (
|
|
239
|
+
input: unknown,
|
|
240
|
+
output: Exit.Exit<unknown, unknown>,
|
|
241
|
+
serverKeys: ReadonlyArray<InvalidationKey>
|
|
242
|
+
) =>
|
|
243
|
+
Effect.suspend(() => {
|
|
244
|
+
const clientTargets = getClientInvalidationTargets(input, output)
|
|
245
|
+
const serverTargets: ReadonlyArray<InvalidationTarget> = serverKeys.map((queryKey) => ({
|
|
246
|
+
filters: { queryKey },
|
|
247
|
+
options: undefined
|
|
248
|
+
}))
|
|
249
|
+
const allTargets: ReadonlyArray<InvalidationTarget> = [...clientTargets, ...serverTargets]
|
|
250
|
+
|
|
251
|
+
if (!allTargets.length) return Effect.void
|
|
252
|
+
|
|
253
|
+
// Group targets by refetchType + options so each group can be merged into a single
|
|
254
|
+
// invalidateQueries call using a predicate, reducing N calls to 1 in the common case.
|
|
255
|
+
type Group = {
|
|
256
|
+
targets: Array<InvalidationTarget>
|
|
257
|
+
refetchType: InvalidateQueryFilters["refetchType"]
|
|
258
|
+
options: InvalidateOptions | undefined
|
|
168
259
|
}
|
|
260
|
+
const groups = new Map<string, Group>()
|
|
261
|
+
for (const target of allTargets) {
|
|
262
|
+
const key = `${target.filters?.refetchType ?? ""}|${target.options?.cancelRefetch ?? ""}|${
|
|
263
|
+
target.options?.throwOnError?.toString() ?? ""
|
|
264
|
+
}`
|
|
265
|
+
const existing = groups.get(key)
|
|
266
|
+
if (existing) {
|
|
267
|
+
existing.targets.push(target)
|
|
268
|
+
} else {
|
|
269
|
+
groups.set(key, { targets: [target], refetchType: target.filters?.refetchType, options: target.options })
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
169
273
|
return Effect
|
|
170
274
|
.andThen(
|
|
171
|
-
Effect.annotateCurrentSpan({
|
|
172
|
-
Effect.forEach(
|
|
275
|
+
Effect.annotateCurrentSpan({ clientTargets, serverKeys }),
|
|
276
|
+
Effect.forEach(
|
|
277
|
+
groups.values(),
|
|
278
|
+
({ options, refetchType, targets }) =>
|
|
279
|
+
invalidateQueriesFn(
|
|
280
|
+
{
|
|
281
|
+
...(refetchType !== undefined ? { refetchType } : {}),
|
|
282
|
+
predicate: (query) => targets.some((t) => t.filters ? matchQuery(t.filters, query) : true)
|
|
283
|
+
},
|
|
284
|
+
options
|
|
285
|
+
),
|
|
286
|
+
{ discard: true, concurrency: "inherit" }
|
|
287
|
+
)
|
|
173
288
|
)
|
|
174
|
-
.pipe(
|
|
175
|
-
|
|
289
|
+
.pipe(
|
|
290
|
+
Effect.tap(
|
|
291
|
+
// hand over control back to the event loop so that state can be updated..
|
|
292
|
+
// TODO: should we do this in general on any mutation, regardless of invalidation?
|
|
293
|
+
Effect.sleep(0)
|
|
294
|
+
),
|
|
295
|
+
Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false })
|
|
296
|
+
)
|
|
297
|
+
})
|
|
176
298
|
|
|
177
|
-
|
|
299
|
+
return invalidateCache
|
|
300
|
+
}
|
|
178
301
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
Effect.tap(
|
|
186
|
-
// hand over control back to the event loop so that state can be updated..
|
|
187
|
-
// TODO: should we do this in general on any mutation, regardless of invalidation?
|
|
188
|
-
Effect.sleep(0)
|
|
189
|
-
),
|
|
190
|
-
Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false })
|
|
191
|
-
)
|
|
192
|
-
})
|
|
302
|
+
export const invalidateQueries = (
|
|
303
|
+
queryClient: QueryClient,
|
|
304
|
+
self: { id: string; options?: ClientForOptions },
|
|
305
|
+
options?: MutationOptionsBase
|
|
306
|
+
) => {
|
|
307
|
+
const invalidateCache = buildInvalidateCache(queryClient, self, options?.queryInvalidation)
|
|
193
308
|
|
|
194
|
-
const
|
|
309
|
+
const select = options?.select
|
|
310
|
+
|
|
311
|
+
const handle = <A, E, R>(eff: Effect.Effect<A, E, R>, input?: unknown) =>
|
|
312
|
+
Effect.gen(function*() {
|
|
313
|
+
const keysRef = yield* Ref.make<ReadonlyArray<InvalidationKey>>([])
|
|
314
|
+
const result = yield* eff.pipe(
|
|
315
|
+
Effect.provideService(InvalidationKeysFromServer, makeInvalidationKeysService(keysRef)),
|
|
316
|
+
Effect.onExit((exit) =>
|
|
317
|
+
Effect.gen(function*() {
|
|
318
|
+
const serverKeys = yield* Ref.get(keysRef)
|
|
319
|
+
yield* invalidateCache(input, exit, serverKeys)
|
|
320
|
+
})
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
if (select) {
|
|
324
|
+
return yield* select(result).pipe(
|
|
325
|
+
Effect.onExit((exit) =>
|
|
326
|
+
Effect.gen(function*() {
|
|
327
|
+
const serverKeys = yield* Ref.get(keysRef)
|
|
328
|
+
yield* invalidateCache(input, exit, serverKeys)
|
|
329
|
+
})
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
return result
|
|
334
|
+
})
|
|
195
335
|
|
|
196
336
|
return handle
|
|
197
337
|
}
|
|
198
338
|
|
|
339
|
+
export interface MutationFnWithInput<I, A, E, R, Id extends string> {
|
|
340
|
+
<B = A, E2 = never, R2 = never>(
|
|
341
|
+
input: I,
|
|
342
|
+
options?: MutationOptionsBase<A, B, E2, R2>
|
|
343
|
+
): Effect.Effect<B, E | E2, R | R2>
|
|
344
|
+
readonly id: Id
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface MutationFn<A, E, R, Id extends string> {
|
|
348
|
+
<B = A, E2 = never, R2 = never>(
|
|
349
|
+
options?: MutationOptionsBase<A, B, E2, R2>
|
|
350
|
+
): Effect.Effect<B, E | E2, R | R2>
|
|
351
|
+
readonly id: Id
|
|
352
|
+
}
|
|
353
|
+
|
|
199
354
|
export const makeMutation = () => {
|
|
200
355
|
const useMutation: {
|
|
201
356
|
/**
|
|
@@ -203,25 +358,23 @@ export const makeMutation = () => {
|
|
|
203
358
|
* Executes query cache invalidation based on default rules or provided option.
|
|
204
359
|
*/
|
|
205
360
|
<I, E, A, R, Request extends Req, Id extends string>(
|
|
206
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Id
|
|
207
|
-
|
|
208
|
-
): ((i: I) => Effect.Effect<A, E, R>) & { readonly id: Id }
|
|
361
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Id>
|
|
362
|
+
): MutationFnWithInput<I, A, E, R, Id>
|
|
209
363
|
/**
|
|
210
364
|
* Pass an Effect, e.g from a client action
|
|
211
365
|
* Executes query cache invalidation based on default rules or provided option.
|
|
212
366
|
*/
|
|
213
367
|
<E, A, R, Request extends Req, Id extends string>(
|
|
214
|
-
self: RequestHandler<A, E, R, Request, Id
|
|
215
|
-
|
|
216
|
-
): Effect.Effect<A, E, R> & { readonly id: Id }
|
|
368
|
+
self: RequestHandler<A, E, R, Request, Id>
|
|
369
|
+
): MutationFn<A, E, R, Id>
|
|
217
370
|
} = <I, E, A, R, Request extends Req, Id extends string>(
|
|
218
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Id> | RequestHandler<A, E, R, Request, Id
|
|
219
|
-
options?: MutationOptionsBase
|
|
371
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Id> | RequestHandler<A, E, R, Request, Id>
|
|
220
372
|
) => {
|
|
221
373
|
const queryClient = useQueryClient()
|
|
222
|
-
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
223
374
|
const handler = self.handler
|
|
224
|
-
const r = Effect.isEffect(handler)
|
|
375
|
+
const r = Effect.isEffect(handler)
|
|
376
|
+
? (options?: MutationOptionsBase) => invalidateQueries(queryClient, self, options)(handler)
|
|
377
|
+
: (i: I, options?: MutationOptionsBase) => invalidateQueries(queryClient, self, options)(handler(i), i)
|
|
225
378
|
|
|
226
379
|
return Object.assign(r, { id: self.id }) as any
|
|
227
380
|
}
|
|
@@ -238,26 +391,74 @@ export const useMakeMutation = () => {
|
|
|
238
391
|
* Executes query cache invalidation based on default rules or provided option.
|
|
239
392
|
*/
|
|
240
393
|
<I, E, A, R, Request extends Req, Id extends string>(
|
|
241
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Id
|
|
242
|
-
|
|
243
|
-
): ((i: I) => Effect.Effect<A, E, R>) & { readonly id: Id }
|
|
394
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Id>
|
|
395
|
+
): MutationFnWithInput<I, A, E, R, Id>
|
|
244
396
|
/**
|
|
245
397
|
* Pass an Effect, e.g from a client action
|
|
246
398
|
* Executes query cache invalidation based on default rules or provided option.
|
|
247
399
|
*/
|
|
248
400
|
<E, A, R, Request extends Req, Id extends string>(
|
|
249
|
-
self: RequestHandler<A, E, R, Request, Id
|
|
250
|
-
|
|
251
|
-
): Effect.Effect<A, E, R> & { readonly id: Id }
|
|
401
|
+
self: RequestHandler<A, E, R, Request, Id>
|
|
402
|
+
): MutationFn<A, E, R, Id>
|
|
252
403
|
} = <I, E, A, R, Request extends Req, Id extends string>(
|
|
253
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Id> | RequestHandler<A, E, R, Request, Id
|
|
254
|
-
options?: MutationOptionsBase
|
|
404
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Id> | RequestHandler<A, E, R, Request, Id>
|
|
255
405
|
) => {
|
|
256
|
-
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
257
406
|
const handler = self.handler
|
|
258
|
-
const r = Effect.isEffect(handler)
|
|
407
|
+
const r = Effect.isEffect(handler)
|
|
408
|
+
? (options?: MutationOptionsBase) => invalidateQueries(queryClient, self, options)(handler)
|
|
409
|
+
: (i: I, options?: MutationOptionsBase) => invalidateQueries(queryClient, self, options)(handler(i), i)
|
|
259
410
|
|
|
260
411
|
return Object.assign(r, { id: self.id }) as any
|
|
261
412
|
}
|
|
262
413
|
return useMutation
|
|
263
414
|
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Returns a stream-based mutation factory for use with `streamFn`.
|
|
418
|
+
* The outer Effect sets up per-invocation invalidation scaffolding
|
|
419
|
+
* and returns a stream that triggers query invalidation via `Stream.ensuring` when it completes.
|
|
420
|
+
*
|
|
421
|
+
* Use with `streamFn` / `Command.streamFn(id)(mutateHandler, ...combinators)` so that
|
|
422
|
+
* the command manages its own reactive state internally.
|
|
423
|
+
*
|
|
424
|
+
* Must be called inside a Vue setup context (uses `useQueryClient` internally).
|
|
425
|
+
*/
|
|
426
|
+
export const makeStreamMutation2 = () => {
|
|
427
|
+
const queryClient = useQueryClient()
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
self: {
|
|
431
|
+
id: string
|
|
432
|
+
options?: ClientForOptions
|
|
433
|
+
handler: Stream.Stream<any, any, any> | ((i: any) => Stream.Stream<any, any, any>)
|
|
434
|
+
},
|
|
435
|
+
mergedInvalidation?: MutationOptionsBase["queryInvalidation"]
|
|
436
|
+
) => {
|
|
437
|
+
const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation)
|
|
438
|
+
|
|
439
|
+
const makeInvocationEffect = (input: unknown, source: Stream.Stream<any, any, any>) =>
|
|
440
|
+
Effect.gen(function*() {
|
|
441
|
+
const keysRef = yield* Ref.make<ReadonlyArray<InvalidationKey>>([])
|
|
442
|
+
const invKeys = makeInvalidationKeysService(keysRef, (key) => invCache(input, Exit.succeed(undefined), [key]))
|
|
443
|
+
const lastRef = yield* Ref.make<any>(undefined)
|
|
444
|
+
return source.pipe(
|
|
445
|
+
Stream.provideService(InvalidationKeysFromServer, invKeys),
|
|
446
|
+
Stream.tap((v) => Ref.set(lastRef, v)),
|
|
447
|
+
Stream.ensuring(
|
|
448
|
+
Effect.gen(function*() {
|
|
449
|
+
const lastValue = yield* Ref.get(lastRef)
|
|
450
|
+
const serverKeys = yield* Ref.get(keysRef)
|
|
451
|
+
yield* invCache(input, Exit.succeed(lastValue), serverKeys)
|
|
452
|
+
})
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
const handler = self.handler
|
|
458
|
+
const act = Stream.isStream(handler)
|
|
459
|
+
? Stream.unwrap(makeInvocationEffect(undefined, handler))
|
|
460
|
+
: (i: any) => Stream.unwrap(makeInvocationEffect(i, (handler as (i: any) => Stream.Stream<any, any, any>)(i)))
|
|
461
|
+
|
|
462
|
+
return act
|
|
463
|
+
}
|
|
464
|
+
}
|