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

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,58 @@ 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
+ * When the request declares a `final` schema, `execute` resolves with the last emitted value
195
+ * typed as `Final`; otherwise it resolves with `void`.
196
+ *
197
+ * Must be called inside a Vue setup context (uses `useQueryClient` internally).
198
+ */
199
+ export const makeStreamMutation = () => {
200
+ const queryClient = useQueryClient();
201
+ return (self, mergedInvalidation) => {
202
+ const state = shallowRef(AsyncResult.initial());
203
+ const runStream = (stream, input) => {
204
+ const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation);
205
+ return Effect
206
+ .sync(() => {
207
+ state.value = AsyncResult.initial(true);
208
+ })
209
+ .pipe(Effect.andThen(stream.pipe(Stream.runForEach((value) => Effect.sync(() => {
210
+ state.value = AsyncResult.success(value, { waiting: true });
211
+ })), Effect.exit, Effect.tap((exit) => Effect.sync(() => {
212
+ if (exit._tag === "Success") {
213
+ const current = state.value;
214
+ if (AsyncResult.isSuccess(current)) {
215
+ state.value = AsyncResult.success(current.value, { waiting: false });
216
+ }
217
+ else {
218
+ state.value = AsyncResult.initial(false);
219
+ }
220
+ }
221
+ else {
222
+ state.value = AsyncResult.failure(exit.cause);
223
+ }
224
+ })), Effect.flatMap((exit) => {
225
+ const current = state.value;
226
+ const lastValue = AsyncResult.isSuccess(current) ? current.value : undefined;
227
+ const invExit = exit._tag === "Success" ? Exit.succeed(lastValue) : exit;
228
+ // Note: when the stream fails, `lastValue` is undefined. The failure is
229
+ // communicated via the reactive `state` ref (AsyncResult.failure). The
230
+ // execute effect always resolves successfully; callers should inspect the
231
+ // ref to distinguish success from failure.
232
+ return invCache(input, invExit, []).pipe(Effect.as(lastValue));
233
+ }))));
234
+ };
235
+ const handler = self.handler;
236
+ const act = Stream.isStream(handler)
237
+ ? runStream(handler)
238
+ : (i) => runStream(handler(i), i);
239
+ return tuple(computed(() => state.value), act);
240
+ };
241
+ };
242
+ //# 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.179",
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.179"
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, infer Final>
130
+ ? Request["type"] extends "stream" ? RequestStreamHandlerWithInput<I, A, E, R, Request, Id, Final> : never
131
+ : Req extends RequestStreamHandler<infer A, infer E, infer R, infer Request, infer Id, infer Final>
132
+ ? Request["type"] extends "stream" ? RequestStreamHandler<A, E, R, Request, Id, Final> : 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,19 @@ 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
+ * When the request declares a `final` schema, `execute` resolves with the last emitted value
231
+ * typed as `Final`; otherwise it resolves with `void`.
232
+ */
233
+ export type StreamMutationWithExtensions<Req> = Req extends
234
+ RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer _Id, infer Final>
235
+ ? readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, (input: I) => Effect.Effect<Final, never, R>]
236
+ : Req extends RequestStreamHandler<infer A, infer E, infer R, infer _Request, infer _Id, infer Final>
237
+ ? readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, Effect.Effect<Final, never, R>]
238
+ : never
239
+
220
240
  // we don't really care about the RT, as we are in charge of ensuring runtime safety anyway
221
241
  // eslint-disable-next-line unused-imports/no-unused-vars
222
242
  declare const useQuery_: QueryImpl<any>["useQuery"]
@@ -571,6 +591,9 @@ export const makeClient = <RT_, RTHooks>(
571
591
  let m: ReturnType<typeof useMutationInt>
572
592
  const useMutation = () => m ??= useMutationInt()
573
593
 
594
+ let sm: ReturnType<typeof makeStreamMutation>
595
+ const useStreamMutation = () => sm ??= makeStreamMutation()
596
+
574
597
  const query = new QueryImpl(getBaseRt)
575
598
  const useQuery = query.useQuery
576
599
  const useSuspenseQuery = query.useSuspenseQuery
@@ -739,6 +762,43 @@ export const makeClient = <RT_, RTHooks>(
739
762
  return mutations
740
763
  }
741
764
 
765
+ const mapStreamMutation = <M extends RequestsAny>(
766
+ client: ClientFrom<M>,
767
+ queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
768
+ invalidationResources?: InvalidationResourcesFor<M>
769
+ ) => {
770
+ const streamMutation = useStreamMutation()
771
+ const invalidation = queryInvalidation?.(client)
772
+ const queryResources = makeQueryResources(invalidationResources)
773
+ const streams = Struct.keys(client).reduce(
774
+ (acc, key) => {
775
+ if (client[key].Request.type !== "stream") {
776
+ return acc
777
+ }
778
+ const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
779
+ | InvalidationCallback<InvalidationResourcesFor<M>>
780
+ | undefined
781
+ const fromRequest = fromRequestConfig
782
+ ? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
783
+ fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((entry) => ({
784
+ filters: entry.filters,
785
+ options: entry.options
786
+ })))
787
+ : undefined
788
+ const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
789
+ ;(acc as any)[camelCase(key) + "Stream"] = streamMutation(client[key] as any, mergedInvalidation)
790
+ return acc
791
+ },
792
+ {} as {
793
+ [
794
+ Key in keyof typeof client as StreamHandler<typeof client[Key]> extends never ? never
795
+ : `${ToCamel<string & Key>}Stream`
796
+ ]: StreamMutationWithExtensions<StreamHandler<typeof client[Key]>>
797
+ }
798
+ )
799
+ return streams
800
+ }
801
+
742
802
  // make available .query, .suspense and .mutate for each operation
743
803
  // and a .helpers with all mutations and queries
744
804
  const mapClient = <M extends RequestsAny>(
@@ -750,6 +810,7 @@ export const makeClient = <RT_, RTHooks>(
750
810
  ) => {
751
811
  const Command = useCommand()
752
812
  const mutation = useMutation()
813
+ const streamMutation = useStreamMutation()
753
814
  const invalidation = queryInvalidation?.(client)
754
815
  const queryResources = makeQueryResources(invalidationResources)
755
816
  const extended = Struct.keys(client).reduce(
@@ -786,6 +847,27 @@ export const makeClient = <RT_, RTHooks>(
786
847
  }
787
848
  }
788
849
  }
850
+ : requestType === "stream"
851
+ ? (() => {
852
+ const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
853
+ | InvalidationCallback<InvalidationResourcesFor<M>>
854
+ | undefined
855
+ const fromRequest = fromRequestConfig
856
+ ? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
857
+ fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((
858
+ entry
859
+ ) => ({
860
+ filters: entry.filters,
861
+ options: entry.options
862
+ })))
863
+ : undefined
864
+ const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
865
+ return {
866
+ ...client[key],
867
+ request: h_,
868
+ mutateStream: streamMutation(client[key] as any, mergedInvalidation)
869
+ }
870
+ })()
789
871
  : {
790
872
  mutate: ((handler: any) => {
791
873
  const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
@@ -844,6 +926,8 @@ export const makeClient = <RT_, RTHooks>(
844
926
  : CommandRequestWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>>)
845
927
  & (CommandHandler<typeof client[Key]> extends never ? {}
846
928
  : { mutate: MutationWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>> })
929
+ & (StreamHandler<typeof client[Key]> extends never ? {}
930
+ : { mutateStream: StreamMutationWithExtensions<StreamHandler<typeof client[Key]>> })
847
931
  & { Input: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I : never }
848
932
  }
849
933
  )
@@ -851,6 +935,7 @@ export const makeClient = <RT_, RTHooks>(
851
935
  helpers: {
852
936
  ...mapRequest(client),
853
937
  ...mapMutation(client, queryInvalidation, invalidationResources),
938
+ ...mapStreamMutation(client, queryInvalidation, invalidationResources),
854
939
  ...mapQuery(client)
855
940
  }
856
941
  })
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,80 @@ 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
+ * When the request declares a `final` schema, `execute` resolves with the last emitted value
423
+ * typed as `Final`; otherwise it resolves with `void`.
424
+ *
425
+ * Must be called inside a Vue setup context (uses `useQueryClient` internally).
426
+ */
427
+ export const makeStreamMutation = () => {
428
+ const queryClient = useQueryClient()
429
+
430
+ return (
431
+ self: {
432
+ id: string
433
+ options?: ClientForOptions
434
+ handler: Stream.Stream<any, any, any> | ((i: any) => Stream.Stream<any, any, any>)
435
+ },
436
+ mergedInvalidation?: MutationOptionsBase["queryInvalidation"]
437
+ ) => {
438
+ const state = shallowRef<AsyncResult.AsyncResult<any, any>>(AsyncResult.initial())
439
+
440
+ const runStream = (stream: Stream.Stream<any, any, any>, input?: unknown): Effect.Effect<any, never, any> => {
441
+ const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation)
442
+ return Effect
443
+ .sync(() => {
444
+ state.value = AsyncResult.initial(true)
445
+ })
446
+ .pipe(
447
+ Effect.andThen(
448
+ stream.pipe(
449
+ Stream.runForEach((value) =>
450
+ Effect.sync(() => {
451
+ state.value = AsyncResult.success(value, { waiting: true })
452
+ })
453
+ ),
454
+ Effect.exit,
455
+ Effect.tap((exit) =>
456
+ Effect.sync(() => {
457
+ if (exit._tag === "Success") {
458
+ const current = state.value
459
+ if (AsyncResult.isSuccess(current)) {
460
+ state.value = AsyncResult.success(current.value, { waiting: false })
461
+ } else {
462
+ state.value = AsyncResult.initial(false)
463
+ }
464
+ } else {
465
+ state.value = AsyncResult.failure(exit.cause)
466
+ }
467
+ })
468
+ ),
469
+ Effect.flatMap((exit) => {
470
+ const current = state.value
471
+ const lastValue = AsyncResult.isSuccess(current) ? current.value : undefined
472
+ const invExit = exit._tag === "Success" ? Exit.succeed(lastValue) : exit
473
+ // Note: when the stream fails, `lastValue` is undefined. The failure is
474
+ // communicated via the reactive `state` ref (AsyncResult.failure). The
475
+ // execute effect always resolves successfully; callers should inspect the
476
+ // ref to distinguish success from failure.
477
+ return invCache(input, invExit, []).pipe(Effect.as(lastValue))
478
+ })
479
+ )
480
+ )
481
+ )
482
+ }
483
+
484
+ const handler = self.handler
485
+ const act = Stream.isStream(handler)
486
+ ? runStream(handler)
487
+ : (i: any) => runStream((handler as (i: any) => Stream.Stream<any, any, any>)(i), i)
488
+
489
+ return tuple(computed(() => state.value), act)
490
+ }
491
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamFinal.test.d.ts","sourceRoot":"","sources":["../streamFinal.test.ts"],"names":[],"mappings":""}