@effect-app/vue 4.0.0-beta.18 → 4.0.0-beta.181
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 +1275 -0
- package/dist/commander.d.ts +450 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +687 -0
- package/dist/confirm.d.ts +19 -0
- package/dist/confirm.d.ts.map +1 -0
- package/dist/confirm.js +24 -0
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -18
- package/dist/form.d.ts +13 -4
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +41 -12
- package/dist/index.d.ts +1 -1
- package/dist/intl.d.ts +15 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +9 -0
- package/dist/lib.d.ts +6 -8
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +34 -7
- package/dist/makeClient.d.ts +191 -290
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +231 -361
- package/dist/makeContext.d.ts +1 -1
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/makeUseCommand.d.ts +8 -0
- package/dist/makeUseCommand.d.ts.map +1 -0
- package/dist/makeUseCommand.js +13 -0
- package/dist/mutate.d.ts +57 -25
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +160 -33
- package/dist/query.d.ts +11 -15
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +19 -27
- package/dist/routeParams.d.ts +1 -1
- package/dist/runtime.d.ts +5 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +27 -17
- package/dist/toast.d.ts +46 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +32 -0
- package/dist/withToast.d.ts +26 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +49 -0
- package/eslint.config.mjs +2 -2
- package/examples/streamMutation.ts +85 -0
- package/package.json +48 -48
- package/src/{experimental/commander.ts → commander.ts} +1158 -275
- package/src/{experimental/confirm.ts → confirm.ts} +10 -14
- package/src/errorReporter.ts +62 -74
- package/src/form.ts +55 -16
- package/src/intl.ts +12 -0
- package/src/lib.ts +46 -13
- package/src/makeClient.ts +670 -1038
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +4 -4
- package/src/mutate.ts +306 -72
- package/src/query.ts +39 -50
- package/src/runtime.ts +39 -18
- package/src/{experimental/toast.ts → toast.ts} +11 -25
- package/src/{experimental/withToast.ts → withToast.ts} +15 -6
- package/test/Mutation.test.ts +130 -10
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/streamFinal.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +3220 -117
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +132 -25
- package/test/form-validation-errors.test.ts +23 -19
- package/test/form.test.ts +20 -2
- package/test/lib.test.ts +240 -0
- package/test/makeClient.test.ts +240 -38
- package/test/streamFinal.test.ts +110 -0
- package/test/stubs.ts +172 -42
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +5 -2
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/dist/experimental/commander.d.ts +0 -359
- package/dist/experimental/commander.d.ts.map +0 -1
- package/dist/experimental/commander.js +0 -557
- package/dist/experimental/confirm.d.ts +0 -19
- package/dist/experimental/confirm.d.ts.map +0 -1
- package/dist/experimental/confirm.js +0 -28
- package/dist/experimental/intl.d.ts +0 -16
- package/dist/experimental/intl.d.ts.map +0 -1
- package/dist/experimental/intl.js +0 -5
- package/dist/experimental/makeUseCommand.d.ts +0 -8
- package/dist/experimental/makeUseCommand.d.ts.map +0 -1
- package/dist/experimental/makeUseCommand.js +0 -13
- package/dist/experimental/toast.d.ts +0 -47
- package/dist/experimental/toast.d.ts.map +0 -1
- package/dist/experimental/toast.js +0 -41
- package/dist/experimental/withToast.d.ts +0 -25
- package/dist/experimental/withToast.d.ts.map +0 -1
- package/dist/experimental/withToast.js +0 -45
- package/src/experimental/intl.ts +0 -9
package/src/makeClient.ts
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { type InvalidateOptions, type InvalidateQueryFilters, isCancelledError, type QueryObserverResult, type RefetchOptions, type UseQueryReturnType } from "@tanstack/vue-query"
|
|
3
3
|
import { camelCase } from "change-case"
|
|
4
|
-
import {
|
|
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 { RequestHandler, RequestHandlers, RequestHandlerWithInput,
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import { type OperationFailure, OperationSuccess } from "effect-app/Operations"
|
|
10
|
-
import { dropUndefinedT, extendM } from "effect-app/utils"
|
|
6
|
+
import type { ExtractModuleName, RequestHandler, RequestHandlers, RequestHandlerWithInput, RequestInputFromMake, RequestsAny, RequestStreamHandler, RequestStreamHandlerWithInput } from "effect-app/client/clientFor"
|
|
7
|
+
import type { InvalidationCallback } from "effect-app/client/makeClient"
|
|
8
|
+
import type * as ExitResult from "effect/Exit"
|
|
11
9
|
import { type Fiber } from "effect/Fiber"
|
|
12
10
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
13
|
-
import { computed, type ComputedRef, onBeforeUnmount,
|
|
14
|
-
import {
|
|
15
|
-
import { type
|
|
16
|
-
import {
|
|
17
|
-
import { type
|
|
18
|
-
import { Toast } from "./experimental/toast.js"
|
|
19
|
-
import { buildFieldInfoFromFieldsRoot } from "./form.js"
|
|
20
|
-
import { reportRuntimeError } from "./lib.js"
|
|
21
|
-
import { asResult, makeMutation, type MutationOptions, type MutationOptionsBase, mutationResultToVue, type Res, useMakeMutation } from "./mutate.js"
|
|
11
|
+
import { computed, type ComputedRef, onBeforeUnmount, ref, type WatchSource } from "vue"
|
|
12
|
+
import { type Commander, CommanderStatic, type Progress } from "./commander.js"
|
|
13
|
+
import { type I18n } from "./intl.js"
|
|
14
|
+
import { type CommanderResolved, makeUseCommand } from "./makeUseCommand.js"
|
|
15
|
+
import { makeMutation, makeStreamMutation, type MutationOptionsBase, useMakeMutation } from "./mutate.js"
|
|
22
16
|
import { type CustomUndefinedInitialQueryOptions, makeQuery } from "./query.js"
|
|
17
|
+
import { makeRunPromise } from "./runtime.js"
|
|
18
|
+
import { type Toast } from "./toast.js"
|
|
23
19
|
|
|
24
20
|
const mapHandler = <A, E, R, I = void, A2 = A, E2 = E, R2 = R>(
|
|
25
21
|
handler: Effect.Effect<A, E, R> | ((i: I) => Effect.Effect<A, E, R>),
|
|
26
22
|
map: (self: Effect.Effect<A, E, R>, i: I) => Effect.Effect<A2, E2, R2>
|
|
27
23
|
) => Effect.isEffect(handler) ? map(handler, undefined as any) : (i: I) => map(handler(i), i)
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
// TODO: optimize - work from encoded shape directly
|
|
26
|
+
const projectHandler = (
|
|
27
|
+
handler: Effect.Effect<any, any, any> | ((i: any) => Effect.Effect<any, any, any>),
|
|
28
|
+
successSchema: S.Top,
|
|
29
|
+
projectionSchema: S.Top
|
|
30
|
+
) => {
|
|
31
|
+
const encode = S.encodeEffect(successSchema)
|
|
32
|
+
const decode = S.decodeEffectConcurrently(projectionSchema)
|
|
33
|
+
return mapHandler(handler, (self) =>
|
|
34
|
+
self.pipe(
|
|
35
|
+
Effect.flatMap(encode),
|
|
36
|
+
Effect.flatMap(decode)
|
|
37
|
+
))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const projectionSchemaHash = (schema: S.Top) => String(Hash.hash(schema.ast))
|
|
41
|
+
|
|
42
|
+
export interface CommandRequestExtensions<RT, Id extends string, I, A, E, R> {
|
|
30
43
|
/** Defines a Command based on this call, taking the `id` of the call as the `id` of the Command.
|
|
31
44
|
* The Request function will be taken as the first member of the Command, the Command required input will be the Request input.
|
|
32
45
|
* see Command.wrap for details */
|
|
@@ -44,16 +57,14 @@ export interface RequestExtWithInput<
|
|
|
44
57
|
A,
|
|
45
58
|
E,
|
|
46
59
|
R
|
|
47
|
-
> extends Commander.CommandContextLocal<Id, Id>,
|
|
60
|
+
> extends Commander.CommandContextLocal<Id, Id>, CommandRequestExtensions<RT, Id, I, A, E, R> {
|
|
48
61
|
/**
|
|
49
|
-
*
|
|
62
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
63
|
+
* This does not perform query cache invalidation.
|
|
50
64
|
*/
|
|
51
|
-
(i: I)
|
|
65
|
+
request: (i: I) => Effect.Effect<A, E, R>
|
|
52
66
|
}
|
|
53
67
|
|
|
54
|
-
/**
|
|
55
|
-
* Request the endpoint
|
|
56
|
-
*/
|
|
57
68
|
export interface RequestExt<
|
|
58
69
|
RT,
|
|
59
70
|
Id extends string,
|
|
@@ -63,25 +74,69 @@ export interface RequestExt<
|
|
|
63
74
|
> extends
|
|
64
75
|
Commander.CommandContextLocal<Id, Id>,
|
|
65
76
|
Commander.CommanderWrap<RT, Id, Id, undefined, void, A, E, R>,
|
|
66
|
-
|
|
67
|
-
Effect.Effect<A, E, R>
|
|
77
|
+
CommandRequestExtensions<RT, Id, void, A, E, R>
|
|
68
78
|
{
|
|
79
|
+
/**
|
|
80
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
81
|
+
* This does not perform query cache invalidation.
|
|
82
|
+
*/
|
|
83
|
+
request: Effect.Effect<A, E, R>
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
export type
|
|
86
|
+
export type CommandRequestWithExtensions<RT, Req> = Req extends
|
|
72
87
|
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id>
|
|
73
88
|
? RequestExtWithInput<RT, Id, I, A, E, R>
|
|
74
89
|
: Req extends RequestHandler<infer A, infer E, infer R, infer _Request, infer Id> ? RequestExt<RT, Id, A, E, R>
|
|
75
90
|
: never
|
|
76
91
|
|
|
92
|
+
export interface QueryExtensionsWithInput<I, A, E, R> {
|
|
93
|
+
/**
|
|
94
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
95
|
+
* This does not set up query state tracking.
|
|
96
|
+
*/
|
|
97
|
+
request: (i: I) => Effect.Effect<A, E, R>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface QueryExtensions<A, E, R> {
|
|
101
|
+
/**
|
|
102
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
103
|
+
* This does not set up query state tracking.
|
|
104
|
+
*/
|
|
105
|
+
request: Effect.Effect<A, E, R>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type QueryRequestWithExtensions<Req> = Req extends
|
|
109
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer _Id>
|
|
110
|
+
? QueryExtensionsWithInput<I, A, E, R>
|
|
111
|
+
: Req extends RequestHandler<infer A, infer E, infer R, infer _Request, infer _Id> ? QueryExtensions<A, E, R>
|
|
112
|
+
: never
|
|
113
|
+
|
|
114
|
+
type QueryHandler<Req> = Req extends
|
|
115
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
116
|
+
? Request["type"] extends "query" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
|
|
117
|
+
: Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
|
|
118
|
+
? Request["type"] extends "query" ? RequestHandler<A, E, R, Request, Id> : never
|
|
119
|
+
: never
|
|
120
|
+
|
|
121
|
+
type CommandHandler<Req> = Req extends
|
|
122
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
123
|
+
? Request["type"] extends "command" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
|
|
124
|
+
: Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
|
|
125
|
+
? Request["type"] extends "command" ? RequestHandler<A, E, R, Request, Id> : never
|
|
126
|
+
: never
|
|
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
|
+
|
|
77
135
|
export interface MutationExtensions<RT, Id extends string, I, A, E, R> {
|
|
78
136
|
/** Defines a Command based on this mutation, taking the `id` of the mutation as the `id` of the Command.
|
|
79
137
|
* The Mutation function will be taken as the first member of the Command, the Command required input will be the Mutation input.
|
|
80
138
|
* see Command.wrap for details */
|
|
81
139
|
wrap: Commander.CommanderWrap<RT, Id, Id, undefined, I, A, E, R>
|
|
82
|
-
/** Defines a Command based on this mutation, taking the `id` of the mutation as the `id` of the Command.
|
|
83
|
-
* see Command.fn for details */
|
|
84
|
-
fn: Commander.CommanderFn<RT, Id, Id, undefined>
|
|
85
140
|
}
|
|
86
141
|
|
|
87
142
|
/** my other doc */
|
|
@@ -91,39 +146,147 @@ export interface MutationExtWithInput<
|
|
|
91
146
|
I,
|
|
92
147
|
A,
|
|
93
148
|
E,
|
|
94
|
-
R
|
|
95
|
-
|
|
149
|
+
R,
|
|
150
|
+
EA = unknown
|
|
151
|
+
> extends MutationExtensions<RT, Id, I, A, E, R> {
|
|
96
152
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
153
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
154
|
+
* Also invalidates query caches using the request namespace by default.
|
|
155
|
+
* Namespace invalidation targets parent namespace keys
|
|
156
|
+
* (for example `$project/$configuration.get` invalidates `$project`).
|
|
157
|
+
* Override invalidation in client options via `queryInvalidation`.
|
|
158
|
+
*
|
|
159
|
+
* Pass `options` to attach a `select` Effect that runs after the mutation
|
|
160
|
+
* succeeds (its output is returned to the caller) and/or override the default
|
|
161
|
+
* `queryInvalidation`.
|
|
100
162
|
*/
|
|
101
|
-
|
|
163
|
+
<B = A, E2 = never, R2 = never>(
|
|
164
|
+
input: I,
|
|
165
|
+
options?: MutationOptionsBase<A, B, E2, R2>
|
|
166
|
+
): Effect.Effect<B, E | E2, R | R2>
|
|
167
|
+
|
|
168
|
+
project: <ProjSchema extends S.Top>(
|
|
169
|
+
schema: EA extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
170
|
+
) => MutationExtWithInput<
|
|
171
|
+
RT,
|
|
172
|
+
Id,
|
|
173
|
+
I,
|
|
174
|
+
S.Schema.Type<ProjSchema>,
|
|
175
|
+
E | S.SchemaError,
|
|
176
|
+
R | S.Codec.DecodingServices<ProjSchema>,
|
|
177
|
+
S.Codec.Encoded<ProjSchema>
|
|
178
|
+
>
|
|
102
179
|
}
|
|
103
180
|
|
|
104
181
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
182
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
183
|
+
* Also invalidates query caches using the request namespace by default.
|
|
184
|
+
* Namespace invalidation targets parent namespace keys
|
|
185
|
+
* (for example `$project/$configuration.get` invalidates `$project`).
|
|
186
|
+
* Override invalidation in client options via `queryInvalidation`.
|
|
108
187
|
*/
|
|
109
188
|
export interface MutationExt<
|
|
110
189
|
RT,
|
|
111
190
|
Id extends string,
|
|
112
191
|
A,
|
|
113
192
|
E,
|
|
114
|
-
R
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
193
|
+
R,
|
|
194
|
+
EA = unknown
|
|
195
|
+
> extends MutationExtensions<RT, Id, void, A, E, R> {
|
|
196
|
+
/**
|
|
197
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
198
|
+
* Also invalidates query caches using the request namespace by default.
|
|
199
|
+
*
|
|
200
|
+
* Pass `options` to attach a `select` Effect that runs after the mutation
|
|
201
|
+
* succeeds (its output is returned to the caller) and/or override the default
|
|
202
|
+
* `queryInvalidation`.
|
|
203
|
+
*/
|
|
204
|
+
<B = A, E2 = never, R2 = never>(
|
|
205
|
+
options?: MutationOptionsBase<A, B, E2, R2>
|
|
206
|
+
): Effect.Effect<B, E | E2, R | R2>
|
|
207
|
+
|
|
208
|
+
project: <ProjSchema extends S.Top>(
|
|
209
|
+
schema: EA extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
210
|
+
) => MutationExt<
|
|
211
|
+
RT,
|
|
212
|
+
Id,
|
|
213
|
+
S.Schema.Type<ProjSchema>,
|
|
214
|
+
E | S.SchemaError,
|
|
215
|
+
R | S.Codec.DecodingServices<ProjSchema>,
|
|
216
|
+
S.Codec.Encoded<ProjSchema>
|
|
217
|
+
>
|
|
121
218
|
}
|
|
122
219
|
|
|
123
220
|
export type MutationWithExtensions<RT, Req> = Req extends
|
|
124
|
-
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer
|
|
125
|
-
? MutationExtWithInput<RT, Id, I, A, E, R
|
|
126
|
-
: Req extends RequestHandler<infer A, infer E, infer R, infer
|
|
221
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
222
|
+
? MutationExtWithInput<RT, Id, I, A, E, R, S.Codec.Encoded<Request["success"]>>
|
|
223
|
+
: Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
|
|
224
|
+
? MutationExt<RT, Id, A, E, R, S.Codec.Encoded<Request["success"]>>
|
|
225
|
+
: never
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Options for invoking a `mutateStream` factory. Supplying `progress` produces
|
|
229
|
+
* a tuple-with-id that carries `running` (the live AsyncResult ref) and
|
|
230
|
+
* `progress` (a `ComputedRef<Progress | undefined>` formatted from each value),
|
|
231
|
+
* which `Command.fn` / `Command.wrapStream` surface as the command's `running`
|
|
232
|
+
* and `progress`. When omitted, the resulting command exposes neither.
|
|
233
|
+
*/
|
|
234
|
+
export type MutateStreamCallOptions<A, E> = {
|
|
235
|
+
progress?: (result: AsyncResult.AsyncResult<A, E>) => Progress | undefined
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The `mutateStream` factory for a stream-type request handler. Always invoke
|
|
240
|
+
* (optionally with `{ progress }`) to get a fresh `[resultRef, execute]` tuple —
|
|
241
|
+
* each call produces a new `ComputedRef` + execute pair so independent invocations
|
|
242
|
+
* don't share state. `execute` updates the ref live with each emitted value.
|
|
243
|
+
* When the request declares a `final` schema, `execute` resolves with the last
|
|
244
|
+
* emitted value typed as `Final`; otherwise it resolves with the success type.
|
|
245
|
+
* The factory itself carries the request `id` so it can be passed to
|
|
246
|
+
* `Command.fn` / `Command.wrapStream` directly.
|
|
247
|
+
*/
|
|
248
|
+
export type StreamMutationWithExtensions<Req> = Req extends
|
|
249
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id, infer Final> ?
|
|
250
|
+
& ((options?: MutateStreamCallOptions<A, E>) =>
|
|
251
|
+
& readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, (input: I) => Effect.Effect<Final, never, R>]
|
|
252
|
+
& {
|
|
253
|
+
readonly id: Id
|
|
254
|
+
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
255
|
+
readonly progress?: ComputedRef<Progress | undefined>
|
|
256
|
+
})
|
|
257
|
+
& { readonly id: Id }
|
|
258
|
+
: Req extends RequestStreamHandler<infer A, infer E, infer R, infer _Request, infer Id, infer Final> ?
|
|
259
|
+
& ((options?: MutateStreamCallOptions<A, E>) =>
|
|
260
|
+
& readonly [ComputedRef<AsyncResult.AsyncResult<A, E>>, Effect.Effect<Final, never, R>]
|
|
261
|
+
& {
|
|
262
|
+
readonly id: Id
|
|
263
|
+
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
264
|
+
readonly progress?: ComputedRef<Progress | undefined>
|
|
265
|
+
})
|
|
266
|
+
& { readonly id: Id }
|
|
267
|
+
: never
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* The pre-built `wrapStream` CommanderWrap for a stream-type request handler.
|
|
271
|
+
* The command's `result` and `running` are the live stream ref.
|
|
272
|
+
* Callable like `wrap`: `client.myExport.wrapStream()` returns the CommandOut.
|
|
273
|
+
*/
|
|
274
|
+
export type StreamCommandWithExtensions<RT, Req> = Req extends
|
|
275
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id, infer _Final>
|
|
276
|
+
? Commander.CommanderWrap<RT, Id, Id, undefined, I, A, E, R>
|
|
277
|
+
: Req extends RequestStreamHandler<infer A, infer E, infer R, infer _Request, infer Id, infer _Final>
|
|
278
|
+
? Commander.CommanderWrap<RT, Id, Id, undefined, void, A, E, R>
|
|
279
|
+
: never
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The `fn` builder for a stream-type request handler — identical to calling
|
|
283
|
+
* `Command.fn(id)` where `id` comes from the request.
|
|
284
|
+
*/
|
|
285
|
+
export type StreamFnExtension<RT, Req> = Req extends
|
|
286
|
+
RequestStreamHandlerWithInput<infer _I, infer _A, infer _E, infer _R, infer _Request, infer Id, infer _Final>
|
|
287
|
+
? Commander.CommanderFn<RT, Id, Id, undefined>
|
|
288
|
+
: Req extends RequestStreamHandler<infer _A, infer _E, infer _R, infer _Request, infer Id, infer _Final>
|
|
289
|
+
? Commander.CommanderFn<RT, Id, Id, undefined>
|
|
127
290
|
: never
|
|
128
291
|
|
|
129
292
|
// we don't really care about the RT, as we are in charge of ensuring runtime safety anyway
|
|
@@ -132,29 +295,72 @@ declare const useQuery_: QueryImpl<any>["useQuery"]
|
|
|
132
295
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
133
296
|
declare const useSuspenseQuery_: QueryImpl<any>["useSuspenseQuery"]
|
|
134
297
|
|
|
298
|
+
export interface ProjectResult<RT, I, B, E, R, Request extends Req, Id extends string> {
|
|
299
|
+
request: (i: I) => Effect.Effect<B, E, R>
|
|
300
|
+
query: Exclude<R, RT> extends never ? ReturnType<typeof useQuery_<I, E, B, Request, Id>>
|
|
301
|
+
: MissingDependencies<RT, R> & {}
|
|
302
|
+
suspense: Exclude<R, RT> extends never ? ReturnType<typeof useSuspenseQuery_<I, E, B, Request, Id>>
|
|
303
|
+
: MissingDependencies<RT, R> & {}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export type QueryProjection<RT, HandlerReq> = HandlerReq extends
|
|
307
|
+
RequestHandlerWithInput<infer I, infer _A, infer E, infer R, infer Request, infer Id>
|
|
308
|
+
? Request["type"] extends "query" ? {
|
|
309
|
+
project: <ProjSchema extends S.Top>(
|
|
310
|
+
schema: S.Codec.Encoded<Request["success"]> extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
311
|
+
) => ProjectResult<
|
|
312
|
+
RT,
|
|
313
|
+
I,
|
|
314
|
+
S.Schema.Type<ProjSchema>,
|
|
315
|
+
E | S.SchemaError,
|
|
316
|
+
R | S.Codec.DecodingServices<ProjSchema>,
|
|
317
|
+
Request,
|
|
318
|
+
Id
|
|
319
|
+
>
|
|
320
|
+
}
|
|
321
|
+
: {}
|
|
322
|
+
: HandlerReq extends RequestHandler<infer _A, infer E, infer R, infer Request, infer Id>
|
|
323
|
+
? Request["type"] extends "query" ? {
|
|
324
|
+
project: <ProjSchema extends S.Top>(
|
|
325
|
+
schema: S.Codec.Encoded<Request["success"]> extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
326
|
+
) => ProjectResult<
|
|
327
|
+
RT,
|
|
328
|
+
void,
|
|
329
|
+
S.Schema.Type<ProjSchema>,
|
|
330
|
+
E | S.SchemaError,
|
|
331
|
+
R | S.Codec.DecodingServices<ProjSchema>,
|
|
332
|
+
Request,
|
|
333
|
+
Id
|
|
334
|
+
>
|
|
335
|
+
}
|
|
336
|
+
: {}
|
|
337
|
+
: {}
|
|
338
|
+
|
|
135
339
|
export interface QueriesWithInput<Request extends Req, Id extends string, I, A, E> {
|
|
136
340
|
/**
|
|
137
|
-
*
|
|
341
|
+
* Read helper for query requests.
|
|
342
|
+
* Runs as a tracked Vue Query and returns reactive state.
|
|
343
|
+
* Queries read state and should not be used to mutate it.
|
|
138
344
|
*/
|
|
139
345
|
query: ReturnType<typeof useQuery_<I, E, A, Request, Id>>
|
|
140
346
|
// TODO or suspense as Option?
|
|
141
347
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* So that Suspense and error boundaries can be used.
|
|
348
|
+
* Like `.query`, but returns a Promise for setup-time awaiting.
|
|
349
|
+
* Use this when integrating with Vue Suspense / error boundaries.
|
|
145
350
|
*/
|
|
146
351
|
suspense: ReturnType<typeof useSuspenseQuery_<I, E, A, Request, Id>>
|
|
147
352
|
}
|
|
148
353
|
export interface QueriesWithoutInput<Request extends Req, Id extends string, A, E> {
|
|
149
354
|
/**
|
|
150
|
-
*
|
|
355
|
+
* Read helper for query requests.
|
|
356
|
+
* Runs as a tracked Vue Query and returns reactive state.
|
|
357
|
+
* Queries read state and should not be used to mutate it.
|
|
151
358
|
*/
|
|
152
359
|
query: ReturnType<typeof useQuery_<E, A, Request, Id>>
|
|
153
360
|
// TODO or suspense as Option?
|
|
154
361
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* So that Suspense and error boundaries can be used.
|
|
362
|
+
* Like `.query`, but returns a Promise for setup-time awaiting.
|
|
363
|
+
* Use this when integrating with Vue Suspense / error boundaries.
|
|
158
364
|
*/
|
|
159
365
|
suspense: ReturnType<typeof useSuspenseQuery_<E, A, Request, Id>>
|
|
160
366
|
}
|
|
@@ -166,184 +372,34 @@ export type MissingDependencies<RT, R> = {
|
|
|
166
372
|
|
|
167
373
|
export type Queries<RT, Req> = Req extends
|
|
168
374
|
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
169
|
-
? Exclude<R, RT> extends never ? QueriesWithInput<Request, Id, I, A, E>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
375
|
+
? Request["type"] extends "query" ? Exclude<R, RT> extends never ? QueriesWithInput<Request, Id, I, A, E>
|
|
376
|
+
: {
|
|
377
|
+
query: MissingDependencies<RT, R> & {}
|
|
378
|
+
suspense: MissingDependencies<RT, R> & {}
|
|
379
|
+
}
|
|
380
|
+
: never
|
|
174
381
|
: Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
|
|
175
|
-
? Exclude<R, RT> extends never ? QueriesWithoutInput<Request, Id, A, E>
|
|
176
|
-
|
|
382
|
+
? Request["type"] extends "query" ? Exclude<R, RT> extends never ? QueriesWithoutInput<Request, Id, A, E>
|
|
383
|
+
: { query: MissingDependencies<RT, R> & {}; suspense: MissingDependencies<RT, R> & {} }
|
|
384
|
+
: never
|
|
177
385
|
: never
|
|
178
386
|
|
|
179
|
-
|
|
180
|
-
* Use this after handling an error yourself, still continueing on the Error track, but the error will not be reported.
|
|
181
|
-
*/
|
|
182
|
-
export class SuppressErrors extends Data.TaggedError("SuppressErrors")<{}> {
|
|
183
|
-
readonly [ErrorSilenced] = true as const
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export type ResponseErrors = S.SchemaError | SupportedErrors | SuppressErrors | OperationFailure
|
|
187
|
-
|
|
188
|
-
export interface Opts<
|
|
189
|
-
A,
|
|
190
|
-
E,
|
|
191
|
-
R,
|
|
192
|
-
I = void,
|
|
193
|
-
A2 = A,
|
|
194
|
-
E2 = E,
|
|
195
|
-
R2 = R,
|
|
196
|
-
ESuccess = never,
|
|
197
|
-
RSuccess = never,
|
|
198
|
-
EError = never,
|
|
199
|
-
RError = never,
|
|
200
|
-
EDefect = never,
|
|
201
|
-
RDefect = never
|
|
202
|
-
> extends MutationOptions<A, E, R, A2, E2, R2, I> {
|
|
203
|
-
/** set to `undefined` to use default message */
|
|
204
|
-
successMessage?: ((a: A2, i: I) => Effect.Effect<string | undefined, ESuccess, RSuccess>) | undefined
|
|
205
|
-
/** set to `undefined` to use default message */
|
|
206
|
-
failMessage?: ((e: E2, i: I) => Effect.Effect<string | undefined, EError, RError>) | undefined
|
|
207
|
-
/** set to `undefined` to use default message */
|
|
208
|
-
defectMessage?: ((e: Cause.Cause<E2>, i: I) => Effect.Effect<string | undefined, EDefect, RDefect>) | undefined
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export interface LowOpts<
|
|
212
|
-
A,
|
|
213
|
-
E,
|
|
214
|
-
I = void,
|
|
215
|
-
ESuccess = never,
|
|
216
|
-
RSuccess = never,
|
|
217
|
-
EError = never,
|
|
218
|
-
RError = never,
|
|
219
|
-
EDefect = never,
|
|
220
|
-
RDefect = never
|
|
221
|
-
> {
|
|
222
|
-
onSuccess: (a: A, i: I) => Effect.Effect<void, ESuccess, RSuccess>
|
|
223
|
-
onFail: (e: E, i: I) => Effect.Effect<void, EError, RError>
|
|
224
|
-
onDefect: (e: Cause.Cause<E>, i: I) => Effect.Effect<void, EDefect, RDefect>
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export interface LowOptsOptional<
|
|
228
|
-
A,
|
|
229
|
-
E,
|
|
230
|
-
R,
|
|
231
|
-
I = void,
|
|
232
|
-
A2 = A,
|
|
233
|
-
E2 = E,
|
|
234
|
-
R2 = R,
|
|
235
|
-
ESuccess = never,
|
|
236
|
-
RSuccess = never,
|
|
237
|
-
EError = never,
|
|
238
|
-
RError = never,
|
|
239
|
-
EDefect = never,
|
|
240
|
-
RDefect = never
|
|
241
|
-
> extends MutationOptions<A, E, R, A2, E2, R2, I> {
|
|
242
|
-
onSuccess?: (a: A, i: I) => Effect.Effect<void, ESuccess, RSuccess>
|
|
243
|
-
onFail?: (e: E, i: I) => Effect.Effect<void, EError, RError>
|
|
244
|
-
onDefect?: (e: Cause.Cause<E>, i: I) => Effect.Effect<void, EDefect, RDefect>
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
type WithAction<A> = A & {
|
|
248
|
-
action: string
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// computed() takes a getter function and returns a readonly reactive ref
|
|
252
|
-
// object for the returned value from the getter.
|
|
253
|
-
|
|
254
|
-
type Resp<I, A, E, R, V = ComputedRef<Res<A, E>>> = readonly [
|
|
255
|
-
V,
|
|
256
|
-
WithAction<(I: I) => Effect.Effect<Exit.Exit<A, E>, never, R>>
|
|
257
|
-
]
|
|
258
|
-
|
|
259
|
-
type ActResp<A, E, R, V = ComputedRef<Res<A, E>>> = readonly [
|
|
260
|
-
V,
|
|
261
|
-
WithAction<Effect.Effect<Exit.Exit<A, E>, never, R>>
|
|
262
|
-
]
|
|
263
|
-
|
|
264
|
-
export const suppressToast = constant(Effect.succeed(undefined))
|
|
265
|
-
|
|
266
|
-
/** handles errors as specified and reports defects */
|
|
267
|
-
function handleRequest<
|
|
268
|
-
E extends ResponseErrors,
|
|
269
|
-
A,
|
|
270
|
-
R,
|
|
271
|
-
I = void,
|
|
272
|
-
ESuccess = never,
|
|
273
|
-
RSuccess = never,
|
|
274
|
-
EError = never,
|
|
275
|
-
RError = never,
|
|
276
|
-
EDefect = never,
|
|
277
|
-
RDefect = never
|
|
278
|
-
>(
|
|
279
|
-
f: Effect.Effect<Exit.Exit<A, E>, never, R> | ((i: I) => Effect.Effect<Exit.Exit<A, E>, never, R>),
|
|
280
|
-
id: string,
|
|
281
|
-
action: string,
|
|
282
|
-
options: {
|
|
283
|
-
onSuccess: (a: A, i: I) => Effect.Effect<void, ESuccess, RSuccess>
|
|
284
|
-
onFail: (e: E, i: I) => Effect.Effect<void, EError, RError>
|
|
285
|
-
onDefect: (e: Cause.Cause<E>, i: I) => Effect.Effect<void, EDefect, RDefect>
|
|
286
|
-
}
|
|
287
|
-
) {
|
|
288
|
-
const handleEffect = (i: any) => (self: Effect.Effect<Exit.Exit<A, E>, never, R>) =>
|
|
289
|
-
self.pipe(
|
|
290
|
-
Effect.tap(
|
|
291
|
-
Effect.matchCauseEffect({
|
|
292
|
-
onSuccess: (r) => options.onSuccess(r, i),
|
|
293
|
-
onFailure: (cause) =>
|
|
294
|
-
Effect.gen(function*() {
|
|
295
|
-
if (Cause.hasInterruptsOnly(cause)) {
|
|
296
|
-
console.info(`Interrupted while trying to ${action}`)
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
const fail = Cause.findErrorOption(cause)
|
|
301
|
-
if (Option.isSome(fail)) {
|
|
302
|
-
if (fail.value._tag === "SuppressErrors") {
|
|
303
|
-
console.info(`Suppressed error trying to ${action}`, fail.value)
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
const message = `Failure trying to ${action}`
|
|
307
|
-
yield* reportMessage(message, { action, error: fail.value })
|
|
308
|
-
yield* options.onFail(fail.value, i)
|
|
309
|
-
return
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const extra = {
|
|
313
|
-
action,
|
|
314
|
-
message: `Unexpected Error trying to ${action}`
|
|
315
|
-
}
|
|
316
|
-
yield* reportRuntimeError(cause, extra)
|
|
387
|
+
const _useMutation = makeMutation()
|
|
317
388
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
)
|
|
324
|
-
return Object.assign(
|
|
325
|
-
Effect.isEffect(f)
|
|
326
|
-
? pipe(
|
|
327
|
-
f,
|
|
328
|
-
handleEffect(void 0)
|
|
329
|
-
)
|
|
330
|
-
: (i: I) =>
|
|
331
|
-
pipe(
|
|
332
|
-
f(i),
|
|
333
|
-
handleEffect(i)
|
|
334
|
-
),
|
|
335
|
-
{ action }
|
|
336
|
-
)
|
|
389
|
+
const wrapWithSpan = (self: { id: string; handler: any }, mut: any) => {
|
|
390
|
+
const span = (eff: Effect.Effect<any, any, any>) =>
|
|
391
|
+
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })(eff)
|
|
392
|
+
return Effect.isEffect(self.handler)
|
|
393
|
+
? (options?: MutationOptionsBase) => span(mut(options))
|
|
394
|
+
: (input: any, options?: MutationOptionsBase) => span(mut(input, options))
|
|
337
395
|
}
|
|
338
396
|
|
|
339
|
-
const _useMutation = makeMutation()
|
|
340
|
-
|
|
341
397
|
/**
|
|
342
398
|
* Pass an Effect or a function that returns an Effect, e.g from a client action
|
|
343
399
|
* Executes query cache invalidation based on default rules or provided option.
|
|
344
400
|
* adds a span with the mutation id
|
|
345
401
|
*/
|
|
346
|
-
export const useMutation: typeof _useMutation = <
|
|
402
|
+
export const useMutation: typeof _useMutation = (<
|
|
347
403
|
I,
|
|
348
404
|
E,
|
|
349
405
|
A,
|
|
@@ -351,16 +407,12 @@ export const useMutation: typeof _useMutation = <
|
|
|
351
407
|
Request extends Req,
|
|
352
408
|
Name extends string
|
|
353
409
|
>(
|
|
354
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name
|
|
355
|
-
options?: MutationOptionsBase
|
|
410
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>
|
|
356
411
|
) =>
|
|
357
412
|
Object.assign(
|
|
358
|
-
|
|
359
|
-
_useMutation(self as any, options),
|
|
360
|
-
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })
|
|
361
|
-
) as any,
|
|
413
|
+
wrapWithSpan(self, _useMutation(self as any)),
|
|
362
414
|
{ id: self.id }
|
|
363
|
-
)
|
|
415
|
+
)) as any
|
|
364
416
|
|
|
365
417
|
/**
|
|
366
418
|
* Pass an Effect or a function that returns an Effect, e.g from a client action
|
|
@@ -369,7 +421,7 @@ export const useMutation: typeof _useMutation = <
|
|
|
369
421
|
*/
|
|
370
422
|
export const useMutationInt = (): typeof _useMutation => {
|
|
371
423
|
const _useMutation = useMakeMutation()
|
|
372
|
-
return <
|
|
424
|
+
return (<
|
|
373
425
|
I,
|
|
374
426
|
E,
|
|
375
427
|
A,
|
|
@@ -377,714 +429,18 @@ export const useMutationInt = (): typeof _useMutation => {
|
|
|
377
429
|
Request extends Req,
|
|
378
430
|
Name extends string
|
|
379
431
|
>(
|
|
380
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name
|
|
381
|
-
options?: MutationOptionsBase
|
|
432
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>
|
|
382
433
|
) =>
|
|
383
434
|
Object.assign(
|
|
384
|
-
|
|
385
|
-
_useMutation(self as any, options),
|
|
386
|
-
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })
|
|
387
|
-
) as any,
|
|
435
|
+
wrapWithSpan(self, _useMutation(self as any)),
|
|
388
436
|
{ id: self.id }
|
|
389
|
-
)
|
|
437
|
+
)) as any
|
|
390
438
|
}
|
|
391
439
|
|
|
392
|
-
export
|
|
393
|
-
constructor(
|
|
394
|
-
private readonly getRuntime: () => ServiceMap.ServiceMap<RT>,
|
|
395
|
-
private readonly toast: Toast,
|
|
396
|
-
private readonly intl: I18n
|
|
397
|
-
) {}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
401
|
-
* you should use the result ref to render errors!
|
|
402
|
-
* @deprecated use `Command.fn` and friends instead
|
|
403
|
-
*/
|
|
404
|
-
readonly useSafeMutation: {
|
|
405
|
-
/**
|
|
406
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
407
|
-
* you should use the result ref to render errors!
|
|
408
|
-
* @deprecated use `Command.fn` and friends instead
|
|
409
|
-
*/
|
|
410
|
-
<I, E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
411
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
412
|
-
options?: MutationOptions<A, E, R, A2, E2, R2, I>
|
|
413
|
-
): readonly [
|
|
414
|
-
ComputedRef<AsyncResult.AsyncResult<A2, E2>>,
|
|
415
|
-
(i: I) => Effect.Effect<Exit.Exit<A2, E2>, never, R2>
|
|
416
|
-
]
|
|
417
|
-
/**
|
|
418
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
419
|
-
* you should use the result ref to render errors!
|
|
420
|
-
* @deprecated use `Command.fn` and friends instead
|
|
421
|
-
*/
|
|
422
|
-
<E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
423
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
424
|
-
options?: MutationOptions<A, E, R, A2, E2, R2>
|
|
425
|
-
): readonly [
|
|
426
|
-
ComputedRef<AsyncResult.AsyncResult<A2, E2>>,
|
|
427
|
-
Effect.Effect<Exit.Exit<A2, E2>, never, R2>
|
|
428
|
-
]
|
|
429
|
-
} = <I, E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
430
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>,
|
|
431
|
-
options?: MutationOptions<A, E, R, A2, E2, R2, I>
|
|
432
|
-
) => {
|
|
433
|
-
const unsafe = _useMutation(self as any, options)
|
|
434
|
-
|
|
435
|
-
type MH = NonNullable<NonNullable<typeof options>["mapHandler"]>
|
|
436
|
-
const mh = options?.mapHandler ?? identity as MH
|
|
437
|
-
|
|
438
|
-
const [a, b] = asResult(
|
|
439
|
-
mapHandler(
|
|
440
|
-
mapHandler(unsafe as any, mh),
|
|
441
|
-
Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause))
|
|
442
|
-
) as any
|
|
443
|
-
)
|
|
444
|
-
return [
|
|
445
|
-
a,
|
|
446
|
-
mapHandler(
|
|
447
|
-
b,
|
|
448
|
-
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })
|
|
449
|
-
)
|
|
450
|
-
] as const as any
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/** handles errors as toasts and reports defects
|
|
454
|
-
* @deprecated use `Command.fn` and friends instead
|
|
455
|
-
*/
|
|
456
|
-
readonly useHandleRequestWithToast = () => {
|
|
457
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
458
|
-
const self = this
|
|
459
|
-
return handleRequestWithToast
|
|
460
|
-
/**
|
|
461
|
-
* Pass a function that returns a Promise.
|
|
462
|
-
* Returns an execution function which reports errors as Toast.
|
|
463
|
-
*/
|
|
464
|
-
function handleRequestWithToast<
|
|
465
|
-
A,
|
|
466
|
-
E extends ResponseErrors,
|
|
467
|
-
R,
|
|
468
|
-
I = void,
|
|
469
|
-
A2 = A,
|
|
470
|
-
E2 extends ResponseErrors = E,
|
|
471
|
-
R2 = R,
|
|
472
|
-
ESuccess = never,
|
|
473
|
-
RSuccess = never,
|
|
474
|
-
EError = never,
|
|
475
|
-
RError = never,
|
|
476
|
-
EDefect = never,
|
|
477
|
-
RDefect = never
|
|
478
|
-
>(
|
|
479
|
-
f: Effect.Effect<Exit.Exit<A2, E2>, never, R2> | ((i: I) => Effect.Effect<Exit.Exit<A2, E2>, never, R2>),
|
|
480
|
-
id: string,
|
|
481
|
-
action: string,
|
|
482
|
-
options: Opts<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect> = {}
|
|
483
|
-
) {
|
|
484
|
-
const actionMessage = self.intl.formatMessage({ id: `action.${action}`, defaultMessage: action })
|
|
485
|
-
const defaultWarnMessage = self.intl.formatMessage(
|
|
486
|
-
{ id: "handle.with_warnings" },
|
|
487
|
-
{ action: actionMessage }
|
|
488
|
-
)
|
|
489
|
-
const defaultSuccessMessage = self.intl.formatMessage(
|
|
490
|
-
{ id: "handle.success" },
|
|
491
|
-
{ action: actionMessage }
|
|
492
|
-
)
|
|
493
|
-
const defaultErrorMessage = self.intl.formatMessage(
|
|
494
|
-
{ id: "handle.with_errors" },
|
|
495
|
-
{ action: actionMessage }
|
|
496
|
-
)
|
|
497
|
-
|
|
498
|
-
return handleRequest<E2, A2, R2, any, ESuccess, RSuccess, EError, RError, EDefect, RDefect>(f, id, action, {
|
|
499
|
-
onSuccess: Effect.fnUntraced(function*(a, i) {
|
|
500
|
-
const message = options.successMessage ? yield* options.successMessage(a, i) : defaultSuccessMessage
|
|
501
|
-
+ (S.is(OperationSuccess)(a) && a.message
|
|
502
|
-
? "\n" + a.message
|
|
503
|
-
: "")
|
|
504
|
-
if (message) {
|
|
505
|
-
yield* self.toast.success(message)
|
|
506
|
-
}
|
|
507
|
-
}),
|
|
508
|
-
onFail: Effect.fnUntraced(function*(e, i) {
|
|
509
|
-
if (!options.failMessage && e._tag === "OperationFailure") {
|
|
510
|
-
yield* self.toast.warning(
|
|
511
|
-
defaultWarnMessage + e.message
|
|
512
|
-
? "\n" + e.message
|
|
513
|
-
: ""
|
|
514
|
-
)
|
|
515
|
-
return
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
const message = options.failMessage
|
|
519
|
-
? yield* options.failMessage(e, i)
|
|
520
|
-
: `${defaultErrorMessage}:\n` + renderError(e)
|
|
521
|
-
if (message) {
|
|
522
|
-
yield* self.toast.error(message)
|
|
523
|
-
}
|
|
524
|
-
}),
|
|
525
|
-
onDefect: Effect.fnUntraced(function*(cause, i) {
|
|
526
|
-
const message = options.defectMessage
|
|
527
|
-
? yield* options.defectMessage(cause, i)
|
|
528
|
-
: self.intl.formatMessage(
|
|
529
|
-
{ id: "handle.unexpected_error" },
|
|
530
|
-
{
|
|
531
|
-
action: actionMessage,
|
|
532
|
-
error: Cause.pretty(cause)
|
|
533
|
-
}
|
|
534
|
-
)
|
|
535
|
-
if (message) {
|
|
536
|
-
yield* self.toast.error(message)
|
|
537
|
-
}
|
|
538
|
-
})
|
|
539
|
-
})
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function renderError(e: ResponseErrors): string {
|
|
543
|
-
return Match.value(e as any).pipe(
|
|
544
|
-
Match.tags({
|
|
545
|
-
// HttpErrorRequest: e =>
|
|
546
|
-
// this.intl.value.formatMessage(
|
|
547
|
-
// { id: "handle.request_error" },
|
|
548
|
-
// { error: `${e.error}` },
|
|
549
|
-
// ),
|
|
550
|
-
// HttpErrorResponse: e =>
|
|
551
|
-
// e.response.status >= 500 ||
|
|
552
|
-
// e.response.body._tag !== "Some" ||
|
|
553
|
-
// !e.response.body.value
|
|
554
|
-
// ? this.intl.value.formatMessage(
|
|
555
|
-
// { id: "handle.error_response" },
|
|
556
|
-
// {
|
|
557
|
-
// error: `${
|
|
558
|
-
// e.response.body._tag === "Some" && e.response.body.value
|
|
559
|
-
// ? parseError(e.response.body.value)
|
|
560
|
-
// : "Unknown"
|
|
561
|
-
// } (${e.response.status})`,
|
|
562
|
-
// },
|
|
563
|
-
// )
|
|
564
|
-
// : this.intl.value.formatMessage(
|
|
565
|
-
// { id: "handle.unexpected_error" },
|
|
566
|
-
// {
|
|
567
|
-
// error:
|
|
568
|
-
// JSON.stringify(e.response.body, undefined, 2) +
|
|
569
|
-
// "( " +
|
|
570
|
-
// e.response.status +
|
|
571
|
-
// ")",
|
|
572
|
-
// },
|
|
573
|
-
// ),
|
|
574
|
-
// ResponseError: e =>
|
|
575
|
-
// this.intl.value.formatMessage(
|
|
576
|
-
// { id: "handle.response_error" },
|
|
577
|
-
// { error: `${e.error}` },
|
|
578
|
-
// ),
|
|
579
|
-
SchemaError: (e: any) => {
|
|
580
|
-
console.warn(e.toString())
|
|
581
|
-
return self.intl.formatMessage({ id: "validation.failed" })
|
|
582
|
-
}
|
|
583
|
-
}),
|
|
584
|
-
Match.orElse((e: any) => `${e.message ?? e._tag ?? e}`)
|
|
585
|
-
)
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
591
|
-
* Returns a tuple with raw Result and execution function which reports success and errors as Toast.
|
|
592
|
-
* @deprecated use `Command.fn` and friends instead
|
|
593
|
-
*/
|
|
594
|
-
readonly useAndHandleMutationResult: {
|
|
595
|
-
/**
|
|
596
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
597
|
-
* Returns a tuple with raw Result and execution function which reports success and errors as Toast.
|
|
598
|
-
* @deprecated use `Command.fn` and friends instead
|
|
599
|
-
*/
|
|
600
|
-
<
|
|
601
|
-
I,
|
|
602
|
-
E extends ResponseErrors,
|
|
603
|
-
A,
|
|
604
|
-
R,
|
|
605
|
-
Request extends Req,
|
|
606
|
-
Name extends string,
|
|
607
|
-
A2 = A,
|
|
608
|
-
E2 extends ResponseErrors = E,
|
|
609
|
-
R2 = R,
|
|
610
|
-
ESuccess = never,
|
|
611
|
-
RSuccess = never,
|
|
612
|
-
EError = never,
|
|
613
|
-
RError = never,
|
|
614
|
-
EDefect = never,
|
|
615
|
-
RDefect = never
|
|
616
|
-
>(
|
|
617
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
618
|
-
action: string,
|
|
619
|
-
options?: Opts<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
620
|
-
): Resp<I, A2, E2, R2, ComputedRef<AsyncResult.AsyncResult<A2, E2>>>
|
|
621
|
-
/**
|
|
622
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
623
|
-
* Returns a tuple with raw Result and execution function which reports success and errors as Toast.
|
|
624
|
-
* @deprecated use `Command.fn` and friends instead
|
|
625
|
-
*/
|
|
626
|
-
<
|
|
627
|
-
E extends ResponseErrors,
|
|
628
|
-
A,
|
|
629
|
-
R,
|
|
630
|
-
Request extends Req,
|
|
631
|
-
Name extends string,
|
|
632
|
-
A2 = A,
|
|
633
|
-
E2 extends ResponseErrors = E,
|
|
634
|
-
R2 = R,
|
|
635
|
-
ESuccess = never,
|
|
636
|
-
RSuccess = never,
|
|
637
|
-
EError = never,
|
|
638
|
-
RError = never,
|
|
639
|
-
EDefect = never,
|
|
640
|
-
RDefect = never
|
|
641
|
-
>(
|
|
642
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
643
|
-
action: string,
|
|
644
|
-
options?: Opts<A, E, R, void, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
645
|
-
): ActResp<A2, E2, R2, ComputedRef<AsyncResult.AsyncResult<A2, E2>>>
|
|
646
|
-
} = <E extends ResponseErrors, A, R, Request extends Req, Name extends string, I>(
|
|
647
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>,
|
|
648
|
-
action: any,
|
|
649
|
-
options?: Opts<any, any, any, any, any, any, any, any, any, any, any, any, any>
|
|
650
|
-
): any => {
|
|
651
|
-
const handleRequestWithToast = this.useHandleRequestWithToast()
|
|
652
|
-
const handler = self.handler
|
|
653
|
-
const unsafe = _useMutation({
|
|
654
|
-
...self,
|
|
655
|
-
handler: Effect.isEffect(handler)
|
|
656
|
-
? (pipe(
|
|
657
|
-
Effect.annotateCurrentSpan({ action }),
|
|
658
|
-
Effect.andThen(handler)
|
|
659
|
-
) as any)
|
|
660
|
-
: (...args: [any]) =>
|
|
661
|
-
pipe(
|
|
662
|
-
Effect.annotateCurrentSpan({ action }),
|
|
663
|
-
Effect.andThen(handler(...args))
|
|
664
|
-
)
|
|
665
|
-
}, options ? dropUndefinedT(options) : undefined)
|
|
666
|
-
|
|
667
|
-
type MH = NonNullable<NonNullable<typeof options>["mapHandler"]>
|
|
668
|
-
const mh = options?.mapHandler ?? identity as MH
|
|
669
|
-
|
|
670
|
-
// Effect.tapDefect(reportRuntimeError) handled in toast handler,
|
|
671
|
-
const [a, b] = asResult(mapHandler(unsafe, mh) as any)
|
|
672
|
-
|
|
673
|
-
return tuple(
|
|
674
|
-
a,
|
|
675
|
-
handleRequestWithToast(b as any, self.id, action, options)
|
|
676
|
-
)
|
|
677
|
-
}
|
|
678
|
-
//
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
682
|
-
* Returns a tuple with state ref and execution function which reports success and errors as Toast.
|
|
683
|
-
*
|
|
684
|
-
* @deprecated use `Command.fn` and friends instead
|
|
685
|
-
*/
|
|
686
|
-
readonly useAndHandleMutation: {
|
|
687
|
-
/**
|
|
688
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
689
|
-
* Returns a tuple with state ref and execution function which reports success and errors as Toast.
|
|
690
|
-
*
|
|
691
|
-
* @deprecated use `Command.fn` and friends instead
|
|
692
|
-
*/
|
|
693
|
-
<
|
|
694
|
-
I,
|
|
695
|
-
E extends ResponseErrors,
|
|
696
|
-
A,
|
|
697
|
-
R,
|
|
698
|
-
Request extends Req,
|
|
699
|
-
Name extends string,
|
|
700
|
-
A2 = A,
|
|
701
|
-
E2 extends ResponseErrors = E,
|
|
702
|
-
R2 = R,
|
|
703
|
-
ESuccess = never,
|
|
704
|
-
RSuccess = never,
|
|
705
|
-
EError = never,
|
|
706
|
-
RError = never,
|
|
707
|
-
EDefect = never,
|
|
708
|
-
RDefect = never
|
|
709
|
-
>(
|
|
710
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
711
|
-
action: string,
|
|
712
|
-
options?: Opts<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
713
|
-
): Resp<I, A2, E2, R2>
|
|
714
|
-
/**
|
|
715
|
-
* Pass a function that returns an Effect, e.g from a client action, give it a name.
|
|
716
|
-
* Returns a tuple with state ref and execution function which reports success and errors as Toast.
|
|
717
|
-
*
|
|
718
|
-
* @deprecated use `Command.fn` and friends instead
|
|
719
|
-
*/
|
|
720
|
-
<
|
|
721
|
-
E extends ResponseErrors,
|
|
722
|
-
A,
|
|
723
|
-
R,
|
|
724
|
-
Request extends Req,
|
|
725
|
-
Name extends string,
|
|
726
|
-
A2 = A,
|
|
727
|
-
E2 extends ResponseErrors = E,
|
|
728
|
-
R2 = R,
|
|
729
|
-
ESuccess = never,
|
|
730
|
-
RSuccess = never,
|
|
731
|
-
EError = never,
|
|
732
|
-
RError = never,
|
|
733
|
-
EDefect = never,
|
|
734
|
-
RDefect = never
|
|
735
|
-
>(
|
|
736
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
737
|
-
action: string,
|
|
738
|
-
options?: Opts<A, E, R, void, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
739
|
-
): ActResp<A2, E2, R2>
|
|
740
|
-
} = (
|
|
741
|
-
self: any,
|
|
742
|
-
action: any,
|
|
743
|
-
options?: Opts<any, any, any, any, any, any, any, any, any, any, any, any, any>
|
|
744
|
-
): any => {
|
|
745
|
-
const [a, b] = this.useAndHandleMutationResult(self, action, options)
|
|
746
|
-
|
|
747
|
-
return tuple(
|
|
748
|
-
computed(() => mutationResultToVue(a.value)),
|
|
749
|
-
b
|
|
750
|
-
)
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/** @deprecated use `Command.fn` and friends instead */
|
|
754
|
-
readonly makeUseAndHandleMutation = (
|
|
755
|
-
defaultOptions?: Opts<any, any, any, any, any, any, any, any, any>
|
|
756
|
-
) =>
|
|
757
|
-
((self: any, action: any, options: any) => {
|
|
758
|
-
return this.useAndHandleMutation(
|
|
759
|
-
self,
|
|
760
|
-
action,
|
|
761
|
-
{ ...defaultOptions, ...options }
|
|
762
|
-
)
|
|
763
|
-
}) as unknown as {
|
|
764
|
-
<
|
|
765
|
-
I,
|
|
766
|
-
E extends ResponseErrors,
|
|
767
|
-
A,
|
|
768
|
-
R,
|
|
769
|
-
Request extends Req,
|
|
770
|
-
Name extends string,
|
|
771
|
-
A2 = A,
|
|
772
|
-
E2 extends ResponseErrors = E,
|
|
773
|
-
R2 = R,
|
|
774
|
-
ESuccess = never,
|
|
775
|
-
RSuccess = never,
|
|
776
|
-
EError = never,
|
|
777
|
-
RError = never,
|
|
778
|
-
EDefect = never,
|
|
779
|
-
RDefect = never
|
|
780
|
-
>(
|
|
781
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
782
|
-
action: string,
|
|
783
|
-
options?: Opts<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
784
|
-
): Resp<I, A2, E2, R2>
|
|
785
|
-
<
|
|
786
|
-
E extends ResponseErrors,
|
|
787
|
-
A,
|
|
788
|
-
R,
|
|
789
|
-
Request extends Req,
|
|
790
|
-
Name extends string,
|
|
791
|
-
A2 = A,
|
|
792
|
-
E2 extends ResponseErrors = E,
|
|
793
|
-
R2 = R,
|
|
794
|
-
ESuccess = never,
|
|
795
|
-
RSuccess = never,
|
|
796
|
-
EError = never,
|
|
797
|
-
RError = never,
|
|
798
|
-
EDefect = never,
|
|
799
|
-
RDefect = never
|
|
800
|
-
>(
|
|
801
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
802
|
-
action: string,
|
|
803
|
-
options?: Opts<A, E, R, void, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
804
|
-
): ActResp<A2, E2, R2>
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
/**
|
|
808
|
-
* The same as @see useAndHandleMutation, but does not display any toasts by default.
|
|
809
|
-
* Messages for success, error and defect toasts can be provided in the Options.
|
|
810
|
-
* @deprecated use `Command.fn` and friends instead
|
|
811
|
-
*/
|
|
812
|
-
readonly useAndHandleMutationSilently: {
|
|
813
|
-
/**
|
|
814
|
-
* The same as @see useAndHandleMutation, but does not display any toasts by default.
|
|
815
|
-
* Messages for success, error and defect toasts can be provided in the Options.
|
|
816
|
-
* @deprecated use `Command.fn` and friends instead
|
|
817
|
-
*/
|
|
818
|
-
<
|
|
819
|
-
I,
|
|
820
|
-
E extends ResponseErrors,
|
|
821
|
-
A,
|
|
822
|
-
R,
|
|
823
|
-
Request extends Req,
|
|
824
|
-
Name extends string,
|
|
825
|
-
A2 = A,
|
|
826
|
-
E2 extends ResponseErrors = E,
|
|
827
|
-
R2 = R,
|
|
828
|
-
ESuccess = never,
|
|
829
|
-
RSuccess = never,
|
|
830
|
-
EError = never,
|
|
831
|
-
RError = never,
|
|
832
|
-
EDefect = never,
|
|
833
|
-
RDefect = never
|
|
834
|
-
>(
|
|
835
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
836
|
-
action: string,
|
|
837
|
-
options?: Opts<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
838
|
-
): Resp<I, A2, E2, R>
|
|
839
|
-
/**
|
|
840
|
-
* The same as @see useAndHandleMutation, but does not display any toasts by default.
|
|
841
|
-
* Messages for success, error and defect toasts can be provided in the Options.
|
|
842
|
-
* @deprecated use `Command.fn` and friends instead
|
|
843
|
-
*/
|
|
844
|
-
<
|
|
845
|
-
E extends ResponseErrors,
|
|
846
|
-
A,
|
|
847
|
-
R,
|
|
848
|
-
Request extends Req,
|
|
849
|
-
Name extends string,
|
|
850
|
-
A2 = A,
|
|
851
|
-
E2 extends ResponseErrors = E,
|
|
852
|
-
R2 = R,
|
|
853
|
-
ESuccess = never,
|
|
854
|
-
RSuccess = never,
|
|
855
|
-
EError = never,
|
|
856
|
-
RError = never,
|
|
857
|
-
EDefect = never,
|
|
858
|
-
RDefect = never
|
|
859
|
-
>(
|
|
860
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
861
|
-
action: string,
|
|
862
|
-
options?: Opts<A, E, R, void, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
863
|
-
): ActResp<void, never, R>
|
|
864
|
-
} = this.makeUseAndHandleMutation({
|
|
865
|
-
successMessage: suppressToast,
|
|
866
|
-
failMessage: suppressToast,
|
|
867
|
-
defectMessage: suppressToast
|
|
868
|
-
}) as any
|
|
869
|
-
|
|
870
|
-
/**
|
|
871
|
-
* The same as @see useAndHandleMutation, but does not act on success, error or defect by default.
|
|
872
|
-
* Actions for success, error and defect can be provided in the Options.
|
|
873
|
-
* @deprecated use `Command.fn` and friends instead
|
|
874
|
-
*/
|
|
875
|
-
readonly useAndHandleMutationCustom: {
|
|
876
|
-
/**
|
|
877
|
-
* The same as @see useAndHandleMutation, but does not act on success, error or defect by default.
|
|
878
|
-
* Actions for success, error and defect can be provided in the Options.
|
|
879
|
-
* @deprecated use `Command.fn` and friends instead
|
|
880
|
-
*/
|
|
881
|
-
<
|
|
882
|
-
I,
|
|
883
|
-
E extends ResponseErrors,
|
|
884
|
-
A,
|
|
885
|
-
R,
|
|
886
|
-
Request extends Req,
|
|
887
|
-
Name extends string,
|
|
888
|
-
A2 = A,
|
|
889
|
-
E2 extends ResponseErrors = E,
|
|
890
|
-
R2 = R,
|
|
891
|
-
ESuccess = never,
|
|
892
|
-
RSuccess = never,
|
|
893
|
-
EError = never,
|
|
894
|
-
RError = never,
|
|
895
|
-
EDefect = never,
|
|
896
|
-
RDefect = never
|
|
897
|
-
>(
|
|
898
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
899
|
-
action: string,
|
|
900
|
-
options?: LowOptsOptional<A, E, R, I, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
901
|
-
): Resp<I, A2, E2, R2>
|
|
902
|
-
/**
|
|
903
|
-
* The same as @see useAndHandleMutation, but does not act on success, error or defect by default.
|
|
904
|
-
* Actions for success, error and defect can be provided in the Options.
|
|
905
|
-
* @deprecated use `Command.fn` and friends instead
|
|
906
|
-
*/
|
|
907
|
-
<
|
|
908
|
-
E extends ResponseErrors,
|
|
909
|
-
A,
|
|
910
|
-
R,
|
|
911
|
-
Request extends Req,
|
|
912
|
-
Name extends string,
|
|
913
|
-
A2 = A,
|
|
914
|
-
E2 extends ResponseErrors = E,
|
|
915
|
-
R2 = R,
|
|
916
|
-
ESuccess = never,
|
|
917
|
-
RSuccess = never,
|
|
918
|
-
EError = never,
|
|
919
|
-
RError = never,
|
|
920
|
-
EDefect = never,
|
|
921
|
-
RDefect = never
|
|
922
|
-
>(
|
|
923
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
924
|
-
action: string,
|
|
925
|
-
options?: LowOptsOptional<A, E, R, void, A2, E2, R2, ESuccess, RSuccess, EError, RError, EDefect, RDefect>
|
|
926
|
-
): ActResp<A2, E2, R2>
|
|
927
|
-
} = (self: any, action: string, options: any) => {
|
|
928
|
-
const unsafe = _useMutation({
|
|
929
|
-
...self,
|
|
930
|
-
handler: Effect.isEffect(self.handler)
|
|
931
|
-
? (pipe(
|
|
932
|
-
Effect.annotateCurrentSpan({ action }),
|
|
933
|
-
Effect.andThen(self.handler)
|
|
934
|
-
) as any)
|
|
935
|
-
: (...args: any[]) =>
|
|
936
|
-
pipe(
|
|
937
|
-
Effect.annotateCurrentSpan({ action }),
|
|
938
|
-
Effect.andThen(self.handler(...args))
|
|
939
|
-
)
|
|
940
|
-
}, options ? dropUndefinedT(options) : undefined)
|
|
941
|
-
|
|
942
|
-
type MH = NonNullable<NonNullable<typeof options>["mapHandler"]>
|
|
943
|
-
const mh = options?.mapHandler ?? identity as MH
|
|
944
|
-
|
|
945
|
-
const [a, b] = asResult(
|
|
946
|
-
mapHandler(
|
|
947
|
-
mapHandler(unsafe as any, mh),
|
|
948
|
-
Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause))
|
|
949
|
-
) as any
|
|
950
|
-
)
|
|
951
|
-
|
|
952
|
-
return tuple(
|
|
953
|
-
computed(() => mutationResultToVue(a.value)),
|
|
954
|
-
handleRequest(b as any, self.id, action, {
|
|
955
|
-
onSuccess: suppressToast,
|
|
956
|
-
onDefect: suppressToast,
|
|
957
|
-
onFail: suppressToast,
|
|
958
|
-
...options
|
|
959
|
-
})
|
|
960
|
-
) as any
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
/**
|
|
964
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
965
|
-
* you should use the result ref to render errors!
|
|
966
|
-
* @deprecated use `Command.fn` and friends instead
|
|
967
|
-
*/
|
|
968
|
-
readonly useSafeMutationWithState: {
|
|
969
|
-
/**
|
|
970
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
971
|
-
* you should use the result ref to render errors!
|
|
972
|
-
* @deprecated use `Command.fn` and friends instead
|
|
973
|
-
*/
|
|
974
|
-
<I, E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
975
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
976
|
-
options?: MutationOptions<A, E, R, A2, E2, R2, I>
|
|
977
|
-
): readonly [
|
|
978
|
-
ComputedRef<Res<A, E>>,
|
|
979
|
-
(i: I) => Effect.Effect<Exit.Exit<A2, E2>, never, R2>
|
|
980
|
-
]
|
|
981
|
-
/**
|
|
982
|
-
* Effect results are converted to Exit, so errors are ignored by default.
|
|
983
|
-
* you should use the result ref to render errors!
|
|
984
|
-
* @deprecated use `Command.fn` and friends instead
|
|
985
|
-
*/
|
|
986
|
-
<E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
987
|
-
self: RequestHandler<A, E, R, Request, Name>,
|
|
988
|
-
options?: MutationOptions<A, E, R, A2, E2, R2>
|
|
989
|
-
): readonly [
|
|
990
|
-
ComputedRef<Res<A, E>>,
|
|
991
|
-
Effect.Effect<Exit.Exit<A2, E2>, never, R2>
|
|
992
|
-
]
|
|
993
|
-
} = <I, E, A, R, Request extends Req, Name extends string, A2 = A, E2 = E, R2 = R>(
|
|
994
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>,
|
|
995
|
-
options?: MutationOptions<A, E, R, A2, E2, R2, I>
|
|
996
|
-
) => {
|
|
997
|
-
const [a, b] = this.useSafeMutation(self as any, options)
|
|
998
|
-
|
|
999
|
-
return tuple(
|
|
1000
|
-
computed(() => mutationResultToVue(a.value)),
|
|
1001
|
-
b
|
|
1002
|
-
) as any
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
/** @deprecated use OmegaForm */
|
|
1006
|
-
readonly buildFormFromSchema = <
|
|
1007
|
-
From extends Record<PropertyKey, any>,
|
|
1008
|
-
To extends Record<PropertyKey, any>,
|
|
1009
|
-
C extends Record<PropertyKey, any>,
|
|
1010
|
-
OnSubmitA
|
|
1011
|
-
>(
|
|
1012
|
-
s:
|
|
1013
|
-
& S.Codec<To>
|
|
1014
|
-
& { new(c: C): any; extend: any; fields: S.Struct.Fields },
|
|
1015
|
-
state: Ref<Omit<From, "_tag">>,
|
|
1016
|
-
onSubmit: (a: To) => Effect.Effect<OnSubmitA, never, RT>
|
|
1017
|
-
) => {
|
|
1018
|
-
const fields = buildFieldInfoFromFieldsRoot(s).fields
|
|
1019
|
-
const schema = S.Struct(Struct.omit(s.fields, ["_tag"])) as unknown as S.Codec<any> & {
|
|
1020
|
-
readonly DecodingServices: never
|
|
1021
|
-
}
|
|
1022
|
-
const parse = S.decodeUnknownSync(schema)
|
|
1023
|
-
const isDirty = ref(false)
|
|
1024
|
-
const isValid = ref(true)
|
|
1025
|
-
const isLoading = ref(false)
|
|
1026
|
-
const runPromise = Effect.runPromiseWith(this.getRuntime())
|
|
1027
|
-
|
|
1028
|
-
const submit1 =
|
|
1029
|
-
(onSubmit: (a: To) => Effect.Effect<OnSubmitA, never, never>) =>
|
|
1030
|
-
async <T extends Promise<{ valid: boolean }>>(e: T) => {
|
|
1031
|
-
isLoading.value = true
|
|
1032
|
-
try {
|
|
1033
|
-
const r = await e
|
|
1034
|
-
if (!r.valid) return
|
|
1035
|
-
return await runPromise(onSubmit(new (s as any)(await runPromise(parse(state.value)))) as any)
|
|
1036
|
-
} finally {
|
|
1037
|
-
isLoading.value = false
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
const submit = submit1(onSubmit as any)
|
|
1041
|
-
|
|
1042
|
-
watch(
|
|
1043
|
-
state,
|
|
1044
|
-
(v) => {
|
|
1045
|
-
// TODO: do better
|
|
1046
|
-
isDirty.value = JSON.stringify(v) !== JSON.stringify(state.value)
|
|
1047
|
-
},
|
|
1048
|
-
{ deep: true }
|
|
1049
|
-
)
|
|
1050
|
-
|
|
1051
|
-
const submitFromState = Effect.gen(function*() {
|
|
1052
|
-
return yield* (onSubmit(yield* parse(state.value)) as any)
|
|
1053
|
-
})
|
|
1054
|
-
|
|
1055
|
-
const submitFromStatePromise = () => runPromise(submitFromState as any)
|
|
1056
|
-
|
|
1057
|
-
return {
|
|
1058
|
-
fields,
|
|
1059
|
-
/** optimized for Vuetify v-form submit callback */
|
|
1060
|
-
submit,
|
|
1061
|
-
/** optimized for Native form submit callback or general use */
|
|
1062
|
-
submitFromState,
|
|
1063
|
-
submitFromStatePromise,
|
|
1064
|
-
isDirty,
|
|
1065
|
-
isValid,
|
|
1066
|
-
isLoading
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
1072
|
-
export class LegacyMutation extends ServiceMap.Service<LegacyMutation>()("LegacyMutation", {
|
|
1073
|
-
make: Effect.gen(function*() {
|
|
1074
|
-
const intl = yield* I18n
|
|
1075
|
-
const toast = yield* Toast
|
|
1076
|
-
|
|
1077
|
-
return <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => new LegacyMutationImpl(getRuntime, toast, intl)
|
|
1078
|
-
})
|
|
1079
|
-
}) {
|
|
1080
|
-
static readonly DefaultWithoutDependencies = Layer.effect(this, this.make)
|
|
1081
|
-
static readonly Default = this.DefaultWithoutDependencies
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
export type ClientFrom<M extends Requests> = RequestHandlers<never, never, M, M["meta"]["moduleName"]>
|
|
440
|
+
export type ClientFrom<M extends RequestsAny> = RequestHandlers<never, never, M, ExtractModuleName<M>>
|
|
1085
441
|
|
|
1086
442
|
export class QueryImpl<R> {
|
|
1087
|
-
constructor(readonly getRuntime: () =>
|
|
443
|
+
constructor(readonly getRuntime: () => Context.Context<R>) {
|
|
1088
444
|
this.useQuery = makeQuery(this.getRuntime)
|
|
1089
445
|
}
|
|
1090
446
|
/**
|
|
@@ -1163,7 +519,7 @@ export class QueryImpl<R> {
|
|
|
1163
519
|
} = <Arg, E, A, Request extends Req, Name extends string>(
|
|
1164
520
|
self: RequestHandlerWithInput<Arg, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>
|
|
1165
521
|
) => {
|
|
1166
|
-
const runPromise =
|
|
522
|
+
const runPromise = makeRunPromise(this.getRuntime())
|
|
1167
523
|
const q = this.useQuery(self as any) as any
|
|
1168
524
|
return (argOrOptions?: any, options?: any) => {
|
|
1169
525
|
const [resultRef, latestRef, fetch, uqrt] = q(argOrOptions, { ...options, suspense: true } // experimental_prefetchInRender: true }
|
|
@@ -1214,10 +570,64 @@ export class QueryImpl<R> {
|
|
|
1214
570
|
}
|
|
1215
571
|
|
|
1216
572
|
// somehow mrt.runtimeEffect doesnt work sync, but this workaround works fine? not sure why though as the layers are generally only sync
|
|
1217
|
-
const managedRuntimeRt = <A, E>(mrt: ManagedRuntime.ManagedRuntime<A, E>) => mrt.runSync(Effect.
|
|
573
|
+
const managedRuntimeRt = <A, E>(mrt: ManagedRuntime.ManagedRuntime<A, E>) => mrt.runSync(Effect.context<A>())
|
|
1218
574
|
|
|
1219
575
|
type Base = I18n | Toast
|
|
1220
|
-
type Mix = ApiClientFactory | Commander |
|
|
576
|
+
type Mix = ApiClientFactory | Commander | Base
|
|
577
|
+
|
|
578
|
+
type InvalidationResources = Record<string, Record<string, unknown>>
|
|
579
|
+
type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I
|
|
580
|
+
: never
|
|
581
|
+
|
|
582
|
+
type CommandInvalidationResources<Req> = Req extends {
|
|
583
|
+
readonly type: "command"
|
|
584
|
+
readonly "~invalidationResources"?: infer Resources
|
|
585
|
+
} ? NonNullable<Resources> extends InvalidationResources ? NonNullable<Resources> : never
|
|
586
|
+
: Req extends {
|
|
587
|
+
readonly type: "command"
|
|
588
|
+
readonly config?: infer Config
|
|
589
|
+
} ? Config extends {
|
|
590
|
+
readonly invalidationResources?: infer LegacyResources
|
|
591
|
+
} ? NonNullable<LegacyResources> extends InvalidationResources ? NonNullable<LegacyResources> : never
|
|
592
|
+
: Config extends {
|
|
593
|
+
readonly invalidatesQueries?: InvalidationCallback<infer LegacyResources, any, any, any>
|
|
594
|
+
} ? NonNullable<LegacyResources> extends InvalidationResources ? NonNullable<LegacyResources> : never
|
|
595
|
+
: never
|
|
596
|
+
: never
|
|
597
|
+
|
|
598
|
+
type InvalidationResourcesForUnion<M extends RequestsAny> = {
|
|
599
|
+
[K in keyof M]: CommandInvalidationResources<M[K]>
|
|
600
|
+
}[keyof M]
|
|
601
|
+
|
|
602
|
+
type InvalidationResourcesFor<M extends RequestsAny> = [InvalidationResourcesForUnion<M>] extends [never] ? never
|
|
603
|
+
: UnionToIntersection<InvalidationResourcesForUnion<M>> extends infer R ? R extends InvalidationResources ? R
|
|
604
|
+
: never
|
|
605
|
+
: never
|
|
606
|
+
|
|
607
|
+
type QueryInvalidationFactory<M extends RequestsAny> = (client: ClientFrom<M>) => QueryInvalidation<M>
|
|
608
|
+
|
|
609
|
+
type StrictResourcesArg<Shape, Actual extends Shape = Shape> =
|
|
610
|
+
& Actual
|
|
611
|
+
& Record<Exclude<keyof Actual, keyof Shape>, never>
|
|
612
|
+
|
|
613
|
+
type ClientForArgs<
|
|
614
|
+
M extends RequestsAny,
|
|
615
|
+
Resources extends InvalidationResourcesFor<M> = InvalidationResourcesFor<M>
|
|
616
|
+
> = [InvalidationResourcesFor<M>] extends [never] ? [
|
|
617
|
+
queryInvalidation?: QueryInvalidationFactory<M>,
|
|
618
|
+
invalidationResources?: StrictResourcesArg<
|
|
619
|
+
InvalidationResourcesFor<M>,
|
|
620
|
+
Resources
|
|
621
|
+
>
|
|
622
|
+
]
|
|
623
|
+
: [
|
|
624
|
+
queryInvalidation: QueryInvalidationFactory<M> | undefined,
|
|
625
|
+
invalidationResources: StrictResourcesArg<
|
|
626
|
+
InvalidationResourcesFor<M>,
|
|
627
|
+
Resources
|
|
628
|
+
>
|
|
629
|
+
]
|
|
630
|
+
|
|
1221
631
|
export const makeClient = <RT_, RTHooks>(
|
|
1222
632
|
// global, but only accessible after startup has completed
|
|
1223
633
|
getBaseMrt: () => ManagedRuntime.ManagedRuntime<RT_ | Mix, never>,
|
|
@@ -1225,54 +635,66 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
1225
635
|
rtHooks: Layer.Layer<RTHooks, never, Mix>
|
|
1226
636
|
) => {
|
|
1227
637
|
type RT = RT_ | Mix
|
|
1228
|
-
const getRt = Effect.services<RT>()
|
|
1229
638
|
const getBaseRt = () => managedRuntimeRt(getBaseMrt())
|
|
1230
639
|
const makeCommand = makeUseCommand<RT, RTHooks>(rtHooks)
|
|
1231
|
-
const makeMutation = Effect.gen(function*() {
|
|
1232
|
-
const mut = yield* LegacyMutation
|
|
1233
|
-
|
|
1234
|
-
return mut(() => getBaseMrt().runSync(getRt))
|
|
1235
|
-
})
|
|
1236
640
|
let cmd: Effect.Success<typeof makeCommand>
|
|
1237
641
|
const useCommand = () => cmd ??= getBaseMrt().runSync(makeCommand)
|
|
1238
|
-
let mut: Effect.Success<typeof makeMutation>
|
|
1239
|
-
const getMutation = () => mut ??= getBaseMrt().runSync(makeMutation)
|
|
1240
642
|
|
|
1241
643
|
let m: ReturnType<typeof useMutationInt>
|
|
1242
644
|
const useMutation = () => m ??= useMutationInt()
|
|
1243
645
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
"useAndHandleMutation",
|
|
1247
|
-
"useAndHandleMutationResult",
|
|
1248
|
-
"useAndHandleMutationSilently",
|
|
1249
|
-
"useAndHandleMutationCustom",
|
|
1250
|
-
"makeUseAndHandleMutation",
|
|
1251
|
-
"useHandleRequestWithToast",
|
|
1252
|
-
"buildFormFromSchema",
|
|
1253
|
-
"useSafeMutation"
|
|
1254
|
-
] as const satisfies readonly (keyof ReturnType<typeof getMutation>)[]
|
|
1255
|
-
type mut = Pick<LegacyMutationImpl<RT>, typeof keys[number]>
|
|
1256
|
-
|
|
1257
|
-
const mutations = keys.reduce(
|
|
1258
|
-
(prev, cur) => {
|
|
1259
|
-
;(prev as any)[cur] = ((...args: [any]) => {
|
|
1260
|
-
return (getMutation() as any)[cur](...args)
|
|
1261
|
-
}) as any
|
|
1262
|
-
return prev
|
|
1263
|
-
},
|
|
1264
|
-
{} as Pick<LegacyMutationImpl<RT>, typeof keys[number]>
|
|
1265
|
-
)
|
|
646
|
+
let sm: ReturnType<typeof makeStreamMutation>
|
|
647
|
+
const useStreamMutation = () => sm ??= makeStreamMutation()
|
|
1266
648
|
|
|
1267
649
|
const query = new QueryImpl(getBaseRt)
|
|
1268
650
|
const useQuery = query.useQuery
|
|
1269
651
|
const useSuspenseQuery = query.useSuspenseQuery
|
|
1270
652
|
|
|
1271
|
-
const
|
|
653
|
+
const mergeInvalidation = (
|
|
654
|
+
a?: MutationOptionsBase["queryInvalidation"],
|
|
655
|
+
b?: MutationOptionsBase["queryInvalidation"]
|
|
656
|
+
): MutationOptionsBase["queryInvalidation"] | undefined => {
|
|
657
|
+
if (!a && !b) {
|
|
658
|
+
return undefined
|
|
659
|
+
}
|
|
660
|
+
return (defaultKey, name, input, output) => [
|
|
661
|
+
...(a?.(defaultKey, name, input, output) ?? []),
|
|
662
|
+
...(b?.(defaultKey, name, input, output) ?? [])
|
|
663
|
+
]
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const withDefaultInvalidation = (
|
|
667
|
+
mut: any,
|
|
668
|
+
isWithInput: boolean,
|
|
669
|
+
defaultInvalidation?: MutationOptionsBase["queryInvalidation"]
|
|
670
|
+
) => {
|
|
671
|
+
if (!defaultInvalidation) return mut
|
|
672
|
+
const apply = (callerOpts?: MutationOptionsBase) => ({
|
|
673
|
+
...callerOpts,
|
|
674
|
+
queryInvalidation: callerOpts?.queryInvalidation
|
|
675
|
+
? mergeInvalidation(defaultInvalidation, callerOpts.queryInvalidation)
|
|
676
|
+
: defaultInvalidation
|
|
677
|
+
})
|
|
678
|
+
return isWithInput
|
|
679
|
+
? (input: any, callerOpts?: MutationOptionsBase) => mut(input, apply(callerOpts))
|
|
680
|
+
: (callerOpts?: MutationOptionsBase) => mut(apply(callerOpts))
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const makeQueryResources = <Resources extends InvalidationResources>(resources: Resources | undefined) => {
|
|
684
|
+
if (!resources) {
|
|
685
|
+
return {} as Record<string, Record<string, unknown>>
|
|
686
|
+
}
|
|
687
|
+
return resources as Record<string, Record<string, unknown>>
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const mapQuery = <M extends RequestsAny>(
|
|
1272
691
|
client: ClientFrom<M>
|
|
1273
692
|
) => {
|
|
1274
693
|
const queries = Struct.keys(client).reduce(
|
|
1275
694
|
(acc, key) => {
|
|
695
|
+
if (client[key].Request.type !== "query") {
|
|
696
|
+
return acc
|
|
697
|
+
}
|
|
1276
698
|
;(acc as any)[camelCase(key) + "Query"] = Object.assign(useQuery(client[key] as any), {
|
|
1277
699
|
id: client[key].id
|
|
1278
700
|
})
|
|
@@ -1284,26 +706,35 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
1284
706
|
{} as
|
|
1285
707
|
& {
|
|
1286
708
|
// apparently can't get JSDoc in here..
|
|
1287
|
-
[
|
|
709
|
+
[
|
|
710
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
711
|
+
: `${ToCamel<string & Key>}Query`
|
|
712
|
+
]: Queries<RT, QueryHandler<typeof client[Key]>>["query"]
|
|
1288
713
|
}
|
|
1289
714
|
// todo: or suspense as an Option?
|
|
1290
715
|
& {
|
|
1291
716
|
// apparently can't get JSDoc in here..
|
|
1292
|
-
[
|
|
717
|
+
[
|
|
718
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
719
|
+
: `${ToCamel<string & Key>}SuspenseQuery`
|
|
720
|
+
]: Queries<
|
|
1293
721
|
RT,
|
|
1294
|
-
typeof client[Key]
|
|
722
|
+
QueryHandler<typeof client[Key]>
|
|
1295
723
|
>["suspense"]
|
|
1296
724
|
}
|
|
1297
725
|
)
|
|
1298
726
|
return queries
|
|
1299
727
|
}
|
|
1300
728
|
|
|
1301
|
-
const mapRequest = <M extends
|
|
729
|
+
const mapRequest = <M extends RequestsAny>(
|
|
1302
730
|
client: ClientFrom<M>
|
|
1303
731
|
) => {
|
|
1304
732
|
const Command = useCommand()
|
|
1305
733
|
const mutations = Struct.keys(client).reduce(
|
|
1306
734
|
(acc, key) => {
|
|
735
|
+
if (client[key].Request.type !== "command") {
|
|
736
|
+
return acc
|
|
737
|
+
}
|
|
1307
738
|
const mut = client[key].handler
|
|
1308
739
|
const fn = Command.fn(client[key].id)
|
|
1309
740
|
const wrap = Command.wrap({ mutate: Effect.isEffect(mut) ? () => mut : mut, id: client[key].id })
|
|
@@ -1315,118 +746,323 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
1315
746
|
return acc
|
|
1316
747
|
},
|
|
1317
748
|
{} as {
|
|
1318
|
-
[
|
|
749
|
+
[
|
|
750
|
+
Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
|
|
751
|
+
: `${ToCamel<string & Key>}Request`
|
|
752
|
+
]: CommandRequestWithExtensions<
|
|
1319
753
|
RT | RTHooks,
|
|
1320
|
-
typeof client[Key]
|
|
754
|
+
CommandHandler<typeof client[Key]>
|
|
1321
755
|
>
|
|
1322
756
|
}
|
|
1323
757
|
)
|
|
1324
758
|
return mutations
|
|
1325
759
|
}
|
|
1326
760
|
|
|
1327
|
-
const mapMutation = <M extends
|
|
1328
|
-
client: ClientFrom<M
|
|
761
|
+
const mapMutation = <M extends RequestsAny>(
|
|
762
|
+
client: ClientFrom<M>,
|
|
763
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
764
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
1329
765
|
) => {
|
|
1330
766
|
const Command = useCommand()
|
|
1331
767
|
const mutation = useMutation()
|
|
768
|
+
const invalidation = queryInvalidation?.(client)
|
|
769
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
1332
770
|
const mutations = Struct.keys(client).reduce(
|
|
1333
771
|
(acc, key) => {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
772
|
+
if (client[key].Request.type !== "command") {
|
|
773
|
+
return acc
|
|
774
|
+
}
|
|
775
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
776
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
777
|
+
| undefined
|
|
778
|
+
const fromRequest = fromRequestConfig
|
|
779
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
780
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((entry) => ({
|
|
781
|
+
filters: entry.filters,
|
|
782
|
+
options: entry.options
|
|
783
|
+
})))
|
|
784
|
+
: undefined
|
|
785
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
786
|
+
const makeProjectedMutation = (handler: any): any => {
|
|
787
|
+
const isWithInput = !Effect.isEffect(handler.handler)
|
|
788
|
+
const mut: any = withDefaultInvalidation(mutation(handler), isWithInput, mergedInvalidation)
|
|
789
|
+
const wrap = Command.wrap({ mutate: mut, id: client[key].id })
|
|
790
|
+
return Object.assign(mut, {
|
|
791
|
+
wrap,
|
|
792
|
+
project: (projectionSchema: any) => {
|
|
793
|
+
const projected = {
|
|
794
|
+
...handler,
|
|
795
|
+
handler: projectHandler(handler.handler, client[key].Request.success, projectionSchema)
|
|
796
|
+
}
|
|
797
|
+
return makeProjectedMutation(projected)
|
|
798
|
+
}
|
|
799
|
+
})
|
|
800
|
+
}
|
|
801
|
+
;(acc as any)[camelCase(key) + "Mutation"] = makeProjectedMutation(client[key] as any)
|
|
1342
802
|
return acc
|
|
1343
803
|
},
|
|
1344
804
|
{} as {
|
|
1345
|
-
[
|
|
805
|
+
[
|
|
806
|
+
Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
|
|
807
|
+
: `${ToCamel<string & Key>}Mutation`
|
|
808
|
+
]: MutationWithExtensions<
|
|
1346
809
|
RT | RTHooks,
|
|
1347
|
-
typeof client[Key]
|
|
810
|
+
CommandHandler<typeof client[Key]>
|
|
1348
811
|
>
|
|
1349
812
|
}
|
|
1350
813
|
)
|
|
1351
814
|
return mutations
|
|
1352
815
|
}
|
|
1353
816
|
|
|
817
|
+
const mapStreamMutation = <M extends RequestsAny>(
|
|
818
|
+
client: ClientFrom<M>,
|
|
819
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
820
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
821
|
+
) => {
|
|
822
|
+
const Command = useCommand()
|
|
823
|
+
const streamMutation = useStreamMutation()
|
|
824
|
+
const invalidation = queryInvalidation?.(client)
|
|
825
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
826
|
+
const streams = Struct.keys(client).reduce(
|
|
827
|
+
(acc, key) => {
|
|
828
|
+
if (client[key].Request.type !== "stream") {
|
|
829
|
+
return acc
|
|
830
|
+
}
|
|
831
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
832
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
833
|
+
| undefined
|
|
834
|
+
const fromRequest = fromRequestConfig
|
|
835
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
836
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((entry) => ({
|
|
837
|
+
filters: entry.filters,
|
|
838
|
+
options: entry.options
|
|
839
|
+
})))
|
|
840
|
+
: undefined
|
|
841
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
842
|
+
const smFactory = Object.assign(
|
|
843
|
+
(opts?: { progress?: (result: AsyncResult.AsyncResult<any, any>) => Progress | undefined }) => {
|
|
844
|
+
const tuple = streamMutation(client[key] as any, mergedInvalidation)
|
|
845
|
+
const extras: {
|
|
846
|
+
id: string
|
|
847
|
+
running?: ComputedRef<AsyncResult.AsyncResult<any, any>>
|
|
848
|
+
progress?: ComputedRef<Progress | undefined>
|
|
849
|
+
} = { id: client[key].id }
|
|
850
|
+
if (opts?.progress) {
|
|
851
|
+
const fmt = opts.progress
|
|
852
|
+
extras.running = tuple[0]
|
|
853
|
+
extras.progress = computed(() => fmt(tuple[0].value))
|
|
854
|
+
}
|
|
855
|
+
return Object.assign(tuple, extras)
|
|
856
|
+
},
|
|
857
|
+
{ id: client[key].id }
|
|
858
|
+
)
|
|
859
|
+
;(acc as any)[camelCase(key) + "Stream"] = Object.assign(smFactory, {
|
|
860
|
+
fn: Command.fn(client[key].id)
|
|
861
|
+
})
|
|
862
|
+
return acc
|
|
863
|
+
},
|
|
864
|
+
{} as {
|
|
865
|
+
[
|
|
866
|
+
Key in keyof typeof client as StreamHandler<typeof client[Key]> extends never ? never
|
|
867
|
+
: `${ToCamel<string & Key>}Stream`
|
|
868
|
+
]:
|
|
869
|
+
& StreamMutationWithExtensions<StreamHandler<typeof client[Key]>>
|
|
870
|
+
& { fn: StreamFnExtension<RT | RTHooks, StreamHandler<typeof client[Key]>> }
|
|
871
|
+
}
|
|
872
|
+
)
|
|
873
|
+
return streams
|
|
874
|
+
}
|
|
875
|
+
|
|
1354
876
|
// make available .query, .suspense and .mutate for each operation
|
|
1355
877
|
// and a .helpers with all mutations and queries
|
|
1356
|
-
const mapClient = <M extends
|
|
1357
|
-
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M
|
|
878
|
+
const mapClient = <M extends RequestsAny>(
|
|
879
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
880
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
1358
881
|
) =>
|
|
1359
882
|
(
|
|
1360
883
|
client: ClientFrom<M>
|
|
1361
884
|
) => {
|
|
1362
885
|
const Command = useCommand()
|
|
1363
886
|
const mutation = useMutation()
|
|
887
|
+
const streamMutation = useStreamMutation()
|
|
1364
888
|
const invalidation = queryInvalidation?.(client)
|
|
889
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
1365
890
|
const extended = Struct.keys(client).reduce(
|
|
1366
891
|
(acc, key) => {
|
|
892
|
+
const requestType = client[key].Request.type
|
|
1367
893
|
const fn = Command.fn(client[key].id)
|
|
1368
|
-
const mutate = extendM(
|
|
1369
|
-
mutation(
|
|
1370
|
-
client[key] as any,
|
|
1371
|
-
invalidation?.[key] ? { queryInvalidation: invalidation[key] } : undefined
|
|
1372
|
-
),
|
|
1373
|
-
(mutate) =>
|
|
1374
|
-
Object.assign(
|
|
1375
|
-
mutate,
|
|
1376
|
-
fn, // to get the i18n key etc.
|
|
1377
|
-
{
|
|
1378
|
-
wrap: Command.wrap({ mutate: Effect.isEffect(mutate) ? () => mutate : mutate, id: client[key].id }),
|
|
1379
|
-
fn
|
|
1380
|
-
}
|
|
1381
|
-
)
|
|
1382
|
-
)
|
|
1383
|
-
|
|
1384
894
|
const h_ = client[key].handler
|
|
1385
|
-
const
|
|
895
|
+
const wrapInput = Effect.isEffect(h_)
|
|
1386
896
|
? () => h_
|
|
1387
897
|
: (...args: [any]) => h_(...args)
|
|
898
|
+
const request = Effect.isEffect(h_) ? h_ : wrapInput
|
|
1388
899
|
;(acc as any)[key] = Object.assign(
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
900
|
+
requestType === "query"
|
|
901
|
+
? {
|
|
902
|
+
...client[key],
|
|
903
|
+
request,
|
|
904
|
+
query: useQuery(client[key] as any),
|
|
905
|
+
suspense: useSuspenseQuery(client[key] as any),
|
|
906
|
+
project: (projectionSchema: any) => {
|
|
907
|
+
const successSchema = client[key].Request.success
|
|
908
|
+
const projectionHash = projectionSchemaHash(projectionSchema)
|
|
909
|
+
const projected = projectHandler(h_ as any, successSchema, projectionSchema)
|
|
910
|
+
const fakeHandler = {
|
|
911
|
+
handler: projected,
|
|
912
|
+
id: client[key].id,
|
|
913
|
+
Request: client[key].Request,
|
|
914
|
+
options: client[key].options,
|
|
915
|
+
queryKeyProjectionHash: projectionHash
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
request: projected,
|
|
919
|
+
query: useQuery(fakeHandler as any),
|
|
920
|
+
suspense: useSuspenseQuery(fakeHandler as any)
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
: requestType === "stream"
|
|
925
|
+
? (() => {
|
|
926
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
927
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
928
|
+
| undefined
|
|
929
|
+
const fromRequest = fromRequestConfig
|
|
930
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
931
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((
|
|
932
|
+
entry
|
|
933
|
+
) => ({
|
|
934
|
+
filters: entry.filters,
|
|
935
|
+
options: entry.options
|
|
936
|
+
})))
|
|
937
|
+
: undefined
|
|
938
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
939
|
+
const streamMutFactory = Object.assign(
|
|
940
|
+
(opts?: { progress?: (result: AsyncResult.AsyncResult<any, any>) => Progress | undefined }) => {
|
|
941
|
+
const tuple = streamMutation(client[key] as any, mergedInvalidation)
|
|
942
|
+
const extras: {
|
|
943
|
+
id: string
|
|
944
|
+
running?: ComputedRef<AsyncResult.AsyncResult<any, any>>
|
|
945
|
+
progress?: ComputedRef<Progress | undefined>
|
|
946
|
+
} = { id: client[key].id }
|
|
947
|
+
if (opts?.progress) {
|
|
948
|
+
const fmt = opts.progress
|
|
949
|
+
extras.running = tuple[0]
|
|
950
|
+
extras.progress = computed(() => fmt(tuple[0].value))
|
|
951
|
+
}
|
|
952
|
+
return Object.assign(tuple, extras)
|
|
953
|
+
},
|
|
954
|
+
{ id: client[key].id }
|
|
955
|
+
)
|
|
956
|
+
return {
|
|
957
|
+
...client[key],
|
|
958
|
+
request: h_,
|
|
959
|
+
mutateStream: streamMutFactory,
|
|
960
|
+
wrapStream: Command.wrapStream(streamMutFactory),
|
|
961
|
+
fn: Command.fn(client[key].id)
|
|
962
|
+
}
|
|
963
|
+
})()
|
|
964
|
+
: {
|
|
965
|
+
mutate: ((handler: any) => {
|
|
966
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
967
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
968
|
+
| undefined
|
|
969
|
+
const fromRequest = fromRequestConfig
|
|
970
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
971
|
+
fromRequestConfig(defaultKey, queryResources as never, input as never, output as never).map((
|
|
972
|
+
entry
|
|
973
|
+
) => ({
|
|
974
|
+
filters: entry.filters,
|
|
975
|
+
options: entry.options
|
|
976
|
+
})))
|
|
977
|
+
: undefined
|
|
978
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
979
|
+
const makeProjectedMutation = (h: any): any => {
|
|
980
|
+
const isWithInput = !Effect.isEffect(h.handler)
|
|
981
|
+
const mutate = withDefaultInvalidation(mutation(h), isWithInput, mergedInvalidation)
|
|
982
|
+
return Object.assign(
|
|
983
|
+
mutate,
|
|
984
|
+
{
|
|
985
|
+
wrap: Command.wrap({
|
|
986
|
+
mutate,
|
|
987
|
+
id: client[key].id
|
|
988
|
+
}),
|
|
989
|
+
project: (projectionSchema: any) => {
|
|
990
|
+
const projected = {
|
|
991
|
+
...h,
|
|
992
|
+
handler: projectHandler(h.handler, client[key].Request.success, projectionSchema)
|
|
993
|
+
}
|
|
994
|
+
return makeProjectedMutation(projected)
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
)
|
|
998
|
+
}
|
|
999
|
+
return makeProjectedMutation(handler)
|
|
1000
|
+
})(client[key] as any),
|
|
1001
|
+
...client[key],
|
|
1002
|
+
...fn, // to get the i18n key etc.
|
|
1003
|
+
request,
|
|
1004
|
+
fn,
|
|
1005
|
+
wrap: Command.wrap({ mutate: wrapInput, id: client[key].id })
|
|
1006
|
+
}
|
|
1399
1007
|
)
|
|
1400
1008
|
return acc
|
|
1401
1009
|
},
|
|
1402
1010
|
{} as {
|
|
1403
1011
|
[Key in keyof typeof client]:
|
|
1404
1012
|
& typeof client[Key]
|
|
1405
|
-
&
|
|
1406
|
-
|
|
1407
|
-
|
|
1013
|
+
& (QueryHandler<typeof client[Key]> extends never ? {}
|
|
1014
|
+
:
|
|
1015
|
+
& QueryRequestWithExtensions<QueryHandler<typeof client[Key]>>
|
|
1016
|
+
& Queries<RT, QueryHandler<typeof client[Key]>>
|
|
1017
|
+
& QueryProjection<RT, QueryHandler<typeof client[Key]>>)
|
|
1018
|
+
& (CommandHandler<typeof client[Key]> extends never ? {}
|
|
1019
|
+
: CommandRequestWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>>)
|
|
1020
|
+
& (CommandHandler<typeof client[Key]> extends never ? {}
|
|
1021
|
+
: { mutate: MutationWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>> })
|
|
1022
|
+
& (StreamHandler<typeof client[Key]> extends never ? {}
|
|
1023
|
+
: {
|
|
1024
|
+
mutateStream: StreamMutationWithExtensions<StreamHandler<typeof client[Key]>>
|
|
1025
|
+
wrapStream: StreamCommandWithExtensions<RT | RTHooks, StreamHandler<typeof client[Key]>>
|
|
1026
|
+
fn: StreamFnExtension<RT | RTHooks, StreamHandler<typeof client[Key]>>
|
|
1027
|
+
})
|
|
1028
|
+
& { Input: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I : never }
|
|
1408
1029
|
}
|
|
1409
1030
|
)
|
|
1410
|
-
return Object.assign(extended, {
|
|
1031
|
+
return Object.assign(extended, {
|
|
1032
|
+
helpers: {
|
|
1033
|
+
...mapRequest(client),
|
|
1034
|
+
...mapMutation(client, queryInvalidation, invalidationResources),
|
|
1035
|
+
...mapStreamMutation(client, queryInvalidation, invalidationResources),
|
|
1036
|
+
...mapQuery(client)
|
|
1037
|
+
}
|
|
1038
|
+
})
|
|
1411
1039
|
}
|
|
1412
1040
|
|
|
1413
1041
|
// TODO: Clean up this delay initialisation messs
|
|
1414
1042
|
// TODO; invalidateQueries should perhaps be configured in the Request impl themselves?
|
|
1415
|
-
const clientFor__ = <M extends
|
|
1043
|
+
const clientFor__ = <M extends RequestsAny>(
|
|
1416
1044
|
m: M,
|
|
1417
|
-
queryInvalidation?:
|
|
1418
|
-
|
|
1045
|
+
queryInvalidation?: QueryInvalidationFactory<M>,
|
|
1046
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
1047
|
+
) => getBaseMrt().runSync(clientFor_(m).pipe(Effect.map(mapClient(queryInvalidation, invalidationResources))))
|
|
1419
1048
|
|
|
1420
1049
|
// delay client creation until first access
|
|
1421
1050
|
// the idea is that we don't need the useNuxtApp().$runtime (only available at later initialisation stage)
|
|
1422
1051
|
// until we are at a place where it is available..
|
|
1423
|
-
const clientFor = <
|
|
1052
|
+
const clientFor = <
|
|
1053
|
+
M extends RequestsAny,
|
|
1054
|
+
Resources extends InvalidationResourcesFor<M> = InvalidationResourcesFor<M>
|
|
1055
|
+
>(
|
|
1424
1056
|
m: M,
|
|
1425
|
-
|
|
1057
|
+
...args: ClientForArgs<M, Resources>
|
|
1426
1058
|
) => {
|
|
1059
|
+
const [queryInvalidation, invalidationResources] = args as [
|
|
1060
|
+
QueryInvalidationFactory<M> | undefined,
|
|
1061
|
+
InvalidationResourcesFor<M> | undefined
|
|
1062
|
+
]
|
|
1427
1063
|
type Client = ReturnType<typeof clientFor__<M>>
|
|
1428
1064
|
let client: Client | undefined = undefined
|
|
1429
|
-
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation))
|
|
1065
|
+
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation, invalidationResources))
|
|
1430
1066
|
|
|
1431
1067
|
// initialize on first use..
|
|
1432
1068
|
const proxy = Struct.keys(m).concat(["helpers"]).reduce((acc, key) => {
|
|
@@ -1444,16 +1080,12 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
1444
1080
|
return proxy
|
|
1445
1081
|
}
|
|
1446
1082
|
|
|
1447
|
-
const legacy: Legacy<RT> = {
|
|
1448
|
-
...mutations,
|
|
1449
|
-
...query
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
1083
|
const Command: CommanderResolved<RT, RTHooks> = {
|
|
1453
1084
|
...{
|
|
1454
1085
|
// delay initialisation until first use...
|
|
1455
1086
|
fn: (...args: [any]) => useCommand().fn(...args),
|
|
1456
1087
|
wrap: (...args: [any]) => useCommand().wrap(...args),
|
|
1088
|
+
wrapStream: (...args: [any]) => useCommand().wrapStream(...args),
|
|
1457
1089
|
alt: (...args: [any]) => useCommand().alt(...args),
|
|
1458
1090
|
alt2: (...args: [any]) => useCommand().alt2(...args)
|
|
1459
1091
|
} as ReturnType<typeof useCommand>,
|
|
@@ -1463,19 +1095,17 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
1463
1095
|
return {
|
|
1464
1096
|
Command,
|
|
1465
1097
|
useCommand,
|
|
1466
|
-
clientFor
|
|
1467
|
-
legacy
|
|
1098
|
+
clientFor
|
|
1468
1099
|
}
|
|
1469
1100
|
}
|
|
1470
1101
|
|
|
1471
|
-
export interface Legacy<R>
|
|
1472
|
-
extends
|
|
1473
|
-
Pick<QueryImpl<R>, "useQuery" | "useSuspenseQuery">,
|
|
1474
|
-
Omit<LegacyMutationImpl<R>, "getRuntime" | "toast" | "intl">
|
|
1475
|
-
{}
|
|
1476
|
-
|
|
1477
1102
|
export type QueryInvalidation<M> = {
|
|
1478
|
-
[K in keyof M]?: (
|
|
1103
|
+
[K in keyof M]?: (
|
|
1104
|
+
defaultKey: string[],
|
|
1105
|
+
name: string,
|
|
1106
|
+
input?: unknown,
|
|
1107
|
+
output?: ExitResult.Exit<unknown, unknown>
|
|
1108
|
+
) => {
|
|
1479
1109
|
filters?: InvalidateQueryFilters | undefined
|
|
1480
1110
|
options?: InvalidateOptions | undefined
|
|
1481
1111
|
}[]
|
|
@@ -1493,10 +1123,12 @@ export interface CommandBase<I = void, A = void> {
|
|
|
1493
1123
|
allowed: boolean
|
|
1494
1124
|
action: string
|
|
1495
1125
|
label: string
|
|
1126
|
+
/** formatted progress info for current `running` state, when `progress` was supplied */
|
|
1127
|
+
progress?: Progress | undefined
|
|
1496
1128
|
}
|
|
1497
1129
|
|
|
1498
1130
|
export interface EffectCommand<I = void, A = unknown, E = unknown> extends CommandBase<I, Fiber<A, E>> {}
|
|
1499
1131
|
|
|
1500
|
-
export interface CommandFromRequest<I extends
|
|
1501
|
-
extends EffectCommand<
|
|
1132
|
+
export interface CommandFromRequest<I extends { readonly make: (...args: any[]) => any }, A = unknown, E = unknown>
|
|
1133
|
+
extends EffectCommand<RequestInputFromMake<I>, A, E>
|
|
1502
1134
|
{}
|