@effect-app/vue 1.26.1 → 1.26.2

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/_cjs/index.cjs +22 -13
  3. package/_cjs/index.cjs.map +1 -1
  4. package/_cjs/lib.cjs +26 -0
  5. package/_cjs/lib.cjs.map +1 -0
  6. package/_cjs/makeClient.cjs +5 -84
  7. package/_cjs/makeClient.cjs.map +1 -1
  8. package/_cjs/makeClient2.cjs +8 -101
  9. package/_cjs/makeClient2.cjs.map +1 -1
  10. package/_cjs/mutate.cjs +37 -0
  11. package/_cjs/mutate.cjs.map +1 -1
  12. package/_cjs/mutate2.cjs.map +1 -1
  13. package/_cjs/query.cjs +25 -2
  14. package/_cjs/query.cjs.map +1 -1
  15. package/_cjs/query2.cjs +3 -25
  16. package/_cjs/query2.cjs.map +1 -1
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -2
  20. package/dist/lib.d.ts +8 -0
  21. package/dist/lib.d.ts.map +1 -0
  22. package/dist/lib.js +15 -0
  23. package/dist/makeClient.d.ts +1 -23
  24. package/dist/makeClient.d.ts.map +1 -1
  25. package/dist/makeClient.js +3 -68
  26. package/dist/makeClient2.d.ts +19 -73
  27. package/dist/makeClient2.d.ts.map +1 -1
  28. package/dist/makeClient2.js +6 -86
  29. package/dist/mutate.d.ts +6 -0
  30. package/dist/mutate.d.ts.map +1 -1
  31. package/dist/mutate.js +25 -1
  32. package/dist/mutate2.d.ts +4 -8
  33. package/dist/mutate2.d.ts.map +1 -1
  34. package/dist/mutate2.js +1 -1
  35. package/dist/query.d.ts +5 -2
  36. package/dist/query.d.ts.map +1 -1
  37. package/dist/query.js +30 -4
  38. package/dist/query2.d.ts +4 -11
  39. package/dist/query2.d.ts.map +1 -1
  40. package/dist/query2.js +4 -26
  41. package/package.json +14 -4
  42. package/src/index.ts +3 -1
  43. package/src/lib.ts +29 -0
  44. package/src/makeClient.ts +4 -115
  45. package/src/makeClient2.ts +27 -183
  46. package/src/mutate.ts +33 -0
  47. package/src/mutate2.ts +10 -16
  48. package/src/query.ts +47 -7
  49. package/src/query2.ts +10 -56
  50. package/vitest.config.ts.timestamp-1711656440837-d04458a029af.mjs +0 -37
  51. package/vitest.config.ts.timestamp-1711724061890-e8772f088aa0d.mjs +0 -37
  52. package/vitest.config.ts.timestamp-1711743471018-f8eca63460d15.mjs +0 -37
  53. package/vitest.config.ts.timestamp-1711743489536-332f6bf4f074c.mjs +0 -37
  54. package/vitest.config.ts.timestamp-1711744615240-85d4d02f24414.mjs +0 -37
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
- export * as Result from "@effect-rx/rx/Result"
1
+ export * from "./lib.js"
2
+
2
3
  export * from "./hooks.js"
3
4
  export * from "./makeClient.js"
5
+ export * from "./makeClient2.js"
4
6
  export * from "./makeContext.js"
5
7
  export * from "./makeIntl.js"
6
8
  export * from "./runtime.js"
package/src/lib.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { type Pausable, useIntervalFn, type UseIntervalFnOptions } from "@vueuse/core"
2
+ import type { MaybeRefOrGetter } from "vue"
3
+
4
+ export * as Result from "@effect-rx/rx/Result"
5
+
6
+ export function pauseWhileProcessing(
7
+ iv: Pausable,
8
+ pmf: () => Promise<unknown>
9
+ ) {
10
+ return Promise
11
+ .resolve(iv.pause())
12
+ .then(() => pmf())
13
+ .finally(() => iv.resume())
14
+ }
15
+
16
+ export function useIntervalPauseWhileProcessing(
17
+ pmf: () => Promise<unknown>,
18
+ interval?: MaybeRefOrGetter<number>,
19
+ options?: Omit<UseIntervalFnOptions, "immediateCallback">
20
+ ) {
21
+ const iv = useIntervalFn(
22
+ () => pauseWhileProcessing(iv, pmf),
23
+ interval,
24
+ options ? { ...options, immediateCallback: false } : options
25
+ )
26
+ return {
27
+ isActive: iv.isActive
28
+ }
29
+ }
package/src/makeClient.ts CHANGED
@@ -1,18 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { flow, pipe, tuple } from "@effect-app/core/Function"
3
- import type { MutationResult } from "@effect-app/vue"
4
- import { Result } from "@effect-app/vue"
5
3
  import * as Sentry from "@sentry/browser"
6
- import { type MaybeRefOrGetter, type Pausable, useIntervalFn, type UseIntervalFnOptions } from "@vueuse/core"
7
4
  import type { Either } from "effect-app"
8
- import { Array, Cause, Effect, Match, Option, Runtime, S } from "effect-app"
5
+ import { Cause, Effect, Match, Runtime, S } from "effect-app"
9
6
  import { type SupportedErrors } from "effect-app/client"
10
7
  import { Failure, Success } from "effect-app/Operations"
11
8
  import { dropUndefinedT } from "effect-app/utils"
12
- import type { R } from "vitest/dist/chunks/config.Crbj2GAb.js"
13
9
  import { computed, type ComputedRef } from "vue"
14
10
  import type { MakeIntlReturn } from "./makeIntl.js"
15
- import type { MakeMutation, MutationOptions } from "./mutate.js"
11
+ import type { MakeMutation, MutationOptions, Res } from "./mutate.js"
12
+ import { mutationResultToVue } from "./mutate.js"
16
13
 
17
14
  /**
18
15
  * Use this after handling an error yourself, still continueing on the Error track, but the error will not be reported.
@@ -23,31 +20,6 @@ export class SuppressErrors extends Cause.YieldableError {
23
20
 
24
21
  export type ResponseErrors = S.ParseResult.ParseError | SupportedErrors | SuppressErrors
25
22
 
26
- export function pauseWhileProcessing(
27
- iv: Pausable,
28
- pmf: () => Promise<unknown>
29
- ) {
30
- return Promise
31
- .resolve(iv.pause())
32
- .then(() => pmf())
33
- .finally(() => iv.resume())
34
- }
35
-
36
- export function useIntervalPauseWhileProcessing(
37
- pmf: () => Promise<unknown>,
38
- interval?: MaybeRefOrGetter<number>,
39
- options?: Omit<UseIntervalFnOptions, "immediateCallback">
40
- ) {
41
- const iv = useIntervalFn(
42
- () => pauseWhileProcessing(iv, pmf),
43
- interval,
44
- options ? { ...options, immediateCallback: false } : options
45
- )
46
- return {
47
- isActive: iv.isActive
48
- }
49
- }
50
-
51
23
  export interface Opts<A, I = void> extends MutationOptions<A, I> {
52
24
  suppressErrorToast?: boolean
53
25
  suppressSuccessToast?: boolean
@@ -83,7 +55,7 @@ export const withSuccess: {
83
55
  (
84
56
  self.handler as (
85
57
  i: any
86
- ) => Effect<any, any, R>
58
+ ) => Effect<any, any, any>
87
59
  )(i),
88
60
  Effect.flatMap((_) =>
89
61
  Effect.promise(() => onSuccess(_, i)).pipe(
@@ -128,12 +100,6 @@ export const withSuccessE: {
128
100
  }
129
101
  }
130
102
 
131
- export interface Res<A, E> {
132
- readonly loading: boolean
133
- readonly data: A | undefined
134
- readonly error: E | undefined
135
- }
136
-
137
103
  type WithAction<A> = A & {
138
104
  action: string
139
105
  }
@@ -150,33 +116,6 @@ type ActResp<E, A> = readonly [
150
116
  WithAction<() => Promise<void>>
151
117
  ]
152
118
 
153
- export function mutationResultToVue<A, E>(
154
- mutationResult: MutationResult<A, E>
155
- ): Res<A, E> {
156
- switch (mutationResult._tag) {
157
- case "Loading": {
158
- return { loading: true, data: undefined, error: undefined }
159
- }
160
- case "Success": {
161
- return {
162
- loading: false,
163
- data: mutationResult.data,
164
- error: undefined
165
- }
166
- }
167
- case "Error": {
168
- return {
169
- loading: false,
170
- data: undefined,
171
- error: mutationResult.error
172
- }
173
- }
174
- case "Initial": {
175
- return { loading: false, data: undefined, error: undefined }
176
- }
177
- }
178
- }
179
-
180
119
  export const makeClient = <Locale extends string, R>(
181
120
  useIntl: MakeIntlReturn<Locale>["useIntl"],
182
121
  useToast: () => {
@@ -452,25 +391,21 @@ export const mapHandler: {
452
391
  self: {
453
392
  handler: (i: I) => Effect<A, E, R>
454
393
  name: string
455
- mapPath: (i: I) => string
456
394
  },
457
395
  map: (i: I) => (handler: Effect<A, E, R>) => Effect<A2, E2, R2>
458
396
  ): {
459
397
  handler: (i: I) => Effect<A2, E2, R2>
460
398
  name: string
461
- mapPath: (i: I) => string
462
399
  }
463
400
  <E, A, R, E2, A2, R2>(
464
401
  self: {
465
402
  handler: Effect<A, E, R>
466
403
  name: string
467
- mapPath: string
468
404
  },
469
405
  map: (handler: Effect<A, E, R>) => Effect<A2, E2, R2>
470
406
  ): {
471
407
  handler: Effect<A2, E2, R2>
472
408
  name: string
473
- mapPath: string
474
409
  }
475
410
  } = (self: any, map: any): any => ({
476
411
  ...self,
@@ -478,49 +413,3 @@ export const mapHandler: {
478
413
  ? (i: any) => map(i)((self.handler as (i: any) => Effect<any, any, any>)(i))
479
414
  : map(self.handler)
480
415
  })
481
-
482
- export function composeQueries<
483
- R extends Record<string, Result.Result<any, any>>
484
- >(
485
- results: R,
486
- renderPreviousOnFailure?: boolean
487
- ): Result.Result<
488
- {
489
- [Property in keyof R]: R[Property] extends Result.Result<infer A, any> ? A
490
- : never
491
- },
492
- {
493
- [Property in keyof R]: R[Property] extends Result.Result<any, infer E> ? E
494
- : never
495
- }[keyof R]
496
- > {
497
- const values = renderPreviousOnFailure
498
- ? Object.values(results).map(orPrevious)
499
- : Object.values(results)
500
- const error = values.find(Result.isFailure)
501
- if (error) {
502
- return error
503
- }
504
- const initial = Array.findFirst(values, (x) => x._tag === "Initial" ? Option.some(x) : Option.none())
505
- if (initial.value !== undefined) {
506
- return initial.value
507
- }
508
- const loading = Array.findFirst(values, (x) => Result.isInitial(x) && x.waiting ? Option.some(x) : Option.none())
509
- if (loading.value !== undefined) {
510
- return loading.value
511
- }
512
-
513
- const isRefreshing = values.some((x) => x.waiting)
514
-
515
- const r = Object.entries(results).reduce((prev, [key, value]) => {
516
- prev[key] = Result.value(value).value
517
- return prev
518
- }, {} as any)
519
- return Result.success(r, isRefreshing)
520
- }
521
-
522
- function orPrevious<E, A>(result: Result.Result<A, E>) {
523
- return Result.isFailure(result) && Option.isSome(result.previousValue)
524
- ? Result.success(result.previousValue.value, result.waiting)
525
- : result
526
- }
@@ -1,62 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { flow, pipe, tuple } from "@effect-app/core/Function"
3
- import type { MutationResult } from "@effect-app/vue"
4
- import { Result } from "@effect-app/vue"
5
3
  import * as Sentry from "@sentry/browser"
6
- import { type MaybeRefOrGetter, type Pausable, useIntervalFn, type UseIntervalFnOptions } from "@vueuse/core"
7
- import { Array, Cause, Effect, Exit, Match, Option, S } from "effect-app"
8
- import { type SupportedErrors } from "effect-app/client"
4
+ import { Cause, Effect, Exit, Match, Option, S } from "effect-app"
9
5
  import { Failure, Success } from "effect-app/Operations"
10
6
  import { dropUndefinedT } from "effect-app/utils"
11
7
  import { computed, type ComputedRef } from "vue"
8
+ import type { Opts, ResponseErrors } from "./makeClient.js"
12
9
  import type { MakeIntlReturn } from "./makeIntl.js"
13
- import type { MakeMutation2, MutationOptions } from "./mutate2.js"
14
-
15
- /**
16
- * Use this after handling an error yourself, still continueing on the Error track, but the error will not be reported.
17
- */
18
- export class SuppressErrors extends Cause.YieldableError {
19
- readonly _tag = "SuppressErrors"
20
- }
21
-
22
- export type ResponseErrors = S.ParseResult.ParseError | SupportedErrors | SuppressErrors
23
-
24
- export function pauseWhileProcessing(
25
- iv: Pausable,
26
- pmf: () => Promise<unknown>
27
- ) {
28
- return Promise
29
- .resolve(iv.pause())
30
- .then(() => pmf())
31
- .finally(() => iv.resume())
32
- }
33
-
34
- export function useIntervalPauseWhileProcessing(
35
- pmf: () => Promise<unknown>,
36
- interval?: MaybeRefOrGetter<number>,
37
- options?: Omit<UseIntervalFnOptions, "immediateCallback">
38
- ) {
39
- const iv = useIntervalFn(
40
- () => pauseWhileProcessing(iv, pmf),
41
- interval,
42
- options ? { ...options, immediateCallback: false } : options
43
- )
44
- return {
45
- isActive: iv.isActive
46
- }
47
- }
48
-
49
- export interface Opts<A> extends MutationOptions {
50
- suppressErrorToast?: boolean
51
- suppressSuccessToast?: boolean
52
- successToast?: (a: A) => any
53
- }
54
-
55
- export interface Res<A, E> {
56
- readonly loading: boolean
57
- readonly data: A | undefined
58
- readonly error: E | undefined
59
- }
10
+ import { mutationResultToVue } from "./mutate.js"
11
+ import type { Res } from "./mutate.js"
12
+ import type { MakeMutation2 } from "./mutate2.js"
60
13
 
61
14
  type WithAction<A> = A & {
62
15
  action: string
@@ -74,33 +27,6 @@ type ActResp<E, A, R> = readonly [
74
27
  WithAction<() => Effect<A, E, R>>
75
28
  ]
76
29
 
77
- export function mutationResultToVue<A, E>(
78
- mutationResult: MutationResult<A, E>
79
- ): Res<A, E> {
80
- switch (mutationResult._tag) {
81
- case "Loading": {
82
- return { loading: true, data: undefined, error: undefined }
83
- }
84
- case "Success": {
85
- return {
86
- loading: false,
87
- data: mutationResult.data,
88
- error: undefined
89
- }
90
- }
91
- case "Error": {
92
- return {
93
- loading: false,
94
- data: undefined,
95
- error: mutationResult.error
96
- }
97
- }
98
- case "Initial": {
99
- return { loading: false, data: undefined, error: undefined }
100
- }
101
- }
102
- }
103
-
104
30
  export const makeClient2 = <Locale extends string, R>(
105
31
  useIntl: MakeIntlReturn<Locale>["useIntl"],
106
32
  useToast: () => {
@@ -271,19 +197,13 @@ export const makeClient2 = <Locale extends string, R>(
271
197
  * Returns a tuple with state ref and execution function which reports errors as Toast.
272
198
  */
273
199
  const useAndHandleMutation: {
274
- <I, E extends ResponseErrors, A, R>(
275
- self: {
276
- handler: (i: I) => Effect<A, E, R>
277
- name: string
278
- },
200
+ <I, E extends ResponseErrors, A, R, Request extends S.TaggedRequest.Any>(
201
+ self: RequestHandlerWithInput<I, A, E, R, Request>,
279
202
  action: string,
280
203
  options?: Opts<A>
281
204
  ): Resp<I, A, E, R>
282
- <E extends ResponseErrors, A, R>(
283
- self: {
284
- handler: Effect<A, E, R>
285
- name: string
286
- },
205
+ <E extends ResponseErrors, A, R, Request extends S.TaggedRequest.Any>(
206
+ self: RequestHandler<A, E, R, Request>,
287
207
  action: string,
288
208
  options?: Opts<A>
289
209
  ): ActResp<E, A, R>
@@ -291,6 +211,7 @@ export const makeClient2 = <Locale extends string, R>(
291
211
  const handleRequestWithToast = useHandleRequestWithToast()
292
212
  const [a, b] = useSafeMutation(
293
213
  {
214
+ ...self,
294
215
  handler: Effect.isEffect(self.handler)
295
216
  ? (pipe(
296
217
  Effect.annotateCurrentSpan({ action }),
@@ -300,8 +221,7 @@ export const makeClient2 = <Locale extends string, R>(
300
221
  pipe(
301
222
  Effect.annotateCurrentSpan({ action }),
302
223
  Effect.andThen(self.handler(...args))
303
- ),
304
- name: self.name
224
+ )
305
225
  },
306
226
  dropUndefinedT({
307
227
  queryInvalidation: options?.queryInvalidation
@@ -319,37 +239,27 @@ export const makeClient2 = <Locale extends string, R>(
319
239
  ) {
320
240
  return ((self: any, action: any, options: any) => {
321
241
  return useAndHandleMutation(
322
- {
323
- handler: self.handler,
324
- name: self.name
325
- },
242
+ self,
326
243
  action,
327
244
  { ...defaultOptions, ...options }
328
245
  )
329
246
  }) as {
330
- <I, E extends ResponseErrors, A, R>(
331
- self: {
332
- handler: (i: I) => Effect<A, E, R>
333
- name: string
334
- },
247
+ <I, E extends ResponseErrors, A, R, Request extends S.TaggedRequest.Any>(
248
+ self: RequestHandlerWithInput<I, A, E, R, Request>,
335
249
  action: string,
336
250
  options?: Opts<A>
337
251
  ): Resp<I, A, E, R>
338
- <E extends ResponseErrors, A>(
339
- self: {
340
- handler: Effect<A, E, R>
341
- name: string
342
- },
252
+ <E extends ResponseErrors, A, Request extends S.TaggedRequest.Any>(
253
+ self: RequestHandler<A, E, R, Request>,
343
254
  action: string,
344
255
  options?: Opts<A>
345
256
  ): ActResp<E, A, R>
346
257
  }
347
258
  }
348
259
 
349
- const useSafeMutationWithState = <I, E, A>(self: {
350
- handler: (i: I) => Effect<A, E, R>
351
- name: string
352
- }) => {
260
+ const useSafeMutationWithState = <I, E, A, Request extends S.TaggedRequest.Any>(
261
+ self: RequestHandlerWithInput<I, A, E, R, Request>
262
+ ) => {
353
263
  const [a, b] = useSafeMutation(self)
354
264
 
355
265
  return tuple(
@@ -366,80 +276,14 @@ export const makeClient2 = <Locale extends string, R>(
366
276
  }
367
277
  }
368
278
 
369
- export const mapHandler: {
370
- <I, E, R, A, E2, A2, R2>(
371
- self: {
372
- handler: (i: I) => Effect<A, E, R>
373
- name: string
374
- mapPath: (i: I) => string
375
- },
376
- map: (i: I) => (handler: Effect<A, E, R>) => Effect<A2, E2, R2>
377
- ): {
378
- handler: (i: I) => Effect<A2, E2, R2>
379
- name: string
380
- mapPath: (i: I) => string
381
- }
382
- <E, A, R, E2, A2, R2>(
383
- self: {
384
- handler: Effect<A, E, R>
385
- name: string
386
- mapPath: string
387
- },
388
- map: (handler: Effect<A, E, R>) => Effect<A2, E2, R2>
389
- ): {
390
- handler: Effect<A2, E2, R2>
391
- name: string
392
- mapPath: string
393
- }
394
- } = (self: any, map: any): any => ({
395
- ...self,
396
- handler: typeof self.handler === "function"
397
- ? (i: any) => map(i)((self.handler as (i: any) => Effect<any, any, any>)(i))
398
- : map(self.handler)
399
- })
400
-
401
- export function composeQueries<
402
- R extends Record<string, Result.Result<any, any>>
403
- >(
404
- results: R,
405
- renderPreviousOnFailure?: boolean
406
- ): Result.Result<
407
- {
408
- [Property in keyof R]: R[Property] extends Result.Result<infer A, any> ? A
409
- : never
410
- },
411
- {
412
- [Property in keyof R]: R[Property] extends Result.Result<any, infer E> ? E
413
- : never
414
- }[keyof R]
415
- > {
416
- const values = renderPreviousOnFailure
417
- ? Object.values(results).map(orPrevious)
418
- : Object.values(results)
419
- const error = values.find(Result.isFailure)
420
- if (error) {
421
- return error
422
- }
423
- const initial = Array.findFirst(values, (x) => x._tag === "Initial" ? Option.some(x) : Option.none())
424
- if (initial.value !== undefined) {
425
- return initial.value
426
- }
427
- const loading = Array.findFirst(values, (x) => Result.isInitial(x) && x.waiting ? Option.some(x) : Option.none())
428
- if (loading.value !== undefined) {
429
- return loading.value
430
- }
431
-
432
- const isRefreshing = values.some((x) => x.waiting)
433
-
434
- const r = Object.entries(results).reduce((prev, [key, value]) => {
435
- prev[key] = Result.value(value).value
436
- return prev
437
- }, {} as any)
438
- return Result.success(r, isRefreshing)
279
+ export interface RequestHandler<A, E, R, Request extends S.TaggedRequest.Any> {
280
+ handler: Effect<A, E, R>
281
+ name: string
282
+ Request: Request
439
283
  }
440
284
 
441
- function orPrevious<E, A>(result: Result.Result<A, E>) {
442
- return Result.isFailure(result) && Option.isSome(result.previousValue)
443
- ? Result.success(result.previousValue.value, result.waiting)
444
- : result
285
+ export interface RequestHandlerWithInput<I, A, E, R, Request extends S.TaggedRequest.Any> {
286
+ handler: (i: I) => Effect<A, E, R>
287
+ name: string
288
+ Request: Request
445
289
  }
package/src/mutate.ts CHANGED
@@ -225,3 +225,36 @@ export const makeMutation = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
225
225
 
226
226
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
227
227
  export interface MakeMutation<R> extends ReturnType<typeof makeMutation<R>> {}
228
+
229
+ export function mutationResultToVue<A, E>(
230
+ mutationResult: MutationResult<A, E>
231
+ ): Res<A, E> {
232
+ switch (mutationResult._tag) {
233
+ case "Loading": {
234
+ return { loading: true, data: undefined, error: undefined }
235
+ }
236
+ case "Success": {
237
+ return {
238
+ loading: false,
239
+ data: mutationResult.data,
240
+ error: undefined
241
+ }
242
+ }
243
+ case "Error": {
244
+ return {
245
+ loading: false,
246
+ data: undefined,
247
+ error: mutationResult.error
248
+ }
249
+ }
250
+ case "Initial": {
251
+ return { loading: false, data: undefined, error: undefined }
252
+ }
253
+ }
254
+ }
255
+
256
+ export interface Res<A, E> {
257
+ readonly loading: boolean
258
+ readonly data: A | undefined
259
+ readonly error: E | undefined
260
+ }
package/src/mutate2.ts CHANGED
@@ -3,10 +3,12 @@ import { tuple } from "@effect-app/core/Function"
3
3
  import * as Result from "@effect-rx/rx/Result"
4
4
  import type { InvalidateOptions, InvalidateQueryFilters } from "@tanstack/vue-query"
5
5
  import { useQueryClient } from "@tanstack/vue-query"
6
+ import type { S } from "effect-app"
6
7
  import { Cause, Effect, Exit, Option } from "effect-app"
7
8
  import type { ComputedRef, Ref } from "vue"
8
9
  import { computed, ref, shallowRef } from "vue"
9
10
  import { reportRuntimeError } from "./internal.js"
11
+ import type { RequestHandler, RequestHandlerWithInput } from "./makeClient2.js"
10
12
  import { getQueryKey } from "./mutate.js"
11
13
 
12
14
  export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
@@ -78,35 +80,27 @@ export interface MutationOptions {
78
80
  */
79
81
 
80
82
  export const makeMutation2 = () => {
81
- type HandlerWithInput<I, A, E, R> = {
82
- handler: (i: I) => Effect<A, E, R>
83
- name: string
84
- }
85
- type Handler<A, E, R> = { handler: Effect<A, E, R>; name: string }
86
-
87
83
  /**
88
84
  * Pass a function that returns an Effect, e.g from a client action, or an Effect
89
85
  * Returns a tuple with state ref and execution function which reports errors as Toast.
90
86
  */
91
87
  const useSafeMutation: {
92
- <I, E, A, R>(
93
- self: HandlerWithInput<I, A, E, R>,
88
+ <I, E, A, R, Request extends S.TaggedRequest.Any>(
89
+ self: RequestHandlerWithInput<I, A, E, R, Request>,
94
90
  options?: MutationOptions
95
91
  ): readonly [
96
92
  Readonly<Ref<MutationResult<A, E>>>,
97
93
  (i: I) => Effect<A, E, R>
98
94
  ]
99
- <E, A, R>(self: Handler<A, E, R>, options?: MutationOptions): readonly [
95
+ <E, A, R, Request extends S.TaggedRequest.Any>(
96
+ self: RequestHandler<A, E, R, Request>,
97
+ options?: MutationOptions
98
+ ): readonly [
100
99
  Readonly<Ref<MutationResult<A, E>>>,
101
100
  () => Effect<A, E, R> // TODO: remove () =>
102
101
  ]
103
- } = <I, E, A, R>(
104
- self: {
105
- handler:
106
- | HandlerWithInput<I, A, E, R>["handler"]
107
- | Handler<A, E, R>["handler"]
108
- name: string
109
- },
102
+ } = <I, E, A, R, Request extends S.TaggedRequest.Any>(
103
+ self: RequestHandlerWithInput<I, A, E, R, Request> | RequestHandler<A, E, R, Request>,
110
104
  options?: MutationOptions
111
105
  ) => {
112
106
  const queryClient = useQueryClient()
package/src/query.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  UseQueryReturnType
13
13
  } from "@tanstack/vue-query"
14
14
  import { useQuery } from "@tanstack/vue-query"
15
- import { Cause, Effect, Option, Runtime, S } from "effect-app"
15
+ import { Array, Cause, Effect, Option, Runtime, S } from "effect-app"
16
16
  import { ServiceUnavailableError } from "effect-app/client"
17
17
  import { computed, ref } from "vue"
18
18
  import type { ComputedRef, Ref, WatchSource } from "vue"
@@ -49,7 +49,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
49
49
  E,
50
50
  R
51
51
  >
52
- mapPath: (req: I) => string
53
52
  name: string
54
53
  }
55
54
  | {
@@ -58,7 +57,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
58
57
  E,
59
58
  R
60
59
  >
61
- mapPath: string
62
60
  name: string
63
61
  },
64
62
  arg?: I | WatchSource<I>,
@@ -162,7 +160,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
162
160
  function useSafeQuery<E, A>(
163
161
  self: {
164
162
  handler: Effect<A, E, R>
165
- mapPath: string
166
163
  name: string
167
164
  },
168
165
  options?: QueryObserverOptionsCustom // TODO
@@ -175,7 +172,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
175
172
  function useSafeQuery<Arg, E, A>(
176
173
  self: {
177
174
  handler: (arg: Arg) => Effect<A, E, R>
178
- mapPath: (arg: Arg) => string
179
175
  name: string
180
176
  },
181
177
  arg: Arg | WatchSource<Arg>,
@@ -198,7 +194,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
198
194
  E,
199
195
  R
200
196
  >
201
- mapPath: (req: I) => string
202
197
  name: string
203
198
  }
204
199
  | {
@@ -207,7 +202,6 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
207
202
  E,
208
203
  R
209
204
  >
210
- mapPath: string
211
205
  name: string
212
206
  },
213
207
  */
@@ -223,3 +217,49 @@ export const makeQuery = <R>(runtime: Ref<Runtime.Runtime<R>>) => {
223
217
 
224
218
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
225
219
  export interface MakeQuery<R> extends ReturnType<typeof makeQuery<R>> {}
220
+
221
+ export function composeQueries<
222
+ R extends Record<string, Result.Result<any, any>>
223
+ >(
224
+ results: R,
225
+ renderPreviousOnFailure?: boolean
226
+ ): Result.Result<
227
+ {
228
+ [Property in keyof R]: R[Property] extends Result.Result<infer A, any> ? A
229
+ : never
230
+ },
231
+ {
232
+ [Property in keyof R]: R[Property] extends Result.Result<any, infer E> ? E
233
+ : never
234
+ }[keyof R]
235
+ > {
236
+ const values = renderPreviousOnFailure
237
+ ? Object.values(results).map(orPrevious)
238
+ : Object.values(results)
239
+ const error = values.find(Result.isFailure)
240
+ if (error) {
241
+ return error
242
+ }
243
+ const initial = Array.findFirst(values, (x) => x._tag === "Initial" ? Option.some(x) : Option.none())
244
+ if (initial.value !== undefined) {
245
+ return initial.value
246
+ }
247
+ const loading = Array.findFirst(values, (x) => Result.isInitial(x) && x.waiting ? Option.some(x) : Option.none())
248
+ if (loading.value !== undefined) {
249
+ return loading.value
250
+ }
251
+
252
+ const isRefreshing = values.some((x) => x.waiting)
253
+
254
+ const r = Object.entries(results).reduce((prev, [key, value]) => {
255
+ prev[key] = Result.value(value).value
256
+ return prev
257
+ }, {} as any)
258
+ return Result.success(r, isRefreshing)
259
+ }
260
+
261
+ function orPrevious<E, A>(result: Result.Result<A, E>) {
262
+ return Result.isFailure(result) && Option.isSome(result.previousValue)
263
+ ? Result.success(result.previousValue.value, result.waiting)
264
+ : result
265
+ }