@effect-app/vue 4.0.0-beta.162 → 4.0.0-beta.164
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 +19 -0
- package/dist/makeClient.d.ts +19 -10
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +60 -9
- package/dist/mutate.d.ts +3 -3
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +6 -6
- package/package.json +2 -2
- package/src/makeClient.ts +112 -14
- package/src/mutate.ts +39 -30
- package/test/dist/stubs.d.ts +1183 -355
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +20 -3
- package/test/makeClient.test.ts +75 -0
- package/test/stubs.ts +28 -2
package/src/makeClient.ts
CHANGED
|
@@ -3,7 +3,9 @@ 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, RequestsAny } from "effect-app/client/clientFor"
|
|
6
|
+
import type { ExtractModuleName, RequestHandler, RequestHandlers, RequestHandlerWithInput, RequestInputFromMake, RequestsAny } from "effect-app/client/clientFor"
|
|
7
|
+
import type { InvalidationCallback } from "effect-app/client/makeClient"
|
|
8
|
+
import type * as ExitResult from "effect/Exit"
|
|
7
9
|
import { type Fiber } from "effect/Fiber"
|
|
8
10
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
9
11
|
import { type ComputedRef, onBeforeUnmount, ref, type WatchSource } from "vue"
|
|
@@ -481,6 +483,28 @@ const managedRuntimeRt = <A, E>(mrt: ManagedRuntime.ManagedRuntime<A, E>) => mrt
|
|
|
481
483
|
|
|
482
484
|
type Base = I18n | Toast
|
|
483
485
|
type Mix = ApiClientFactory | Commander | Base
|
|
486
|
+
|
|
487
|
+
type InvalidationResources = Record<string, Record<string, { readonly type: "command" | "query" }>>
|
|
488
|
+
type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I
|
|
489
|
+
: never
|
|
490
|
+
|
|
491
|
+
type CommandInvalidationResources<Req> = Req extends {
|
|
492
|
+
readonly type: "command"
|
|
493
|
+
readonly config?: infer Config
|
|
494
|
+
} ? Config extends {
|
|
495
|
+
readonly invalidationResources?: infer Resources
|
|
496
|
+
} ? Resources extends InvalidationResources ? Resources : never
|
|
497
|
+
: never
|
|
498
|
+
: never
|
|
499
|
+
|
|
500
|
+
type InvalidationResourcesForUnion<M extends RequestsAny> = {
|
|
501
|
+
[K in keyof M]: CommandInvalidationResources<M[K]>
|
|
502
|
+
}[keyof M]
|
|
503
|
+
|
|
504
|
+
type InvalidationResourcesFor<M extends RequestsAny> = [InvalidationResourcesForUnion<M>] extends [never] ? never
|
|
505
|
+
: UnionToIntersection<InvalidationResourcesForUnion<M>> extends infer R ? R extends InvalidationResources ? R
|
|
506
|
+
: never
|
|
507
|
+
: never
|
|
484
508
|
export const makeClient = <RT_, RTHooks>(
|
|
485
509
|
// global, but only accessible after startup has completed
|
|
486
510
|
getBaseMrt: () => ManagedRuntime.ManagedRuntime<RT_ | Mix, never>,
|
|
@@ -500,6 +524,37 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
500
524
|
const useQuery = query.useQuery
|
|
501
525
|
const useSuspenseQuery = query.useSuspenseQuery
|
|
502
526
|
|
|
527
|
+
const mergeInvalidation = (
|
|
528
|
+
a?: MutationOptionsBase["queryInvalidation"],
|
|
529
|
+
b?: MutationOptionsBase["queryInvalidation"]
|
|
530
|
+
): MutationOptionsBase["queryInvalidation"] | undefined => {
|
|
531
|
+
if (!a && !b) {
|
|
532
|
+
return undefined
|
|
533
|
+
}
|
|
534
|
+
return (defaultKey, name, input, output) => [
|
|
535
|
+
...(a?.(defaultKey, name, input, output) ?? []),
|
|
536
|
+
...(b?.(defaultKey, name, input, output) ?? [])
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const makeQueryResources = <Resources extends InvalidationResources>(resources: Resources | undefined) => {
|
|
541
|
+
if (!resources) {
|
|
542
|
+
return {} as Record<string, Record<string, unknown>>
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return Struct.keys(resources).reduce((acc, resourceName) => {
|
|
546
|
+
const resource = resources[resourceName]!
|
|
547
|
+
;(acc as any)[resourceName] = Struct.keys(resource).reduce((moduleAcc, requestName) => {
|
|
548
|
+
const request = resource[requestName]!
|
|
549
|
+
if (request.type === "query") {
|
|
550
|
+
;(moduleAcc as any)[requestName] = request
|
|
551
|
+
}
|
|
552
|
+
return moduleAcc
|
|
553
|
+
}, {} as Record<string, unknown>)
|
|
554
|
+
return acc
|
|
555
|
+
}, {} as Record<string, Record<string, unknown>>)
|
|
556
|
+
}
|
|
557
|
+
|
|
503
558
|
const mapQuery = <M extends RequestsAny>(
|
|
504
559
|
client: ClientFrom<M>
|
|
505
560
|
) => {
|
|
@@ -572,17 +627,35 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
572
627
|
}
|
|
573
628
|
|
|
574
629
|
const mapMutation = <M extends RequestsAny>(
|
|
575
|
-
client: ClientFrom<M
|
|
630
|
+
client: ClientFrom<M>,
|
|
631
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
632
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
576
633
|
) => {
|
|
577
634
|
const Command = useCommand()
|
|
578
635
|
const mutation = useMutation()
|
|
636
|
+
const invalidation = queryInvalidation?.(client)
|
|
637
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
579
638
|
const mutations = Struct.keys(client).reduce(
|
|
580
639
|
(acc, key) => {
|
|
581
640
|
if (client[key].Request.type !== "command") {
|
|
582
641
|
return acc
|
|
583
642
|
}
|
|
643
|
+
const fromRequestConfig = client[key].Request.config?.invalidatesQueries as
|
|
644
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
645
|
+
| undefined
|
|
646
|
+
const fromRequest = fromRequestConfig
|
|
647
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
648
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((entry) => ({
|
|
649
|
+
filters: entry.filters as InvalidateQueryFilters | undefined,
|
|
650
|
+
options: entry.options as InvalidateOptions | undefined
|
|
651
|
+
})))
|
|
652
|
+
: undefined
|
|
653
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
584
654
|
const makeProjectedMutation = (handler: any): any => {
|
|
585
|
-
const mut: any = mutation(
|
|
655
|
+
const mut: any = mutation(
|
|
656
|
+
handler,
|
|
657
|
+
mergedInvalidation ? { queryInvalidation: mergedInvalidation } : undefined
|
|
658
|
+
)
|
|
586
659
|
const wrap = Command.wrap({ mutate: Effect.isEffect(mut) ? () => mut : mut, id: client[key].id })
|
|
587
660
|
return Object.assign(mut, {
|
|
588
661
|
wrap,
|
|
@@ -614,7 +687,8 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
614
687
|
// make available .query, .suspense and .mutate for each operation
|
|
615
688
|
// and a .helpers with all mutations and queries
|
|
616
689
|
const mapClient = <M extends RequestsAny>(
|
|
617
|
-
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M
|
|
690
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
691
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
618
692
|
) =>
|
|
619
693
|
(
|
|
620
694
|
client: ClientFrom<M>
|
|
@@ -622,6 +696,7 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
622
696
|
const Command = useCommand()
|
|
623
697
|
const mutation = useMutation()
|
|
624
698
|
const invalidation = queryInvalidation?.(client)
|
|
699
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
625
700
|
const extended = Struct.keys(client).reduce(
|
|
626
701
|
(acc, key) => {
|
|
627
702
|
const requestType = client[key].Request.type
|
|
@@ -658,10 +733,23 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
658
733
|
}
|
|
659
734
|
: {
|
|
660
735
|
mutate: ((handler: any) => {
|
|
736
|
+
const fromRequestConfig = client[key].Request.config?.invalidatesQueries as
|
|
737
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
738
|
+
| undefined
|
|
739
|
+
const fromRequest = fromRequestConfig
|
|
740
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
741
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((
|
|
742
|
+
entry
|
|
743
|
+
) => ({
|
|
744
|
+
filters: entry.filters as InvalidateQueryFilters | undefined,
|
|
745
|
+
options: entry.options as InvalidateOptions | undefined
|
|
746
|
+
})))
|
|
747
|
+
: undefined
|
|
748
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
661
749
|
const makeProjectedMutation = (h: any): any => {
|
|
662
750
|
const mutate = mutation(
|
|
663
751
|
h,
|
|
664
|
-
|
|
752
|
+
mergedInvalidation ? { queryInvalidation: mergedInvalidation } : undefined
|
|
665
753
|
) as any
|
|
666
754
|
return Object.assign(
|
|
667
755
|
mutate,
|
|
@@ -706,26 +794,34 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
706
794
|
& { Input: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I : never }
|
|
707
795
|
}
|
|
708
796
|
)
|
|
709
|
-
return Object.assign(extended, {
|
|
797
|
+
return Object.assign(extended, {
|
|
798
|
+
helpers: {
|
|
799
|
+
...mapRequest(client),
|
|
800
|
+
...mapMutation(client, queryInvalidation, invalidationResources),
|
|
801
|
+
...mapQuery(client)
|
|
802
|
+
}
|
|
803
|
+
})
|
|
710
804
|
}
|
|
711
805
|
|
|
712
806
|
// TODO: Clean up this delay initialisation messs
|
|
713
807
|
// TODO; invalidateQueries should perhaps be configured in the Request impl themselves?
|
|
714
808
|
const clientFor__ = <M extends RequestsAny>(
|
|
715
809
|
m: M,
|
|
716
|
-
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M
|
|
717
|
-
|
|
810
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
811
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
812
|
+
) => getBaseMrt().runSync(clientFor_(m).pipe(Effect.map(mapClient(queryInvalidation, invalidationResources))))
|
|
718
813
|
|
|
719
814
|
// delay client creation until first access
|
|
720
815
|
// the idea is that we don't need the useNuxtApp().$runtime (only available at later initialisation stage)
|
|
721
816
|
// until we are at a place where it is available..
|
|
722
817
|
const clientFor = <M extends RequestsAny>(
|
|
723
818
|
m: M,
|
|
724
|
-
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M
|
|
819
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
820
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
725
821
|
) => {
|
|
726
822
|
type Client = ReturnType<typeof clientFor__<M>>
|
|
727
823
|
let client: Client | undefined = undefined
|
|
728
|
-
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation))
|
|
824
|
+
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation, invalidationResources))
|
|
729
825
|
|
|
730
826
|
// initialize on first use..
|
|
731
827
|
const proxy = Struct.keys(m).concat(["helpers"]).reduce((acc, key) => {
|
|
@@ -762,7 +858,12 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
762
858
|
}
|
|
763
859
|
|
|
764
860
|
export type QueryInvalidation<M> = {
|
|
765
|
-
[K in keyof M]?: (
|
|
861
|
+
[K in keyof M]?: (
|
|
862
|
+
defaultKey: string[],
|
|
863
|
+
name: string,
|
|
864
|
+
input?: unknown,
|
|
865
|
+
output?: ExitResult.Exit<unknown, unknown>
|
|
866
|
+
) => {
|
|
766
867
|
filters?: InvalidateQueryFilters | undefined
|
|
767
868
|
options?: InvalidateOptions | undefined
|
|
768
869
|
}[]
|
|
@@ -784,9 +885,6 @@ export interface CommandBase<I = void, A = void> {
|
|
|
784
885
|
|
|
785
886
|
export interface EffectCommand<I = void, A = unknown, E = unknown> extends CommandBase<I, Fiber<A, E>> {}
|
|
786
887
|
|
|
787
|
-
type RequestInputFromMake<I extends { readonly make: (...args: any[]) => any }> = Parameters<I["make"]> extends
|
|
788
|
-
[infer A, ...ReadonlyArray<any>] ? A : void
|
|
789
|
-
|
|
790
888
|
export interface CommandFromRequest<I extends { readonly make: (...args: any[]) => any }, A = unknown, E = unknown>
|
|
791
889
|
extends EffectCommand<RequestInputFromMake<I>, A, E>
|
|
792
890
|
{}
|
package/src/mutate.ts
CHANGED
|
@@ -72,7 +72,7 @@ export interface MutationOptionsBase {
|
|
|
72
72
|
* By default we invalidate one level of the query key, e.g $project/$configuration.get, we invalidate $project.
|
|
73
73
|
* This can be overridden by providing a function that returns an array of filters and options.
|
|
74
74
|
*/
|
|
75
|
-
queryInvalidation?: (defaultKey: string[], name: string) => {
|
|
75
|
+
queryInvalidation?: (defaultKey: string[], name: string, input?: unknown, output?: Exit.Exit<unknown, unknown>) => {
|
|
76
76
|
filters?: InvalidateQueryFilters | undefined
|
|
77
77
|
options?: InvalidateOptions | undefined
|
|
78
78
|
}[]
|
|
@@ -158,40 +158,49 @@ export const invalidateQueries = (
|
|
|
158
158
|
)
|
|
159
159
|
)
|
|
160
160
|
|
|
161
|
-
const invalidateCache =
|
|
162
|
-
|
|
161
|
+
const invalidateCache = (input: unknown, output: Exit.Exit<unknown, unknown>) =>
|
|
162
|
+
Effect.suspend(() => {
|
|
163
|
+
const queryKey = getQueryKey(self)
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
if (options) {
|
|
166
|
+
const opts = options(queryKey, self.id, input, output)
|
|
167
|
+
if (!opts.length) {
|
|
168
|
+
return Effect.void
|
|
169
|
+
}
|
|
170
|
+
return Effect
|
|
171
|
+
.andThen(
|
|
172
|
+
Effect.annotateCurrentSpan({ queryKey, opts }),
|
|
173
|
+
Effect.forEach(opts, (_) => invalidateQueries(_.filters, _.options), { concurrency: "inherit" })
|
|
174
|
+
)
|
|
175
|
+
.pipe(Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false }))
|
|
168
176
|
}
|
|
177
|
+
|
|
178
|
+
if (!queryKey) return Effect.void
|
|
179
|
+
|
|
169
180
|
return Effect
|
|
170
181
|
.andThen(
|
|
171
|
-
Effect.annotateCurrentSpan({ queryKey
|
|
172
|
-
|
|
182
|
+
Effect.annotateCurrentSpan({ queryKey }),
|
|
183
|
+
invalidateQueries({ queryKey })
|
|
173
184
|
)
|
|
174
|
-
.pipe(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
.pipe(
|
|
186
|
+
Effect.tap(
|
|
187
|
+
// hand over control back to the event loop so that state can be updated..
|
|
188
|
+
// TODO: should we do this in general on any mutation, regardless of invalidation?
|
|
189
|
+
Effect.sleep(0)
|
|
190
|
+
),
|
|
191
|
+
Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false })
|
|
192
|
+
)
|
|
193
|
+
})
|
|
178
194
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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 })
|
|
195
|
+
const handle = <A, E, R>(self: Effect.Effect<A, E, R>, input?: unknown) =>
|
|
196
|
+
self.pipe(
|
|
197
|
+
Effect.onExit((exit) =>
|
|
198
|
+
invalidateCache(
|
|
199
|
+
input,
|
|
200
|
+
exit
|
|
201
|
+
)
|
|
191
202
|
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const handle = <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.ensuring(self, invalidateCache)
|
|
203
|
+
)
|
|
195
204
|
|
|
196
205
|
return handle
|
|
197
206
|
}
|
|
@@ -221,7 +230,7 @@ export const makeMutation = () => {
|
|
|
221
230
|
const queryClient = useQueryClient()
|
|
222
231
|
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
223
232
|
const handler = self.handler
|
|
224
|
-
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i))
|
|
233
|
+
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i), i)
|
|
225
234
|
|
|
226
235
|
return Object.assign(r, { id: self.id }) as any
|
|
227
236
|
}
|
|
@@ -255,7 +264,7 @@ export const useMakeMutation = () => {
|
|
|
255
264
|
) => {
|
|
256
265
|
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
257
266
|
const handler = self.handler
|
|
258
|
-
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i))
|
|
267
|
+
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i), i)
|
|
259
268
|
|
|
260
269
|
return Object.assign(r, { id: self.id }) as any
|
|
261
270
|
}
|