@effect-app/vue 4.0.0-beta.16 → 4.0.0-beta.161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +1023 -0
  2. package/dist/commander.d.ts +370 -0
  3. package/dist/commander.d.ts.map +1 -0
  4. package/dist/commander.js +591 -0
  5. package/dist/confirm.d.ts +19 -0
  6. package/dist/confirm.d.ts.map +1 -0
  7. package/dist/confirm.js +24 -0
  8. package/dist/errorReporter.d.ts +4 -4
  9. package/dist/errorReporter.d.ts.map +1 -1
  10. package/dist/errorReporter.js +12 -18
  11. package/dist/form.d.ts +13 -4
  12. package/dist/form.d.ts.map +1 -1
  13. package/dist/form.js +41 -12
  14. package/dist/index.d.ts +1 -1
  15. package/dist/intl.d.ts +15 -0
  16. package/dist/intl.d.ts.map +1 -0
  17. package/dist/intl.js +9 -0
  18. package/dist/lib.d.ts +2 -1
  19. package/dist/lib.d.ts.map +1 -1
  20. package/dist/lib.js +32 -1
  21. package/dist/makeClient.d.ts +97 -276
  22. package/dist/makeClient.d.ts.map +1 -1
  23. package/dist/makeClient.js +94 -353
  24. package/dist/makeContext.d.ts +1 -1
  25. package/dist/makeContext.d.ts.map +1 -1
  26. package/dist/makeIntl.d.ts +1 -1
  27. package/dist/makeIntl.d.ts.map +1 -1
  28. package/dist/makeUseCommand.d.ts +8 -0
  29. package/dist/makeUseCommand.d.ts.map +1 -0
  30. package/dist/makeUseCommand.js +13 -0
  31. package/dist/mutate.d.ts +2 -2
  32. package/dist/mutate.d.ts.map +1 -1
  33. package/dist/mutate.js +1 -1
  34. package/dist/query.d.ts +11 -15
  35. package/dist/query.d.ts.map +1 -1
  36. package/dist/query.js +17 -26
  37. package/dist/routeParams.d.ts +1 -1
  38. package/dist/runtime.d.ts +5 -2
  39. package/dist/runtime.d.ts.map +1 -1
  40. package/dist/runtime.js +27 -17
  41. package/dist/toast.d.ts +46 -0
  42. package/dist/toast.d.ts.map +1 -0
  43. package/dist/toast.js +32 -0
  44. package/dist/withToast.d.ts +26 -0
  45. package/dist/withToast.d.ts.map +1 -0
  46. package/dist/withToast.js +49 -0
  47. package/eslint.config.mjs +2 -2
  48. package/package.json +48 -48
  49. package/src/{experimental/commander.ts → commander.ts} +930 -255
  50. package/src/{experimental/confirm.ts → confirm.ts} +10 -14
  51. package/src/errorReporter.ts +60 -72
  52. package/src/form.ts +55 -16
  53. package/src/intl.ts +12 -0
  54. package/src/lib.ts +43 -0
  55. package/src/makeClient.ts +305 -1015
  56. package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +3 -3
  57. package/src/query.ts +37 -48
  58. package/src/runtime.ts +39 -18
  59. package/src/{experimental/toast.ts → toast.ts} +11 -25
  60. package/src/{experimental/withToast.ts → withToast.ts} +15 -6
  61. package/test/Mutation.test.ts +130 -10
  62. package/test/dist/form.test.d.ts.map +1 -1
  63. package/test/dist/lib.test.d.ts.map +1 -0
  64. package/test/dist/stubs.d.ts +1103 -118
  65. package/test/dist/stubs.d.ts.map +1 -1
  66. package/test/dist/stubs.js +56 -23
  67. package/test/form-validation-errors.test.ts +23 -19
  68. package/test/form.test.ts +20 -2
  69. package/test/lib.test.ts +240 -0
  70. package/test/makeClient.test.ts +93 -39
  71. package/test/stubs.ts +83 -41
  72. package/tsconfig.json +0 -1
  73. package/tsconfig.json.bak +2 -2
  74. package/tsconfig.src.json +34 -34
  75. package/tsconfig.test.json +2 -2
  76. package/vitest.config.ts +5 -5
  77. package/dist/experimental/commander.d.ts +0 -359
  78. package/dist/experimental/commander.d.ts.map +0 -1
  79. package/dist/experimental/commander.js +0 -557
  80. package/dist/experimental/confirm.d.ts +0 -19
  81. package/dist/experimental/confirm.d.ts.map +0 -1
  82. package/dist/experimental/confirm.js +0 -28
  83. package/dist/experimental/intl.d.ts +0 -16
  84. package/dist/experimental/intl.d.ts.map +0 -1
  85. package/dist/experimental/intl.js +0 -5
  86. package/dist/experimental/makeUseCommand.d.ts +0 -8
  87. package/dist/experimental/makeUseCommand.d.ts.map +0 -1
  88. package/dist/experimental/makeUseCommand.js +0 -13
  89. package/dist/experimental/toast.d.ts +0 -47
  90. package/dist/experimental/toast.d.ts.map +0 -1
  91. package/dist/experimental/toast.js +0 -41
  92. package/dist/experimental/withToast.d.ts +0 -25
  93. package/dist/experimental/withToast.d.ts.map +0 -1
  94. package/dist/experimental/withToast.js +0 -45
  95. package/src/experimental/intl.ts +0 -9
package/src/makeClient.ts CHANGED
@@ -1,32 +1,43 @@
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 { Cause, Data, Effect, Exit, Layer, type ManagedRuntime, Match, Option, S, ServiceMap, Struct } from "effect-app"
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, Requests } from "effect-app/client/clientFor"
7
- import { ErrorSilenced, type SupportedErrors } from "effect-app/client/errors"
8
- import { constant, identity, pipe, tuple } from "effect-app/Function"
9
- import { type OperationFailure, OperationSuccess } from "effect-app/Operations"
10
- import { dropUndefinedT, extendM } from "effect-app/utils"
6
+ import type { ExtractModuleName, RequestHandler, RequestHandlers, RequestHandlerWithInput, RequestsAny } from "effect-app/client/clientFor"
11
7
  import { type Fiber } from "effect/Fiber"
12
8
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
13
- import { computed, type ComputedRef, onBeforeUnmount, type Ref, ref, watch, type WatchSource } from "vue"
14
- import { reportMessage } from "./errorReporter.js"
15
- import { type Commander, CommanderStatic } from "./experimental/commander.js"
16
- import { I18n } from "./experimental/intl.js"
17
- import { type CommanderResolved, makeUseCommand } from "./experimental/makeUseCommand.js"
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"
9
+ import { type ComputedRef, onBeforeUnmount, ref, type WatchSource } from "vue"
10
+ import { type Commander, CommanderStatic } from "./commander.js"
11
+ import { type I18n } from "./intl.js"
12
+ import { type CommanderResolved, makeUseCommand } from "./makeUseCommand.js"
13
+ import { makeMutation, type MutationOptionsBase, useMakeMutation } from "./mutate.js"
22
14
  import { type CustomUndefinedInitialQueryOptions, makeQuery } from "./query.js"
15
+ import { makeRunPromise } from "./runtime.js"
16
+ import { type Toast } from "./toast.js"
23
17
 
24
18
  const mapHandler = <A, E, R, I = void, A2 = A, E2 = E, R2 = R>(
25
19
  handler: Effect.Effect<A, E, R> | ((i: I) => Effect.Effect<A, E, R>),
26
20
  map: (self: Effect.Effect<A, E, R>, i: I) => Effect.Effect<A2, E2, R2>
27
21
  ) => Effect.isEffect(handler) ? map(handler, undefined as any) : (i: I) => map(handler(i), i)
28
22
 
29
- export interface RequestExtensions<RT, Id extends string, I, A, E, R> {
23
+ // TODO: optimize - work from encoded shape directly
24
+ const projectHandler = (
25
+ handler: Effect.Effect<any, any, any> | ((i: any) => Effect.Effect<any, any, any>),
26
+ successSchema: S.Top,
27
+ projectionSchema: S.Top
28
+ ) => {
29
+ const encode = S.encodeEffect(successSchema)
30
+ const decode = S.decodeEffectConcurrently(projectionSchema)
31
+ return mapHandler(handler, (self) =>
32
+ self.pipe(
33
+ Effect.flatMap(encode),
34
+ Effect.flatMap(decode)
35
+ ))
36
+ }
37
+
38
+ const projectionSchemaHash = (schema: S.Top) => String(Hash.hash(schema.ast))
39
+
40
+ export interface CommandRequestExtensions<RT, Id extends string, I, A, E, R> {
30
41
  /** Defines a Command based on this call, taking the `id` of the call as the `id` of the Command.
31
42
  * The Request function will be taken as the first member of the Command, the Command required input will be the Request input.
32
43
  * see Command.wrap for details */
@@ -44,16 +55,14 @@ export interface RequestExtWithInput<
44
55
  A,
45
56
  E,
46
57
  R
47
- > extends Commander.CommandContextLocal<Id, Id>, RequestExtensions<RT, Id, I, A, E, R> {
58
+ > extends Commander.CommandContextLocal<Id, Id>, CommandRequestExtensions<RT, Id, I, A, E, R> {
48
59
  /**
49
- * Request the endpoint with input
60
+ * Send the request to the endpoint and return the raw Effect response.
61
+ * This does not perform query cache invalidation.
50
62
  */
51
- (i: I): Effect.Effect<A, E, R>
63
+ request: (i: I) => Effect.Effect<A, E, R>
52
64
  }
53
65
 
54
- /**
55
- * Request the endpoint
56
- */
57
66
  export interface RequestExt<
58
67
  RT,
59
68
  Id extends string,
@@ -63,25 +72,62 @@ export interface RequestExt<
63
72
  > extends
64
73
  Commander.CommandContextLocal<Id, Id>,
65
74
  Commander.CommanderWrap<RT, Id, Id, undefined, void, A, E, R>,
66
- RequestExtensions<RT, Id, void, A, E, R>,
67
- Effect.Effect<A, E, R>
75
+ CommandRequestExtensions<RT, Id, void, A, E, R>
68
76
  {
77
+ /**
78
+ * Send the request to the endpoint and return the raw Effect response.
79
+ * This does not perform query cache invalidation.
80
+ */
81
+ request: Effect.Effect<A, E, R>
69
82
  }
70
83
 
71
- export type RequestWithExtensions<RT, Req> = Req extends
84
+ export type CommandRequestWithExtensions<RT, Req> = Req extends
72
85
  RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id>
73
86
  ? RequestExtWithInput<RT, Id, I, A, E, R>
74
87
  : Req extends RequestHandler<infer A, infer E, infer R, infer _Request, infer Id> ? RequestExt<RT, Id, A, E, R>
75
88
  : never
76
89
 
90
+ export interface QueryExtensionsWithInput<I, A, E, R> {
91
+ /**
92
+ * Send the request to the endpoint and return the raw Effect response.
93
+ * This does not set up query state tracking.
94
+ */
95
+ request: (i: I) => Effect.Effect<A, E, R>
96
+ }
97
+
98
+ export interface QueryExtensions<A, E, R> {
99
+ /**
100
+ * Send the request to the endpoint and return the raw Effect response.
101
+ * This does not set up query state tracking.
102
+ */
103
+ request: Effect.Effect<A, E, R>
104
+ }
105
+
106
+ export type QueryRequestWithExtensions<Req> = Req extends
107
+ RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer _Id>
108
+ ? QueryExtensionsWithInput<I, A, E, R>
109
+ : Req extends RequestHandler<infer A, infer E, infer R, infer _Request, infer _Id> ? QueryExtensions<A, E, R>
110
+ : never
111
+
112
+ type QueryHandler<Req> = Req extends
113
+ RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
114
+ ? Request["type"] extends "query" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
115
+ : Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
116
+ ? Request["type"] extends "query" ? RequestHandler<A, E, R, Request, Id> : never
117
+ : never
118
+
119
+ type CommandHandler<Req> = Req extends
120
+ RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
121
+ ? Request["type"] extends "command" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
122
+ : Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
123
+ ? Request["type"] extends "command" ? RequestHandler<A, E, R, Request, Id> : never
124
+ : never
125
+
77
126
  export interface MutationExtensions<RT, Id extends string, I, A, E, R> {
78
127
  /** Defines a Command based on this mutation, taking the `id` of the mutation as the `id` of the Command.
79
128
  * The Mutation function will be taken as the first member of the Command, the Command required input will be the Mutation input.
80
129
  * see Command.wrap for details */
81
130
  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
131
  }
86
132
 
87
133
  /** my other doc */
@@ -91,39 +137,63 @@ export interface MutationExtWithInput<
91
137
  I,
92
138
  A,
93
139
  E,
94
- R
95
- > extends Commander.CommandContextLocal<Id, Id>, MutationExtensions<RT, Id, I, A, E, R> {
140
+ R,
141
+ EA = unknown
142
+ > extends MutationExtensions<RT, Id, I, A, E, R> {
96
143
  /**
97
- * Call the endpoint with input
98
- * Invalidate queries based on namespace of this mutation.
99
- * Do not use for queries.
144
+ * Send the request to the endpoint and return the raw Effect response.
145
+ * Also invalidates query caches using the request namespace by default.
146
+ * Namespace invalidation targets parent namespace keys
147
+ * (for example `$project/$configuration.get` invalidates `$project`).
148
+ * Override invalidation in client options via `queryInvalidation`.
100
149
  */
101
150
  (i: I): Effect.Effect<A, E, R>
151
+
152
+ project: <ProjSchema extends S.Top>(
153
+ schema: EA extends ProjSchema["Encoded"] ? ProjSchema : never
154
+ ) => MutationExtWithInput<
155
+ RT,
156
+ Id,
157
+ I,
158
+ S.Schema.Type<ProjSchema>,
159
+ E | S.SchemaError,
160
+ R | S.Codec.DecodingServices<ProjSchema>,
161
+ S.Codec.Encoded<ProjSchema>
162
+ >
102
163
  }
103
164
 
104
165
  /**
105
- * Call the endpoint
106
- * Invalidate queries based on namespace of this mutation.
107
- * Do not use for queries.
166
+ * Send the request to the endpoint and return the raw Effect response.
167
+ * Also invalidates query caches using the request namespace by default.
168
+ * Namespace invalidation targets parent namespace keys
169
+ * (for example `$project/$configuration.get` invalidates `$project`).
170
+ * Override invalidation in client options via `queryInvalidation`.
108
171
  */
109
172
  export interface MutationExt<
110
173
  RT,
111
174
  Id extends string,
112
175
  A,
113
176
  E,
114
- R
115
- > extends
116
- Commander.CommandContextLocal<Id, Id>,
117
- Commander.CommanderWrap<RT, Id, Id, undefined, void, A, E, R>,
118
- MutationExtensions<RT, Id, void, A, E, R>,
119
- Effect.Effect<A, E, R>
120
- {
177
+ R,
178
+ EA = unknown
179
+ > extends MutationExtensions<RT, Id, void, A, E, R>, Effect.Effect<A, E, R> {
180
+ project: <ProjSchema extends S.Top>(
181
+ schema: EA extends ProjSchema["Encoded"] ? ProjSchema : never
182
+ ) => MutationExt<
183
+ RT,
184
+ Id,
185
+ S.Schema.Type<ProjSchema>,
186
+ E | S.SchemaError,
187
+ R | S.Codec.DecodingServices<ProjSchema>,
188
+ S.Codec.Encoded<ProjSchema>
189
+ >
121
190
  }
122
191
 
123
192
  export type MutationWithExtensions<RT, Req> = Req extends
124
- RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id>
125
- ? MutationExtWithInput<RT, Id, I, A, E, R>
126
- : Req extends RequestHandler<infer A, infer E, infer R, infer _Request, infer Id> ? MutationExt<RT, Id, A, E, R>
193
+ RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
194
+ ? MutationExtWithInput<RT, Id, I, A, E, R, S.Codec.Encoded<Request["success"]>>
195
+ : Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
196
+ ? MutationExt<RT, Id, A, E, R, S.Codec.Encoded<Request["success"]>>
127
197
  : never
128
198
 
129
199
  // we don't really care about the RT, as we are in charge of ensuring runtime safety anyway
@@ -132,29 +202,72 @@ declare const useQuery_: QueryImpl<any>["useQuery"]
132
202
  // eslint-disable-next-line unused-imports/no-unused-vars
133
203
  declare const useSuspenseQuery_: QueryImpl<any>["useSuspenseQuery"]
134
204
 
205
+ export interface ProjectResult<RT, I, B, E, R, Request extends Req, Id extends string> {
206
+ request: (i: I) => Effect.Effect<B, E, R>
207
+ query: Exclude<R, RT> extends never ? ReturnType<typeof useQuery_<I, E, B, Request, Id>>
208
+ : MissingDependencies<RT, R> & {}
209
+ suspense: Exclude<R, RT> extends never ? ReturnType<typeof useSuspenseQuery_<I, E, B, Request, Id>>
210
+ : MissingDependencies<RT, R> & {}
211
+ }
212
+
213
+ export type QueryProjection<RT, HandlerReq> = HandlerReq extends
214
+ RequestHandlerWithInput<infer I, infer _A, infer E, infer R, infer Request, infer Id>
215
+ ? Request["type"] extends "query" ? {
216
+ project: <ProjSchema extends S.Top>(
217
+ schema: S.Codec.Encoded<Request["success"]> extends ProjSchema["Encoded"] ? ProjSchema : never
218
+ ) => ProjectResult<
219
+ RT,
220
+ I,
221
+ S.Schema.Type<ProjSchema>,
222
+ E | S.SchemaError,
223
+ R | S.Codec.DecodingServices<ProjSchema>,
224
+ Request,
225
+ Id
226
+ >
227
+ }
228
+ : {}
229
+ : HandlerReq extends RequestHandler<infer _A, infer E, infer R, infer Request, infer Id>
230
+ ? Request["type"] extends "query" ? {
231
+ project: <ProjSchema extends S.Top>(
232
+ schema: S.Codec.Encoded<Request["success"]> extends ProjSchema["Encoded"] ? ProjSchema : never
233
+ ) => ProjectResult<
234
+ RT,
235
+ void,
236
+ S.Schema.Type<ProjSchema>,
237
+ E | S.SchemaError,
238
+ R | S.Codec.DecodingServices<ProjSchema>,
239
+ Request,
240
+ Id
241
+ >
242
+ }
243
+ : {}
244
+ : {}
245
+
135
246
  export interface QueriesWithInput<Request extends Req, Id extends string, I, A, E> {
136
247
  /**
137
- * Effect results are passed to the caller, including errors.
248
+ * Read helper for query requests.
249
+ * Runs as a tracked Vue Query and returns reactive state.
250
+ * Queries read state and should not be used to mutate it.
138
251
  */
139
252
  query: ReturnType<typeof useQuery_<I, E, A, Request, Id>>
140
253
  // TODO or suspense as Option?
141
254
  /**
142
- * The difference with useQuery is that this function will return a Promise you can await in the Setup,
143
- * which ensures that either there always is a latest value, or an error occurs on load.
144
- * So that Suspense and error boundaries can be used.
255
+ * Like `.query`, but returns a Promise for setup-time awaiting.
256
+ * Use this when integrating with Vue Suspense / error boundaries.
145
257
  */
146
258
  suspense: ReturnType<typeof useSuspenseQuery_<I, E, A, Request, Id>>
147
259
  }
148
260
  export interface QueriesWithoutInput<Request extends Req, Id extends string, A, E> {
149
261
  /**
150
- * Effect results are passed to the caller, including errors.
262
+ * Read helper for query requests.
263
+ * Runs as a tracked Vue Query and returns reactive state.
264
+ * Queries read state and should not be used to mutate it.
151
265
  */
152
266
  query: ReturnType<typeof useQuery_<E, A, Request, Id>>
153
267
  // TODO or suspense as Option?
154
268
  /**
155
- * The difference with useQuery is that this function will return a Promise you can await in the Setup,
156
- * which ensures that either there always is a latest value, or an error occurs on load.
157
- * So that Suspense and error boundaries can be used.
269
+ * Like `.query`, but returns a Promise for setup-time awaiting.
270
+ * Use this when integrating with Vue Suspense / error boundaries.
158
271
  */
159
272
  suspense: ReturnType<typeof useSuspenseQuery_<E, A, Request, Id>>
160
273
  }
@@ -166,176 +279,18 @@ export type MissingDependencies<RT, R> = {
166
279
 
167
280
  export type Queries<RT, Req> = Req extends
168
281
  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
- query: MissingDependencies<RT, R> & {}
172
- suspense: MissingDependencies<RT, R> & {}
173
- }
282
+ ? Request["type"] extends "query" ? Exclude<R, RT> extends never ? QueriesWithInput<Request, Id, I, A, E>
283
+ : {
284
+ query: MissingDependencies<RT, R> & {}
285
+ suspense: MissingDependencies<RT, R> & {}
286
+ }
287
+ : never
174
288
  : 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
- : { query: MissingDependencies<RT, R> & {}; suspense: MissingDependencies<RT, R> & {} }
289
+ ? Request["type"] extends "query" ? Exclude<R, RT> extends never ? QueriesWithoutInput<Request, Id, A, E>
290
+ : { query: MissingDependencies<RT, R> & {}; suspense: MissingDependencies<RT, R> & {} }
291
+ : never
177
292
  : never
178
293
 
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)
317
-
318
- yield* options.onDefect(cause, i)
319
- })
320
- })
321
- ),
322
- Effect.withSpan(`mutation ${id}`, {}, { captureStackTrace: false })
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
- )
337
- }
338
-
339
294
  const _useMutation = makeMutation()
340
295
 
341
296
  /**
@@ -389,702 +344,10 @@ export const useMutationInt = (): typeof _useMutation => {
389
344
  )
390
345
  }
391
346
 
392
- export class LegacyMutationImpl<RT> {
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"]>
347
+ export type ClientFrom<M extends RequestsAny> = RequestHandlers<never, never, M, ExtractModuleName<M>>
1085
348
 
1086
349
  export class QueryImpl<R> {
1087
- constructor(readonly getRuntime: () => ServiceMap.ServiceMap<R>) {
350
+ constructor(readonly getRuntime: () => Context.Context<R>) {
1088
351
  this.useQuery = makeQuery(this.getRuntime)
1089
352
  }
1090
353
  /**
@@ -1163,7 +426,7 @@ export class QueryImpl<R> {
1163
426
  } = <Arg, E, A, Request extends Req, Name extends string>(
1164
427
  self: RequestHandlerWithInput<Arg, A, E, R, Request, Name> | RequestHandler<A, E, R, Request, Name>
1165
428
  ) => {
1166
- const runPromise = Effect.runPromiseWith(this.getRuntime())
429
+ const runPromise = makeRunPromise(this.getRuntime())
1167
430
  const q = this.useQuery(self as any) as any
1168
431
  return (argOrOptions?: any, options?: any) => {
1169
432
  const [resultRef, latestRef, fetch, uqrt] = q(argOrOptions, { ...options, suspense: true } // experimental_prefetchInRender: true }
@@ -1214,10 +477,10 @@ export class QueryImpl<R> {
1214
477
  }
1215
478
 
1216
479
  // 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.services<A>())
480
+ const managedRuntimeRt = <A, E>(mrt: ManagedRuntime.ManagedRuntime<A, E>) => mrt.runSync(Effect.context<A>())
1218
481
 
1219
482
  type Base = I18n | Toast
1220
- type Mix = ApiClientFactory | Commander | LegacyMutation | Base
483
+ type Mix = ApiClientFactory | Commander | Base
1221
484
  export const makeClient = <RT_, RTHooks>(
1222
485
  // global, but only accessible after startup has completed
1223
486
  getBaseMrt: () => ManagedRuntime.ManagedRuntime<RT_ | Mix, never>,
@@ -1225,54 +488,26 @@ export const makeClient = <RT_, RTHooks>(
1225
488
  rtHooks: Layer.Layer<RTHooks, never, Mix>
1226
489
  ) => {
1227
490
  type RT = RT_ | Mix
1228
- const getRt = Effect.services<RT>()
1229
491
  const getBaseRt = () => managedRuntimeRt(getBaseMrt())
1230
492
  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
493
  let cmd: Effect.Success<typeof makeCommand>
1237
494
  const useCommand = () => cmd ??= getBaseMrt().runSync(makeCommand)
1238
- let mut: Effect.Success<typeof makeMutation>
1239
- const getMutation = () => mut ??= getBaseMrt().runSync(makeMutation)
1240
495
 
1241
496
  let m: ReturnType<typeof useMutationInt>
1242
497
  const useMutation = () => m ??= useMutationInt()
1243
498
 
1244
- const keys = [
1245
- "useSafeMutationWithState",
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
- )
1266
-
1267
499
  const query = new QueryImpl(getBaseRt)
1268
500
  const useQuery = query.useQuery
1269
501
  const useSuspenseQuery = query.useSuspenseQuery
1270
502
 
1271
- const mapQuery = <M extends Requests>(
503
+ const mapQuery = <M extends RequestsAny>(
1272
504
  client: ClientFrom<M>
1273
505
  ) => {
1274
506
  const queries = Struct.keys(client).reduce(
1275
507
  (acc, key) => {
508
+ if (client[key].Request.type !== "query") {
509
+ return acc
510
+ }
1276
511
  ;(acc as any)[camelCase(key) + "Query"] = Object.assign(useQuery(client[key] as any), {
1277
512
  id: client[key].id
1278
513
  })
@@ -1284,26 +519,35 @@ export const makeClient = <RT_, RTHooks>(
1284
519
  {} as
1285
520
  & {
1286
521
  // apparently can't get JSDoc in here..
1287
- [Key in keyof typeof client as `${ToCamel<string & Key>}Query`]: Queries<RT, typeof client[Key]>["query"]
522
+ [
523
+ Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
524
+ : `${ToCamel<string & Key>}Query`
525
+ ]: Queries<RT, QueryHandler<typeof client[Key]>>["query"]
1288
526
  }
1289
527
  // todo: or suspense as an Option?
1290
528
  & {
1291
529
  // apparently can't get JSDoc in here..
1292
- [Key in keyof typeof client as `${ToCamel<string & Key>}SuspenseQuery`]: Queries<
530
+ [
531
+ Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
532
+ : `${ToCamel<string & Key>}SuspenseQuery`
533
+ ]: Queries<
1293
534
  RT,
1294
- typeof client[Key]
535
+ QueryHandler<typeof client[Key]>
1295
536
  >["suspense"]
1296
537
  }
1297
538
  )
1298
539
  return queries
1299
540
  }
1300
541
 
1301
- const mapRequest = <M extends Requests>(
542
+ const mapRequest = <M extends RequestsAny>(
1302
543
  client: ClientFrom<M>
1303
544
  ) => {
1304
545
  const Command = useCommand()
1305
546
  const mutations = Struct.keys(client).reduce(
1306
547
  (acc, key) => {
548
+ if (client[key].Request.type !== "command") {
549
+ return acc
550
+ }
1307
551
  const mut = client[key].handler
1308
552
  const fn = Command.fn(client[key].id)
1309
553
  const wrap = Command.wrap({ mutate: Effect.isEffect(mut) ? () => mut : mut, id: client[key].id })
@@ -1315,36 +559,52 @@ export const makeClient = <RT_, RTHooks>(
1315
559
  return acc
1316
560
  },
1317
561
  {} as {
1318
- [Key in keyof typeof client as `${ToCamel<string & Key>}Request`]: RequestWithExtensions<
562
+ [
563
+ Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
564
+ : `${ToCamel<string & Key>}Request`
565
+ ]: CommandRequestWithExtensions<
1319
566
  RT | RTHooks,
1320
- typeof client[Key]
567
+ CommandHandler<typeof client[Key]>
1321
568
  >
1322
569
  }
1323
570
  )
1324
571
  return mutations
1325
572
  }
1326
573
 
1327
- const mapMutation = <M extends Requests>(
574
+ const mapMutation = <M extends RequestsAny>(
1328
575
  client: ClientFrom<M>
1329
576
  ) => {
1330
577
  const Command = useCommand()
1331
578
  const mutation = useMutation()
1332
579
  const mutations = Struct.keys(client).reduce(
1333
580
  (acc, key) => {
1334
- const mut: any = mutation(client[key] as any)
1335
- const fn = Command.fn(client[key].id)
1336
- const wrap = Command.wrap({ mutate: Effect.isEffect(mut) ? () => mut : mut, id: client[key].id })
1337
- ;(acc as any)[camelCase(key) + "Mutation"] = Object.assign(
1338
- mut,
1339
- fn, // to get the i18n key etc.
1340
- { wrap, fn }
1341
- )
581
+ if (client[key].Request.type !== "command") {
582
+ return acc
583
+ }
584
+ const makeProjectedMutation = (handler: any): any => {
585
+ const mut: any = mutation(handler)
586
+ const wrap = Command.wrap({ mutate: Effect.isEffect(mut) ? () => mut : mut, id: client[key].id })
587
+ return Object.assign(mut, {
588
+ wrap,
589
+ project: (projectionSchema: any) => {
590
+ const projected = {
591
+ ...handler,
592
+ handler: projectHandler(handler.handler, client[key].Request.success, projectionSchema)
593
+ }
594
+ return makeProjectedMutation(projected)
595
+ }
596
+ })
597
+ }
598
+ ;(acc as any)[camelCase(key) + "Mutation"] = makeProjectedMutation(client[key] as any)
1342
599
  return acc
1343
600
  },
1344
601
  {} as {
1345
- [Key in keyof typeof client as `${ToCamel<string & Key>}Mutation`]: MutationWithExtensions<
602
+ [
603
+ Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
604
+ : `${ToCamel<string & Key>}Mutation`
605
+ ]: MutationWithExtensions<
1346
606
  RT | RTHooks,
1347
- typeof client[Key]
607
+ CommandHandler<typeof client[Key]>
1348
608
  >
1349
609
  }
1350
610
  )
@@ -1353,7 +613,7 @@ export const makeClient = <RT_, RTHooks>(
1353
613
 
1354
614
  // make available .query, .suspense and .mutate for each operation
1355
615
  // and a .helpers with all mutations and queries
1356
- const mapClient = <M extends Requests>(
616
+ const mapClient = <M extends RequestsAny>(
1357
617
  queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>
1358
618
  ) =>
1359
619
  (
@@ -1364,47 +624,86 @@ export const makeClient = <RT_, RTHooks>(
1364
624
  const invalidation = queryInvalidation?.(client)
1365
625
  const extended = Struct.keys(client).reduce(
1366
626
  (acc, key) => {
627
+ const requestType = client[key].Request.type
1367
628
  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
629
  const h_ = client[key].handler
1385
- const h = Effect.isEffect(h_)
630
+ const wrapInput = Effect.isEffect(h_)
1386
631
  ? () => h_
1387
632
  : (...args: [any]) => h_(...args)
633
+ const request = Effect.isEffect(h_) ? h_ : wrapInput
1388
634
  ;(acc as any)[key] = Object.assign(
1389
- h,
1390
- client[key],
1391
- fn, // to get the i18n key etc.
1392
- {
1393
- mutate,
1394
- query: useQuery(client[key] as any),
1395
- suspense: useSuspenseQuery(client[key] as any),
1396
- wrap: Command.wrap({ mutate: h, id: client[key].id }),
1397
- fn
1398
- }
635
+ requestType === "query"
636
+ ? {
637
+ ...client[key],
638
+ request,
639
+ query: useQuery(client[key] as any),
640
+ suspense: useSuspenseQuery(client[key] as any),
641
+ project: (projectionSchema: any) => {
642
+ const successSchema = client[key].Request.success
643
+ const projectionHash = projectionSchemaHash(projectionSchema)
644
+ const projected = projectHandler(h_ as any, successSchema, projectionSchema)
645
+ const fakeHandler = {
646
+ handler: projected,
647
+ id: client[key].id,
648
+ Request: client[key].Request,
649
+ options: client[key].options,
650
+ queryKeyProjectionHash: projectionHash
651
+ }
652
+ return {
653
+ request: projected,
654
+ query: useQuery(fakeHandler as any),
655
+ suspense: useSuspenseQuery(fakeHandler as any)
656
+ }
657
+ }
658
+ }
659
+ : {
660
+ mutate: ((handler: any) => {
661
+ const makeProjectedMutation = (h: any): any => {
662
+ const mutate = mutation(
663
+ h,
664
+ invalidation?.[key] ? { queryInvalidation: invalidation[key] } : undefined
665
+ ) as any
666
+ return Object.assign(
667
+ mutate,
668
+ {
669
+ wrap: Command.wrap({
670
+ mutate: Effect.isEffect(mutate) ? () => mutate : mutate,
671
+ id: client[key].id
672
+ }),
673
+ project: (projectionSchema: any) => {
674
+ const projected = {
675
+ ...h,
676
+ handler: projectHandler(h.handler, client[key].Request.success, projectionSchema)
677
+ }
678
+ return makeProjectedMutation(projected)
679
+ }
680
+ }
681
+ )
682
+ }
683
+ return makeProjectedMutation(handler)
684
+ })(client[key] as any),
685
+ ...client[key],
686
+ ...fn, // to get the i18n key etc.
687
+ request,
688
+ fn,
689
+ wrap: Command.wrap({ mutate: wrapInput, id: client[key].id })
690
+ }
1399
691
  )
1400
692
  return acc
1401
693
  },
1402
694
  {} as {
1403
695
  [Key in keyof typeof client]:
1404
696
  & typeof client[Key]
1405
- & RequestWithExtensions<RT | RTHooks, typeof client[Key]>
1406
- & { mutate: MutationWithExtensions<RT | RTHooks, typeof client[Key]> }
1407
- & Queries<RT, typeof client[Key]>
697
+ & (QueryHandler<typeof client[Key]> extends never ? {}
698
+ :
699
+ & QueryRequestWithExtensions<QueryHandler<typeof client[Key]>>
700
+ & Queries<RT, QueryHandler<typeof client[Key]>>
701
+ & QueryProjection<RT, QueryHandler<typeof client[Key]>>)
702
+ & (CommandHandler<typeof client[Key]> extends never ? {}
703
+ : CommandRequestWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>>)
704
+ & (CommandHandler<typeof client[Key]> extends never ? {}
705
+ : { mutate: MutationWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>> })
706
+ & { Input: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I : never }
1408
707
  }
1409
708
  )
1410
709
  return Object.assign(extended, { helpers: { ...mapRequest(client), ...mapMutation(client), ...mapQuery(client) } })
@@ -1412,7 +711,7 @@ export const makeClient = <RT_, RTHooks>(
1412
711
 
1413
712
  // TODO: Clean up this delay initialisation messs
1414
713
  // TODO; invalidateQueries should perhaps be configured in the Request impl themselves?
1415
- const clientFor__ = <M extends Requests>(
714
+ const clientFor__ = <M extends RequestsAny>(
1416
715
  m: M,
1417
716
  queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>
1418
717
  ) => getBaseMrt().runSync(clientFor_(m).pipe(Effect.map(mapClient(queryInvalidation))))
@@ -1420,7 +719,7 @@ export const makeClient = <RT_, RTHooks>(
1420
719
  // delay client creation until first access
1421
720
  // the idea is that we don't need the useNuxtApp().$runtime (only available at later initialisation stage)
1422
721
  // until we are at a place where it is available..
1423
- const clientFor = <M extends Requests>(
722
+ const clientFor = <M extends RequestsAny>(
1424
723
  m: M,
1425
724
  queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>
1426
725
  ) => {
@@ -1444,11 +743,6 @@ export const makeClient = <RT_, RTHooks>(
1444
743
  return proxy
1445
744
  }
1446
745
 
1447
- const legacy: Legacy<RT> = {
1448
- ...mutations,
1449
- ...query
1450
- }
1451
-
1452
746
  const Command: CommanderResolved<RT, RTHooks> = {
1453
747
  ...{
1454
748
  // delay initialisation until first use...
@@ -1463,17 +757,10 @@ export const makeClient = <RT_, RTHooks>(
1463
757
  return {
1464
758
  Command,
1465
759
  useCommand,
1466
- clientFor,
1467
- legacy
760
+ clientFor
1468
761
  }
1469
762
  }
1470
763
 
1471
- export interface Legacy<R>
1472
- extends
1473
- Pick<QueryImpl<R>, "useQuery" | "useSuspenseQuery">,
1474
- Omit<LegacyMutationImpl<R>, "getRuntime" | "toast" | "intl">
1475
- {}
1476
-
1477
764
  export type QueryInvalidation<M> = {
1478
765
  [K in keyof M]?: (defaultKey: string[], name: string) => {
1479
766
  filters?: InvalidateQueryFilters | undefined
@@ -1497,6 +784,9 @@ export interface CommandBase<I = void, A = void> {
1497
784
 
1498
785
  export interface EffectCommand<I = void, A = unknown, E = unknown> extends CommandBase<I, Fiber<A, E>> {}
1499
786
 
1500
- export interface CommandFromRequest<I extends abstract new(...args: any) => any, A = unknown, E = unknown>
1501
- extends EffectCommand<ConstructorParameters<I>[0], A, E>
787
+ type RequestInputFromMake<I extends { readonly make: (...args: any[]) => any }> = Parameters<I["make"]> extends
788
+ [infer A, ...ReadonlyArray<any>] ? A : void
789
+
790
+ export interface CommandFromRequest<I extends { readonly make: (...args: any[]) => any }, A = unknown, E = unknown>
791
+ extends EffectCommand<RequestInputFromMake<I>, A, E>
1502
792
  {}