@effect-app/vue 4.0.0-beta.177 → 4.0.0-beta.178

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/dist/mutate.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { matchQuery } from "@tanstack/query-core";
3
3
  import { useQueryClient } from "@tanstack/vue-query";
4
- import { Effect, Option } from "effect-app";
4
+ import { Effect, Exit, Option } from "effect-app";
5
5
  import { InvalidationKeysFromServer, makeInvalidationKeysService, makeQueryKey } from "effect-app/client";
6
6
  import { tuple } from "effect-app/Function";
7
7
  import * as Ref from "effect/Ref";
8
+ import * as Stream from "effect/Stream";
8
9
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
9
10
  import { computed, shallowRef } from "vue";
10
11
  export const getQueryKey = (h) => {
@@ -63,12 +64,44 @@ export const asResult = (handler) => {
63
64
  .pipe(Effect.andThen(Effect.suspend(() => handler(...args).pipe(Effect.exit, Effect.tap((exit) => Effect.sync(() => (state.value = AsyncResult.fromExit(exit))))))));
64
65
  return tuple(computed(() => state.value), act);
65
66
  };
66
- export const invalidateQueries = (queryClient, self, options) => {
67
- const invalidateQueries = (filters, options) => Effect.currentSpan.pipe(Effect.orElseSucceed(() => null), Effect.flatMap((span) => Effect.promise(() => queryClient.invalidateQueries(filters, { ...options, updateMeta: { span } }))));
67
+ /**
68
+ * Like `asResult`, but for streams. The ref is updated with each emitted value
69
+ * (keeping `waiting: true`) and is finalised (with `waiting: false`) once the
70
+ * stream terminates successfully. Errors are surfaced as `AsyncResult.failure`.
71
+ */
72
+ export const asStreamResult = (handler) => {
73
+ const state = shallowRef(AsyncResult.initial());
74
+ const runStream = (stream) => Effect
75
+ .sync(() => {
76
+ state.value = AsyncResult.initial(true);
77
+ })
78
+ .pipe(Effect.andThen(stream.pipe(Stream.runForEach((value) => Effect.sync(() => {
79
+ state.value = AsyncResult.success(value, { waiting: true });
80
+ })), Effect.exit, Effect.flatMap((exit) => Effect.sync(() => {
81
+ if (exit._tag === "Success") {
82
+ const current = state.value;
83
+ if (AsyncResult.isSuccess(current)) {
84
+ state.value = AsyncResult.success(current.value, { waiting: false });
85
+ }
86
+ else {
87
+ state.value = AsyncResult.initial(false);
88
+ }
89
+ }
90
+ else {
91
+ state.value = AsyncResult.failure(exit.cause);
92
+ }
93
+ })))));
94
+ const act = Stream.isStream(handler)
95
+ ? runStream(handler)
96
+ : (...args) => runStream(handler(...args));
97
+ return tuple(computed(() => state.value), act);
98
+ };
99
+ const buildInvalidateCache = (queryClient, self, queryInvalidation) => {
100
+ const invalidateQueriesFn = (filters, options) => Effect.currentSpan.pipe(Effect.orElseSucceed(() => null), Effect.flatMap((span) => Effect.promise(() => queryClient.invalidateQueries(filters, { ...options, updateMeta: { span } }))));
68
101
  const getClientInvalidationTargets = (input, output) => {
69
102
  const queryKey = getQueryKey(self);
70
- if (options?.queryInvalidation) {
71
- return options.queryInvalidation(queryKey, self.id, input, output).map((_) => ({
103
+ if (queryInvalidation) {
104
+ return queryInvalidation(queryKey, self.id, input, output).map((_) => ({
72
105
  filters: _.filters,
73
106
  options: _.options
74
107
  }));
@@ -99,7 +132,7 @@ export const invalidateQueries = (queryClient, self, options) => {
99
132
  }
100
133
  }
101
134
  return Effect
102
- .andThen(Effect.annotateCurrentSpan({ clientTargets, serverKeys }), Effect.forEach(groups.values(), ({ options, refetchType, targets }) => invalidateQueries({
135
+ .andThen(Effect.annotateCurrentSpan({ clientTargets, serverKeys }), Effect.forEach(groups.values(), ({ options, refetchType, targets }) => invalidateQueriesFn({
103
136
  ...(refetchType !== undefined ? { refetchType } : {}),
104
137
  predicate: (query) => targets.some((t) => t.filters ? matchQuery(t.filters, query) : true)
105
138
  }, options), { discard: true, concurrency: "inherit" }))
@@ -108,6 +141,10 @@ export const invalidateQueries = (queryClient, self, options) => {
108
141
  // TODO: should we do this in general on any mutation, regardless of invalidation?
109
142
  Effect.sleep(0)), Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false }));
110
143
  });
144
+ return invalidateCache;
145
+ };
146
+ export const invalidateQueries = (queryClient, self, options) => {
147
+ const invalidateCache = buildInvalidateCache(queryClient, self, options?.queryInvalidation);
111
148
  const select = options?.select;
112
149
  const handle = (eff, input) => Effect.gen(function* () {
113
150
  const keysRef = yield* Ref.make([]);
@@ -148,4 +185,51 @@ export const useMakeMutation = () => {
148
185
  };
149
186
  return useMutation;
150
187
  };
151
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXV0YXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL211dGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2pELE9BQU8sRUFBeUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0gsT0FBTyxFQUFjLE1BQU0sRUFBYSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDbEUsT0FBTyxFQUF3QiwwQkFBMEIsRUFBRSwyQkFBMkIsRUFBRSxZQUFZLEVBQVksTUFBTSxtQkFBbUIsQ0FBQTtBQUV6SSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0MsT0FBTyxLQUFLLEdBQUcsTUFBTSxZQUFZLENBQUE7QUFDakMsT0FBTyxLQUFLLFdBQVcsTUFBTSx3Q0FBd0MsQ0FBQTtBQUNyRSxPQUFPLEVBQUUsUUFBUSxFQUFvQixVQUFVLEVBQUUsTUFBTSxLQUFLLENBQUE7QUFFNUQsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLENBQUMsQ0FBNkMsRUFBRSxFQUFFO0lBQzNFLE1BQU0sR0FBRyxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUMzQixNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDL0MsNkZBQTZGO0lBQzdGLG9GQUFvRjtJQUNwRixNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMs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
188
+ /**
189
+ * Like `makeMutation`, but for stream-type request handlers.
190
+ * Returns a `[ref, execute]` tuple where `ref` is a reactive `AsyncResult` updated per
191
+ * stream element. Queries are invalidated once when the stream finishes, regardless of
192
+ * success or failure.
193
+ *
194
+ * Must be called inside a Vue setup context (uses `useQueryClient` internally).
195
+ */
196
+ export const makeStreamMutation = () => {
197
+ const queryClient = useQueryClient();
198
+ return (self, mergedInvalidation) => {
199
+ const state = shallowRef(AsyncResult.initial());
200
+ const runStream = (stream, input) => {
201
+ const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation);
202
+ return Effect
203
+ .sync(() => {
204
+ state.value = AsyncResult.initial(true);
205
+ })
206
+ .pipe(Effect.andThen(stream.pipe(Stream.runForEach((value) => Effect.sync(() => {
207
+ state.value = AsyncResult.success(value, { waiting: true });
208
+ })), Effect.exit, Effect.tap((exit) => Effect.sync(() => {
209
+ if (exit._tag === "Success") {
210
+ const current = state.value;
211
+ if (AsyncResult.isSuccess(current)) {
212
+ state.value = AsyncResult.success(current.value, { waiting: false });
213
+ }
214
+ else {
215
+ state.value = AsyncResult.initial(false);
216
+ }
217
+ }
218
+ else {
219
+ state.value = AsyncResult.failure(exit.cause);
220
+ }
221
+ })), Effect.tap((exit) => {
222
+ const current = state.value;
223
+ const lastValue = AsyncResult.isSuccess(current) ? current.value : undefined;
224
+ const invExit = exit._tag === "Success" ? Exit.succeed(lastValue) : exit;
225
+ return invCache(input, invExit, []);
226
+ }), Effect.asVoid)));
227
+ };
228
+ const handler = self.handler;
229
+ const act = Stream.isStream(handler)
230
+ ? runStream(handler)
231
+ : (i) => runStream(handler(i), i);
232
+ return tuple(computed(() => state.value), act);
233
+ };
234
+ };
235
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXV0YXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL211dGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2pELE9BQU8sRUFBeUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0gsT0FBTyxFQUFjLE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sWUFBWSxDQUFBO0FBQzdELE9BQU8sRUFBd0IsMEJBQTBCLEVBQUUsMkJBQTJCLEVBQUUsWUFBWSxFQUFZLE1BQU0sbUJBQW1CLENBQUE7QUFFekksT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxHQUFHLE1BQU0sWUFBWSxDQUFBO0FBQ2pDLE9BQU8sS0FBSyxNQUFNLE1BQU0sZUFBZSxDQUFBO0FBQ3ZDLE9BQU8sS0FBSyxXQUFXLE1BQU0sd0NBQXdDLENBQUE7QUFDckUsT0FBTyxFQUFFLFFBQVEsRUFBb0IsVUFBVSxFQUFFLE1BQU0sS0FBSyxDQUFBO0FBRTVELE1BQU0sQ0FBQyxNQUFNLFdBQVcsR0FBRyxDQUFDLENBQTZDLEVBQUUsRUFBRTtJQUMzRSxNQUFNLEdBQUcsR0FBRyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDM0IsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBQy9DLDZGQUE2RjtJQUM3RixvRkFBb0Y7SUFDcEYsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxNQUFN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
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Example: stream-based mutation for a long-running operation.
3
+ *
4
+ * The server streams a tagged union of progress updates and a final result.
5
+ * The Vue ref is updated for every emitted value; `AsyncResult` stays in the
6
+ * `waiting` state until the stream ends.
7
+ *
8
+ * When using `makeClient` / `clientFor`, stream-type requests are exposed as
9
+ * `mutateStream` on the client object (and as `XxxStream` in `.helpers`).
10
+ * The example below shows both the low-level `asStreamResult` API and how the
11
+ * same functionality appears on the generated client.
12
+ */
13
+ import { Effect, S, Stream } from "effect-app"
14
+ import { asStreamResult } from "../src/mutate.js"
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Domain model
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /** Intermediate progress report, e.g. "5 of 400 items processed". */
21
+ export class OperationProgress extends S.TaggedClass<OperationProgress>()("OperationProgress", {
22
+ completed: S.NonNegativeInt,
23
+ total: S.NonNegativeInt
24
+ }) {}
25
+
26
+ /** The final result produced once the operation is complete. */
27
+ export class ExportComplete extends S.TaggedClass<ExportComplete>()("ExportComplete", {
28
+ fileUrl: S.NonEmptyString
29
+ }) {}
30
+
31
+ /** Tagged union emitted by the stream. */
32
+ export type ExportEvent = OperationProgress | ExportComplete
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Simulated stream (replace with a real RPC / SSE stream in production)
36
+ // ---------------------------------------------------------------------------
37
+
38
+ /**
39
+ * Produces `total` progress updates followed by a single `ExportComplete`.
40
+ * Each step is separated by a 50 ms delay to simulate real async work.
41
+ */
42
+ const makeExportStream = (total: S.NonNegativeInt): Stream.Stream<ExportEvent> =>
43
+ Stream.concat(
44
+ Stream.range(1, total).pipe(
45
+ Stream.map((completed) => new OperationProgress({ completed: S.NonNegativeInt(completed), total })),
46
+ Stream.tap(() => Effect.sleep("50 millis"))
47
+ ),
48
+ Stream.make(new ExportComplete({ fileUrl: S.NonEmptyString("https://example.com/export.csv") }))
49
+ )
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Option A: low-level `asStreamResult` (call inside a `setup()` function)
53
+ // ---------------------------------------------------------------------------
54
+
55
+ export const useExportMutation = () => {
56
+ /**
57
+ * `result` - reactive ref, always reflects the latest stream event.
58
+ * `AsyncResult` tag:
59
+ * - Initial (waiting=true) - operation in progress
60
+ * - Success (waiting=true) - progress update received, still running
61
+ * - Success (waiting=false) - final result, operation complete
62
+ * - Failure - operation failed
63
+ *
64
+ * `execute` - call with the desired `total` to kick off the stream.
65
+ */
66
+ const [result, execute] = asStreamResult((total: S.NonNegativeInt) => makeExportStream(total))
67
+
68
+ return { result, execute }
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Option B: via `makeClient` / `clientFor` (stream requests -> `mutateStream`)
73
+ //
74
+ // When a request schema has `type: "stream"`, `clientFor` exposes:
75
+ //
76
+ // client.exportData.mutateStream
77
+ // // -> [ComputedRef<AsyncResult<ExportEvent, E>>, (input: I) => Effect<void, never, R>]
78
+ //
79
+ // which is equivalent to calling `asStreamResult(client.exportData.handler)`.
80
+ //
81
+ // The `.helpers` object also includes `exportDataStream` (the camelCase key
82
+ // plus "Stream" suffix) with the same [ref, execute] tuple.
83
+ // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/vue",
3
- "version": "4.0.0-beta.177",
3
+ "version": "4.0.0-beta.178",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/effect-ts-app/libs/tree/main/packages/vue",
@@ -11,7 +11,7 @@
11
11
  "@vueuse/core": "^14.2.1",
12
12
  "change-case": "^5.4.4",
13
13
  "query-string": "^9.3.1",
14
- "effect-app": "4.0.0-beta.177"
14
+ "effect-app": "4.0.0-beta.178"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@effect/atom-vue": "^4.0.0-beta.59",
package/src/makeClient.ts CHANGED
@@ -3,7 +3,7 @@ import { type InvalidateOptions, type InvalidateQueryFilters, isCancelledError,
3
3
  import { camelCase } from "change-case"
4
4
  import { type Context, Effect, Exit, Hash, type Layer, type ManagedRuntime, S, Struct } from "effect-app"
5
5
  import { type ApiClientFactory, type Req } from "effect-app/client"
6
- import type { ExtractModuleName, RequestHandler, RequestHandlers, RequestHandlerWithInput, RequestInputFromMake, RequestsAny } from "effect-app/client/clientFor"
6
+ import type { ExtractModuleName, RequestHandler, RequestHandlers, RequestHandlerWithInput, RequestInputFromMake, RequestsAny, RequestStreamHandler, RequestStreamHandlerWithInput } from "effect-app/client/clientFor"
7
7
  import type { InvalidationCallback } from "effect-app/client/makeClient"
8
8
  import type * as ExitResult from "effect/Exit"
9
9
  import { type Fiber } from "effect/Fiber"
@@ -12,7 +12,7 @@ import { type ComputedRef, onBeforeUnmount, ref, type WatchSource } from "vue"
12
12
  import { type Commander, CommanderStatic } from "./commander.js"
13
13
  import { type I18n } from "./intl.js"
14
14
  import { type CommanderResolved, makeUseCommand } from "./makeUseCommand.js"
15
- import { makeMutation, type MutationOptionsBase, useMakeMutation } from "./mutate.js"
15
+ import { makeMutation, makeStreamMutation, type MutationOptionsBase, useMakeMutation } from "./mutate.js"
16
16
  import { type CustomUndefinedInitialQueryOptions, makeQuery } from "./query.js"
17
17
  import { makeRunPromise } from "./runtime.js"
18
18
  import { type Toast } from "./toast.js"
@@ -125,6 +125,13 @@ type CommandHandler<Req> = Req extends
125
125
  ? Request["type"] extends "command" ? RequestHandler<A, E, R, Request, Id> : never
126
126
  : never
127
127
 
128
+ type StreamHandler<Req> = Req extends
129
+ RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
130
+ ? Request["type"] extends "stream" ? RequestStreamHandlerWithInput<I, A, E, R, Request, Id> : never
131
+ : Req extends RequestStreamHandler<infer A, infer E, infer R, infer Request, infer Id>
132
+ ? Request["type"] extends "stream" ? RequestStreamHandler<A, E, R, Request, Id> : never
133
+ : never
134
+
128
135
  export interface MutationExtensions<RT, Id extends string, I, A, E, R> {
129
136
  /** Defines a Command based on this mutation, taking the `id` of the mutation as the `id` of the Command.
130
137
  * The Mutation function will be taken as the first member of the Command, the Command required input will be the Mutation input.
@@ -217,6 +224,17 @@ export type MutationWithExtensions<RT, Req> = Req extends
217
224
  ? MutationExt<RT, Id, A, E, R, S.Codec.Encoded<Request["success"]>>
218
225
  : never
219
226
 
227
+ /**
228
+ * The `mutateStream` tuple for a stream-type request handler:
229
+ * `[resultRef, execute]` where `execute` updates the ref live with each emitted value.
230
+ */
231
+ export type StreamMutationWithExtensions<Req> = Req extends
232
+ RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer _Id>
233
+ ? readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, (input: I) => Effect.Effect<void, never, R>]
234
+ : Req extends RequestStreamHandler<infer A, infer E, infer R, infer _Request, infer _Id>
235
+ ? readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, Effect.Effect<void, never, R>]
236
+ : never
237
+
220
238
  // we don't really care about the RT, as we are in charge of ensuring runtime safety anyway
221
239
  // eslint-disable-next-line unused-imports/no-unused-vars
222
240
  declare const useQuery_: QueryImpl<any>["useQuery"]
@@ -571,6 +589,9 @@ export const makeClient = <RT_, RTHooks>(
571
589
  let m: ReturnType<typeof useMutationInt>
572
590
  const useMutation = () => m ??= useMutationInt()
573
591
 
592
+ let sm: ReturnType<typeof makeStreamMutation>
593
+ const useStreamMutation = () => sm ??= makeStreamMutation()
594
+
574
595
  const query = new QueryImpl(getBaseRt)
575
596
  const useQuery = query.useQuery
576
597
  const useSuspenseQuery = query.useSuspenseQuery
@@ -739,6 +760,43 @@ export const makeClient = <RT_, RTHooks>(
739
760
  return mutations
740
761
  }
741
762
 
763
+ const mapStreamMutation = <M extends RequestsAny>(
764
+ client: ClientFrom<M>,
765
+ queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
766
+ invalidationResources?: InvalidationResourcesFor<M>
767
+ ) => {
768
+ const streamMutation = useStreamMutation()
769
+ const invalidation = queryInvalidation?.(client)
770
+ const queryResources = makeQueryResources(invalidationResources)
771
+ const streams = Struct.keys(client).reduce(
772
+ (acc, key) => {
773
+ if (client[key].Request.type !== "stream") {
774
+ return acc
775
+ }
776
+ const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
777
+ | InvalidationCallback<InvalidationResourcesFor<M>>
778
+ | undefined
779
+ const fromRequest = fromRequestConfig
780
+ ? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
781
+ fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((entry) => ({
782
+ filters: entry.filters,
783
+ options: entry.options
784
+ })))
785
+ : undefined
786
+ const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
787
+ ;(acc as any)[camelCase(key) + "Stream"] = streamMutation(client[key] as any, mergedInvalidation)
788
+ return acc
789
+ },
790
+ {} as {
791
+ [
792
+ Key in keyof typeof client as StreamHandler<typeof client[Key]> extends never ? never
793
+ : `${ToCamel<string & Key>}Stream`
794
+ ]: StreamMutationWithExtensions<StreamHandler<typeof client[Key]>>
795
+ }
796
+ )
797
+ return streams
798
+ }
799
+
742
800
  // make available .query, .suspense and .mutate for each operation
743
801
  // and a .helpers with all mutations and queries
744
802
  const mapClient = <M extends RequestsAny>(
@@ -750,6 +808,7 @@ export const makeClient = <RT_, RTHooks>(
750
808
  ) => {
751
809
  const Command = useCommand()
752
810
  const mutation = useMutation()
811
+ const streamMutation = useStreamMutation()
753
812
  const invalidation = queryInvalidation?.(client)
754
813
  const queryResources = makeQueryResources(invalidationResources)
755
814
  const extended = Struct.keys(client).reduce(
@@ -786,6 +845,27 @@ export const makeClient = <RT_, RTHooks>(
786
845
  }
787
846
  }
788
847
  }
848
+ : requestType === "stream"
849
+ ? (() => {
850
+ const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
851
+ | InvalidationCallback<InvalidationResourcesFor<M>>
852
+ | undefined
853
+ const fromRequest = fromRequestConfig
854
+ ? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
855
+ fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((
856
+ entry
857
+ ) => ({
858
+ filters: entry.filters,
859
+ options: entry.options
860
+ })))
861
+ : undefined
862
+ const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
863
+ return {
864
+ ...client[key],
865
+ request: h_,
866
+ mutateStream: streamMutation(client[key] as any, mergedInvalidation)
867
+ }
868
+ })()
789
869
  : {
790
870
  mutate: ((handler: any) => {
791
871
  const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
@@ -844,6 +924,8 @@ export const makeClient = <RT_, RTHooks>(
844
924
  : CommandRequestWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>>)
845
925
  & (CommandHandler<typeof client[Key]> extends never ? {}
846
926
  : { mutate: MutationWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>> })
927
+ & (StreamHandler<typeof client[Key]> extends never ? {}
928
+ : { mutateStream: StreamMutationWithExtensions<StreamHandler<typeof client[Key]>> })
847
929
  & { Input: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I : never }
848
930
  }
849
931
  )
@@ -851,6 +933,7 @@ export const makeClient = <RT_, RTHooks>(
851
933
  helpers: {
852
934
  ...mapRequest(client),
853
935
  ...mapMutation(client, queryInvalidation, invalidationResources),
936
+ ...mapStreamMutation(client, queryInvalidation, invalidationResources),
854
937
  ...mapQuery(client)
855
938
  }
856
939
  })
package/src/mutate.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { matchQuery } from "@tanstack/query-core"
3
3
  import { type InvalidateOptions, type InvalidateQueryFilters, type QueryClient, useQueryClient } from "@tanstack/vue-query"
4
- import { type Cause, Effect, type Exit, Option } from "effect-app"
4
+ import { type Cause, Effect, Exit, Option } from "effect-app"
5
5
  import { type InvalidationKey, InvalidationKeysFromServer, makeInvalidationKeysService, makeQueryKey, type Req } from "effect-app/client"
6
6
  import type { ClientForOptions, RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
7
7
  import { tuple } from "effect-app/Function"
8
8
  import * as Ref from "effect/Ref"
9
+ import * as Stream from "effect/Stream"
9
10
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
10
11
  import { computed, type ComputedRef, shallowRef } from "vue"
11
12
 
@@ -137,17 +138,73 @@ export const asResult: {
137
138
  return tuple(computed(() => state.value), act) as any
138
139
  }
139
140
 
140
- export const invalidateQueries = (
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 = (
141
198
  queryClient: QueryClient,
142
199
  self: { id: string; options?: ClientForOptions },
143
- options?: MutationOptionsBase
200
+ queryInvalidation?: MutationOptionsBase["queryInvalidation"]
144
201
  ) => {
145
202
  type InvalidationTarget = {
146
203
  readonly filters: InvalidateQueryFilters | undefined
147
204
  readonly options: InvalidateOptions | undefined
148
205
  }
149
206
 
150
- const invalidateQueries = (
207
+ const invalidateQueriesFn = (
151
208
  filters?: InvalidateQueryFilters,
152
209
  options?: InvalidateOptions
153
210
  ) =>
@@ -164,8 +221,8 @@ export const invalidateQueries = (
164
221
  ): ReadonlyArray<InvalidationTarget> => {
165
222
  const queryKey = getQueryKey(self)
166
223
 
167
- if (options?.queryInvalidation) {
168
- return options.queryInvalidation(queryKey, self.id, input, output).map((_) => ({
224
+ if (queryInvalidation) {
225
+ return queryInvalidation(queryKey, self.id, input, output).map((_) => ({
169
226
  filters: _.filters,
170
227
  options: _.options
171
228
  }))
@@ -219,7 +276,7 @@ export const invalidateQueries = (
219
276
  Effect.forEach(
220
277
  groups.values(),
221
278
  ({ options, refetchType, targets }) =>
222
- invalidateQueries(
279
+ invalidateQueriesFn(
223
280
  {
224
281
  ...(refetchType !== undefined ? { refetchType } : {}),
225
282
  predicate: (query) => targets.some((t) => t.filters ? matchQuery(t.filters, query) : true)
@@ -239,6 +296,16 @@ export const invalidateQueries = (
239
296
  )
240
297
  })
241
298
 
299
+ return invalidateCache
300
+ }
301
+
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)
308
+
242
309
  const select = options?.select
243
310
 
244
311
  const handle = <A, E, R>(eff: Effect.Effect<A, E, R>, input?: unknown) =>
@@ -345,3 +412,74 @@ export const useMakeMutation = () => {
345
412
  }
346
413
  return useMutation
347
414
  }
415
+
416
+ /**
417
+ * Like `makeMutation`, but for stream-type request handlers.
418
+ * Returns a `[ref, execute]` tuple where `ref` is a reactive `AsyncResult` updated per
419
+ * stream element. Queries are invalidated once when the stream finishes, regardless of
420
+ * success or failure.
421
+ *
422
+ * Must be called inside a Vue setup context (uses `useQueryClient` internally).
423
+ */
424
+ export const makeStreamMutation = () => {
425
+ const queryClient = useQueryClient()
426
+
427
+ return (
428
+ self: {
429
+ id: string
430
+ options?: ClientForOptions
431
+ handler: Stream.Stream<any, any, any> | ((i: any) => Stream.Stream<any, any, any>)
432
+ },
433
+ mergedInvalidation?: MutationOptionsBase["queryInvalidation"]
434
+ ) => {
435
+ const state = shallowRef<AsyncResult.AsyncResult<any, any>>(AsyncResult.initial())
436
+
437
+ const runStream = (stream: Stream.Stream<any, any, any>, input?: unknown): Effect.Effect<void, never, any> => {
438
+ const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation)
439
+ return Effect
440
+ .sync(() => {
441
+ state.value = AsyncResult.initial(true)
442
+ })
443
+ .pipe(
444
+ Effect.andThen(
445
+ stream.pipe(
446
+ Stream.runForEach((value) =>
447
+ Effect.sync(() => {
448
+ state.value = AsyncResult.success(value, { waiting: true })
449
+ })
450
+ ),
451
+ Effect.exit,
452
+ Effect.tap((exit) =>
453
+ Effect.sync(() => {
454
+ if (exit._tag === "Success") {
455
+ const current = state.value
456
+ if (AsyncResult.isSuccess(current)) {
457
+ state.value = AsyncResult.success(current.value, { waiting: false })
458
+ } else {
459
+ state.value = AsyncResult.initial(false)
460
+ }
461
+ } else {
462
+ state.value = AsyncResult.failure(exit.cause)
463
+ }
464
+ })
465
+ ),
466
+ Effect.tap((exit) => {
467
+ const current = state.value
468
+ const lastValue = AsyncResult.isSuccess(current) ? current.value : undefined
469
+ const invExit = exit._tag === "Success" ? Exit.succeed(lastValue) : exit
470
+ return invCache(input, invExit, [])
471
+ }),
472
+ Effect.asVoid
473
+ )
474
+ )
475
+ )
476
+ }
477
+
478
+ const handler = self.handler
479
+ const act = Stream.isStream(handler)
480
+ ? runStream(handler)
481
+ : (i: any) => runStream((handler as (i: any) => Stream.Stream<any, any, any>)(i), i)
482
+
483
+ return tuple(computed(() => state.value), act)
484
+ }
485
+ }