@effect-app/vue 4.0.0-beta.99 → 4.0.0
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 +2701 -0
- package/dist/atomQuery.d.ts +120 -0
- package/dist/atomQuery.d.ts.map +1 -0
- package/dist/atomQuery.js +434 -0
- package/dist/commander.d.ts +318 -43
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +593 -81
- package/dist/confirm.d.ts +5 -3
- package/dist/confirm.d.ts.map +1 -1
- package/dist/confirm.js +12 -14
- package/dist/dependencyMetadata.d.ts +18 -0
- package/dist/dependencyMetadata.d.ts.map +1 -0
- package/dist/dependencyMetadata.js +67 -0
- package/dist/errorReporter.d.ts +6 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +14 -19
- package/dist/form.d.ts +5 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +16 -10
- package/dist/internal/tanstackQuery.d.ts +9 -0
- package/dist/internal/tanstackQuery.d.ts.map +1 -0
- package/dist/internal/tanstackQuery.js +204 -0
- package/dist/intl.d.ts +4 -4
- package/dist/intl.d.ts.map +1 -1
- package/dist/intl.js +2 -2
- package/dist/lib.d.ts +8 -10
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +37 -12
- package/dist/liveQueryInvalidation.d.ts +16 -0
- package/dist/liveQueryInvalidation.d.ts.map +1 -0
- package/dist/liveQueryInvalidation.js +115 -0
- package/dist/makeClient.d.ts +232 -109
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +416 -84
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/makeUseCommand.d.ts +3 -2
- package/dist/makeUseCommand.d.ts.map +1 -1
- package/dist/makeUseCommand.js +2 -2
- package/dist/mutate.d.ts +110 -41
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +236 -59
- package/dist/query.d.ts +161 -44
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +382 -113
- package/dist/queryLifetime.d.ts +25 -0
- package/dist/queryLifetime.d.ts.map +1 -0
- package/dist/queryLifetime.js +54 -0
- package/dist/routeParams.d.ts +4 -4
- package/dist/routeParams.d.ts.map +1 -1
- package/dist/routeParams.js +4 -3
- package/dist/runtime.d.ts +1 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -38
- package/dist/suspense.d.ts +19 -0
- package/dist/suspense.d.ts.map +1 -0
- package/dist/suspense.js +38 -0
- package/dist/toast.d.ts +1 -45
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +2 -32
- package/dist/withToast.d.ts +1 -24
- package/dist/withToast.d.ts.map +1 -1
- package/dist/withToast.js +2 -45
- package/docs/atom-query-api-redesign.md +207 -0
- package/docs/mutation-command-atoms.md +40 -0
- package/docs/query-key-invalidation.md +107 -0
- package/examples/streamMutation.ts +72 -0
- package/package.json +35 -94
- package/src/atomQuery.ts +600 -0
- package/src/commander.ts +1064 -116
- package/src/confirm.ts +12 -14
- package/src/dependencyMetadata.ts +84 -0
- package/src/errorReporter.ts +65 -75
- package/src/form.ts +22 -15
- package/src/index.ts +7 -7
- package/src/internal/tanstackQuery.ts +308 -0
- package/src/intl.ts +2 -2
- package/src/lib.ts +51 -23
- package/src/liveQueryInvalidation.ts +138 -0
- package/src/makeClient.ts +972 -263
- package/src/makeIntl.ts +2 -2
- package/src/makeUseCommand.ts +8 -5
- package/src/mutate.ts +407 -154
- package/src/query.ts +758 -242
- package/src/queryLifetime.ts +74 -0
- package/src/routeParams.ts +7 -7
- package/src/runtime.ts +1 -54
- package/src/suspense.ts +43 -0
- package/src/toast.ts +1 -52
- package/src/withToast.ts +1 -99
- package/test/Mutation.test.ts +104 -17
- package/test/dependencyInvalidation.test.ts +444 -0
- package/test/dist/dependencyInvalidation.test.d.ts.map +1 -0
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/inactiveInvalidation.test.d.ts.map +1 -0
- package/test/dist/interrupt-refetch-repro.test.d.ts.map +1 -0
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/liveQueryInvalidation.test.d.ts.map +1 -0
- package/test/dist/query-span.test.d.ts.map +1 -0
- package/test/dist/queryOptions.test.d.ts.map +1 -0
- package/test/dist/streamFinal.test.d.ts.map +1 -0
- package/test/dist/streamFn.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +3681 -256
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +166 -25
- package/test/dist/suspense-regression.test.d.ts.map +1 -0
- package/test/dist/suspense.test.d.ts.map +1 -0
- package/test/form-validation-errors.test.ts +2 -1
- package/test/form.test.ts +2 -1
- package/test/inactiveInvalidation.test.ts +172 -0
- package/test/interrupt-refetch-repro.test.ts +625 -0
- package/test/lib.test.ts +240 -0
- package/test/liveQueryInvalidation.test.ts +108 -0
- package/test/makeClient.test.ts +414 -32
- package/test/query-span.test.ts +92 -0
- package/test/queryOptions.test.ts +88 -0
- package/test/streamFinal.test.ts +64 -0
- package/test/streamFn.test.ts +457 -0
- package/test/stubs.ts +194 -36
- package/test/suspense-regression.test.ts +168 -0
- package/test/suspense.test.ts +160 -0
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +9 -1
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/eslint.config.mjs +0 -24
- package/tsconfig.json.bak +0 -12
package/src/commander.ts
CHANGED
|
@@ -1,19 +1,44 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { asResult, type MissingDependencies, reportRuntimeError } from "@effect-app/vue"
|
|
3
2
|
import { reportMessage } from "@effect-app/vue/errorReporter"
|
|
4
|
-
import {
|
|
3
|
+
import { deepToRaw, reportRuntimeError } from "@effect-app/vue/lib"
|
|
4
|
+
import { type MissingDependencies } from "@effect-app/vue/makeClient"
|
|
5
|
+
import { asResult, asStreamResult } from "@effect-app/vue/mutate"
|
|
5
6
|
import { SupportedErrors } from "effect-app/client"
|
|
6
|
-
import
|
|
7
|
+
import * as Context from "effect-app/Context"
|
|
8
|
+
import * as Effect from "effect-app/Effect"
|
|
9
|
+
import * as Layer from "effect-app/Layer"
|
|
10
|
+
import * as Option from "effect-app/Option"
|
|
11
|
+
import * as S from "effect-app/Schema"
|
|
7
12
|
import { isGeneratorFunction, wrapEffect } from "effect-app/utils"
|
|
8
|
-
import
|
|
13
|
+
import * as Cause from "effect/Cause"
|
|
14
|
+
import type * as Exit from "effect/Exit"
|
|
15
|
+
import type * as Fiber from "effect/Fiber"
|
|
16
|
+
import { flow } from "effect/Function"
|
|
17
|
+
import * as MutableHashMap from "effect/MutableHashMap"
|
|
18
|
+
import * as Predicate from "effect/Predicate"
|
|
19
|
+
import { type Refinement } from "effect/Predicate"
|
|
20
|
+
import * as AsyncResult from "effect/reactivity/AsyncResult"
|
|
21
|
+
import * as Stream from "effect/Stream"
|
|
9
22
|
import { type FormatXMLElementFn, type PrimitiveType } from "intl-messageformat"
|
|
10
|
-
import { computed, type ComputedRef, reactive, ref } from "vue"
|
|
11
|
-
import { Confirm } from "./confirm.
|
|
12
|
-
import { I18n } from "./intl.
|
|
13
|
-
import {
|
|
23
|
+
import { computed, type ComputedRef, reactive, ref, toRaw } from "vue"
|
|
24
|
+
import { Confirm } from "./confirm.ts"
|
|
25
|
+
import { I18n } from "./intl.ts"
|
|
26
|
+
import { CurrentToastId, Toast } from "./toast.ts"
|
|
27
|
+
import { WithToast } from "./withToast.ts"
|
|
14
28
|
|
|
15
29
|
type IntlRecord = Record<string, PrimitiveType | FormatXMLElementFn<string, string>>
|
|
16
|
-
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Progress information surfaced by a stream command. Either a plain text label
|
|
33
|
+
* or a `{ text, percentage }` pair when concrete progress is known.
|
|
34
|
+
*/
|
|
35
|
+
export type Progress = string | { readonly text: string; readonly percentage: number }
|
|
36
|
+
|
|
37
|
+
type FnOptions<
|
|
38
|
+
Id extends string,
|
|
39
|
+
I18nCustomKey extends string,
|
|
40
|
+
State extends IntlRecord | undefined
|
|
41
|
+
> = {
|
|
17
42
|
i18nCustomKey?: I18nCustomKey
|
|
18
43
|
/**
|
|
19
44
|
* passed to the i18n formatMessage calls so you can use it in translation messagee
|
|
@@ -78,6 +103,35 @@ export class CommandContext extends Context.Service<CommandContext, {
|
|
|
78
103
|
"CommandContext"
|
|
79
104
|
) {}
|
|
80
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Service available inside `streamFn` stream handlers that lets you imperatively push
|
|
108
|
+
* progress updates to the command's reactive `progress` ref.
|
|
109
|
+
*
|
|
110
|
+
* Use `Command.mapProgress(fn)` or `Command.updateProgress(progress)` to interact with this service.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* // Using mapProgress (recommended) — applied as a stream pipe operator:
|
|
115
|
+
* const exportCmd = Command.streamFn("exportData")(
|
|
116
|
+
* function*(arg, ctx) {
|
|
117
|
+
* return makeExportStream(arg.id).pipe(
|
|
118
|
+
* Command.mapProgress((r) =>
|
|
119
|
+
* AsyncResult.isSuccess(r) && r.value._tag === "OperationProgress"
|
|
120
|
+
* ? { text: `${r.value.completed}/${r.value.total}`, percentage: r.value.completed / r.value.total * 100 }
|
|
121
|
+
* : undefined
|
|
122
|
+
* )
|
|
123
|
+
* )
|
|
124
|
+
* }
|
|
125
|
+
* )
|
|
126
|
+
* // exportCmd.progress is updated for every OperationProgress event
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export class CommandProgress extends Context.Reference<{
|
|
130
|
+
readonly update: (progress: Progress | undefined) => Effect.Effect<void>
|
|
131
|
+
}>("Commander.CommandProgress", {
|
|
132
|
+
defaultValue: () => ({ update: (_progress: Progress | undefined): Effect.Effect<void> => Effect.void })
|
|
133
|
+
}) {}
|
|
134
|
+
|
|
81
135
|
export type EmitWithCallback<A, Event extends string> = (event: Event, value: A, onDone: () => void) => void
|
|
82
136
|
|
|
83
137
|
/**
|
|
@@ -91,6 +145,19 @@ export const wrapEmit = <A, Event extends string>(
|
|
|
91
145
|
(value: A) => new Promise<void>((resolve) => emit(event, value, resolve))
|
|
92
146
|
|
|
93
147
|
export declare namespace Commander {
|
|
148
|
+
export type IntlRecord = Record<string, PrimitiveType | FormatXMLElementFn<string, string>>
|
|
149
|
+
export type FnOptions<
|
|
150
|
+
Id extends string,
|
|
151
|
+
I18nKey extends string,
|
|
152
|
+
State extends IntlRecord | undefined
|
|
153
|
+
> = {
|
|
154
|
+
i18nCustomKey?: I18nKey
|
|
155
|
+
state?: ComputedRef<State> | (() => State)
|
|
156
|
+
blockKey?: (id: Id) => string | undefined
|
|
157
|
+
waitKey?: (id: Id) => string | undefined
|
|
158
|
+
allowed?: (id: Id, state: ComputedRef<State>) => boolean
|
|
159
|
+
}
|
|
160
|
+
|
|
94
161
|
export type CommanderBase<RT, Id extends string, I18nKey extends string, State extends IntlRecord | undefined> =
|
|
95
162
|
& Gen<RT, Id, I18nKey, State>
|
|
96
163
|
& NonGen<RT, Id, I18nKey, State>
|
|
@@ -138,7 +205,12 @@ export declare namespace Commander {
|
|
|
138
205
|
/** reactive */
|
|
139
206
|
label: string
|
|
140
207
|
/** reactive */
|
|
141
|
-
result: AsyncResult<A, E>
|
|
208
|
+
result: AsyncResult.AsyncResult<A, E>
|
|
209
|
+
/**
|
|
210
|
+
* reactive – formatted progress info driven by `Command.mapProgress` or `Command.updateProgress`
|
|
211
|
+
* inside a `streamFn` handler. Undefined for non-stream commands.
|
|
212
|
+
*/
|
|
213
|
+
progress: Progress | undefined
|
|
142
214
|
/** reactive */
|
|
143
215
|
waiting: boolean
|
|
144
216
|
/** reactive */
|
|
@@ -161,7 +233,7 @@ export declare namespace Commander {
|
|
|
161
233
|
new(): {}
|
|
162
234
|
|
|
163
235
|
/** click handlers */
|
|
164
|
-
handle: ((arg: Arg) => Fiber.Fiber<Exit.Exit<A, E
|
|
236
|
+
handle: ((arg: Arg) => Fiber.Fiber<Exit.Exit<A, E>>) & {
|
|
165
237
|
/** @deprecated don't exist */
|
|
166
238
|
effect: (arg: Arg) => Effect.Effect<A, E, R>
|
|
167
239
|
}
|
|
@@ -208,7 +280,7 @@ export declare namespace Commander {
|
|
|
208
280
|
|
|
209
281
|
export type Gen<RT, Id extends string, I18nKey extends string, State extends IntlRecord | undefined> = {
|
|
210
282
|
<
|
|
211
|
-
Eff extends Effect.
|
|
283
|
+
Eff extends Effect.Effect<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
212
284
|
AEff,
|
|
213
285
|
Arg = void
|
|
214
286
|
>(
|
|
@@ -217,17 +289,17 @@ export declare namespace Commander {
|
|
|
217
289
|
Arg,
|
|
218
290
|
AEff,
|
|
219
291
|
[Eff] extends [never] ? never
|
|
220
|
-
: [Eff] extends [Effect.
|
|
292
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
221
293
|
: never,
|
|
222
294
|
[Eff] extends [never] ? never
|
|
223
|
-
: [Eff] extends [Effect.
|
|
295
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
224
296
|
: never,
|
|
225
297
|
Id,
|
|
226
298
|
I18nKey,
|
|
227
299
|
State
|
|
228
300
|
>
|
|
229
301
|
<
|
|
230
|
-
Eff extends Effect.
|
|
302
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
231
303
|
AEff,
|
|
232
304
|
A extends Effect.Effect<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
233
305
|
Arg = void
|
|
@@ -237,10 +309,10 @@ export declare namespace Commander {
|
|
|
237
309
|
_: Effect.Effect<
|
|
238
310
|
AEff,
|
|
239
311
|
[Eff] extends [never] ? never
|
|
240
|
-
: [Eff] extends [Effect.
|
|
312
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
241
313
|
: never,
|
|
242
314
|
[Eff] extends [never] ? never
|
|
243
|
-
: [Eff] extends [Effect.
|
|
315
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
244
316
|
: never
|
|
245
317
|
>,
|
|
246
318
|
arg: ArgForCombinator<Arg>,
|
|
@@ -248,7 +320,7 @@ export declare namespace Commander {
|
|
|
248
320
|
) => A
|
|
249
321
|
): CommandOutHelper<Arg, A, Id, I18nKey, State>
|
|
250
322
|
<
|
|
251
|
-
Eff extends Effect.
|
|
323
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
252
324
|
AEff,
|
|
253
325
|
A,
|
|
254
326
|
B extends Effect.Effect<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
@@ -259,10 +331,10 @@ export declare namespace Commander {
|
|
|
259
331
|
_: Effect.Effect<
|
|
260
332
|
AEff,
|
|
261
333
|
[Eff] extends [never] ? never
|
|
262
|
-
: [Eff] extends [Effect.
|
|
334
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
263
335
|
: never,
|
|
264
336
|
[Eff] extends [never] ? never
|
|
265
|
-
: [Eff] extends [Effect.
|
|
337
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
266
338
|
: never
|
|
267
339
|
>,
|
|
268
340
|
arg: ArgForCombinator<Arg>,
|
|
@@ -275,7 +347,7 @@ export declare namespace Commander {
|
|
|
275
347
|
) => B
|
|
276
348
|
): CommandOutHelper<Arg, B, Id, I18nKey, State>
|
|
277
349
|
<
|
|
278
|
-
Eff extends Effect.
|
|
350
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
279
351
|
AEff,
|
|
280
352
|
A,
|
|
281
353
|
B,
|
|
@@ -287,10 +359,10 @@ export declare namespace Commander {
|
|
|
287
359
|
_: Effect.Effect<
|
|
288
360
|
AEff,
|
|
289
361
|
[Eff] extends [never] ? never
|
|
290
|
-
: [Eff] extends [Effect.
|
|
362
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
291
363
|
: never,
|
|
292
364
|
[Eff] extends [never] ? never
|
|
293
|
-
: [Eff] extends [Effect.
|
|
365
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
294
366
|
: never
|
|
295
367
|
>,
|
|
296
368
|
arg: ArgForCombinator<Arg>,
|
|
@@ -308,7 +380,7 @@ export declare namespace Commander {
|
|
|
308
380
|
) => C
|
|
309
381
|
): CommandOutHelper<Arg, C, Id, I18nKey, State>
|
|
310
382
|
<
|
|
311
|
-
Eff extends Effect.
|
|
383
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
312
384
|
AEff,
|
|
313
385
|
A,
|
|
314
386
|
B,
|
|
@@ -321,10 +393,10 @@ export declare namespace Commander {
|
|
|
321
393
|
_: Effect.Effect<
|
|
322
394
|
AEff,
|
|
323
395
|
[Eff] extends [never] ? never
|
|
324
|
-
: [Eff] extends [Effect.
|
|
396
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
325
397
|
: never,
|
|
326
398
|
[Eff] extends [never] ? never
|
|
327
|
-
: [Eff] extends [Effect.
|
|
399
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
328
400
|
: never
|
|
329
401
|
>,
|
|
330
402
|
arg: ArgForCombinator<Arg>,
|
|
@@ -347,7 +419,7 @@ export declare namespace Commander {
|
|
|
347
419
|
) => D
|
|
348
420
|
): CommandOutHelper<Arg, D, Id, I18nKey, State>
|
|
349
421
|
<
|
|
350
|
-
Eff extends Effect.
|
|
422
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
351
423
|
AEff,
|
|
352
424
|
A,
|
|
353
425
|
B,
|
|
@@ -361,10 +433,10 @@ export declare namespace Commander {
|
|
|
361
433
|
_: Effect.Effect<
|
|
362
434
|
AEff,
|
|
363
435
|
[Eff] extends [never] ? never
|
|
364
|
-
: [Eff] extends [Effect.
|
|
436
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
365
437
|
: never,
|
|
366
438
|
[Eff] extends [never] ? never
|
|
367
|
-
: [Eff] extends [Effect.
|
|
439
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
368
440
|
: never
|
|
369
441
|
>,
|
|
370
442
|
arg: ArgForCombinator<Arg>,
|
|
@@ -392,7 +464,7 @@ export declare namespace Commander {
|
|
|
392
464
|
) => E
|
|
393
465
|
): CommandOutHelper<Arg, E, Id, I18nKey, State>
|
|
394
466
|
<
|
|
395
|
-
Eff extends Effect.
|
|
467
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
396
468
|
AEff,
|
|
397
469
|
A,
|
|
398
470
|
B,
|
|
@@ -407,10 +479,10 @@ export declare namespace Commander {
|
|
|
407
479
|
_: Effect.Effect<
|
|
408
480
|
AEff,
|
|
409
481
|
[Eff] extends [never] ? never
|
|
410
|
-
: [Eff] extends [Effect.
|
|
482
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
411
483
|
: never,
|
|
412
484
|
[Eff] extends [never] ? never
|
|
413
|
-
: [Eff] extends [Effect.
|
|
485
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
414
486
|
: never
|
|
415
487
|
>,
|
|
416
488
|
arg: ArgForCombinator<Arg>,
|
|
@@ -443,7 +515,7 @@ export declare namespace Commander {
|
|
|
443
515
|
) => F
|
|
444
516
|
): CommandOutHelper<Arg, F, Id, I18nKey, State>
|
|
445
517
|
<
|
|
446
|
-
Eff extends Effect.
|
|
518
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
447
519
|
AEff,
|
|
448
520
|
A,
|
|
449
521
|
B,
|
|
@@ -459,10 +531,10 @@ export declare namespace Commander {
|
|
|
459
531
|
_: Effect.Effect<
|
|
460
532
|
AEff,
|
|
461
533
|
[Eff] extends [never] ? never
|
|
462
|
-
: [Eff] extends [Effect.
|
|
534
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
463
535
|
: never,
|
|
464
536
|
[Eff] extends [never] ? never
|
|
465
|
-
: [Eff] extends [Effect.
|
|
537
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
466
538
|
: never
|
|
467
539
|
>,
|
|
468
540
|
arg: ArgForCombinator<Arg>,
|
|
@@ -500,7 +572,7 @@ export declare namespace Commander {
|
|
|
500
572
|
) => G
|
|
501
573
|
): CommandOutHelper<Arg, G, Id, I18nKey, State>
|
|
502
574
|
<
|
|
503
|
-
Eff extends Effect.
|
|
575
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
504
576
|
AEff,
|
|
505
577
|
A,
|
|
506
578
|
B,
|
|
@@ -517,10 +589,10 @@ export declare namespace Commander {
|
|
|
517
589
|
_: Effect.Effect<
|
|
518
590
|
AEff,
|
|
519
591
|
[Eff] extends [never] ? never
|
|
520
|
-
: [Eff] extends [Effect.
|
|
592
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
521
593
|
: never,
|
|
522
594
|
[Eff] extends [never] ? never
|
|
523
|
-
: [Eff] extends [Effect.
|
|
595
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
524
596
|
: never
|
|
525
597
|
>,
|
|
526
598
|
arg: ArgForCombinator<Arg>,
|
|
@@ -563,7 +635,7 @@ export declare namespace Commander {
|
|
|
563
635
|
) => H
|
|
564
636
|
): CommandOutHelper<Arg, H, Id, I18nKey, State>
|
|
565
637
|
<
|
|
566
|
-
Eff extends Effect.
|
|
638
|
+
Eff extends Effect.Effect<any, any, any>,
|
|
567
639
|
AEff,
|
|
568
640
|
A,
|
|
569
641
|
B,
|
|
@@ -581,10 +653,10 @@ export declare namespace Commander {
|
|
|
581
653
|
_: Effect.Effect<
|
|
582
654
|
AEff,
|
|
583
655
|
[Eff] extends [never] ? never
|
|
584
|
-
: [Eff] extends [Effect.
|
|
656
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
585
657
|
: never,
|
|
586
658
|
[Eff] extends [never] ? never
|
|
587
|
-
: [Eff] extends [Effect.
|
|
659
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
588
660
|
: never
|
|
589
661
|
>,
|
|
590
662
|
arg: ArgForCombinator<Arg>,
|
|
@@ -1657,46 +1729,288 @@ export declare namespace Commander {
|
|
|
1657
1729
|
) => Eff
|
|
1658
1730
|
): CommandOutHelper<Arg, Eff, Id, I18nKey, State>
|
|
1659
1731
|
}
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* Type for `streamFn` — generator overload where the body yields Effects and returns a `Stream`.
|
|
1735
|
+
* `waiting` stays `true` while the stream is running, and updates the `result` ref per emitted value.
|
|
1736
|
+
*/
|
|
1737
|
+
export type StreamGen<RT, Id extends string, I18nKey extends string, State extends IntlRecord | undefined> = {
|
|
1738
|
+
<
|
|
1739
|
+
Eff extends Effect.Effect<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
1740
|
+
SA,
|
|
1741
|
+
SE,
|
|
1742
|
+
SR,
|
|
1743
|
+
Arg = void
|
|
1744
|
+
>(
|
|
1745
|
+
body: (
|
|
1746
|
+
arg: Arg,
|
|
1747
|
+
ctx: CommandContextLocal2<Id, I18nKey, State>
|
|
1748
|
+
) => Generator<Eff, Stream.Stream<SA, SE, SR>, never>
|
|
1749
|
+
): CommandOut<
|
|
1750
|
+
Arg,
|
|
1751
|
+
SA,
|
|
1752
|
+
| SE
|
|
1753
|
+
| ([Eff] extends [never] ? never
|
|
1754
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
1755
|
+
: never),
|
|
1756
|
+
| SR
|
|
1757
|
+
| ([Eff] extends [never] ? never
|
|
1758
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
1759
|
+
: never),
|
|
1760
|
+
Id,
|
|
1761
|
+
I18nKey,
|
|
1762
|
+
State
|
|
1763
|
+
>
|
|
1764
|
+
<
|
|
1765
|
+
Eff extends Effect.Effect<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
1766
|
+
SA,
|
|
1767
|
+
SE,
|
|
1768
|
+
SR,
|
|
1769
|
+
B,
|
|
1770
|
+
Arg = void
|
|
1771
|
+
>(
|
|
1772
|
+
body: (
|
|
1773
|
+
arg: Arg,
|
|
1774
|
+
ctx: CommandContextLocal2<Id, I18nKey, State>
|
|
1775
|
+
) => Generator<Eff, Stream.Stream<SA, SE, SR>, never>,
|
|
1776
|
+
a: (
|
|
1777
|
+
_: Effect.Effect<
|
|
1778
|
+
Stream.Stream<SA, SE, SR>,
|
|
1779
|
+
([Eff] extends [never] ? never
|
|
1780
|
+
: [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E
|
|
1781
|
+
: never),
|
|
1782
|
+
([Eff] extends [never] ? never
|
|
1783
|
+
: [Eff] extends [Effect.Effect<infer _A, infer _E, infer R>] ? R
|
|
1784
|
+
: never)
|
|
1785
|
+
>,
|
|
1786
|
+
arg: ArgForCombinator<Arg>,
|
|
1787
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1788
|
+
) => B
|
|
1789
|
+
): B extends Stream.Stream<infer SA2, infer SE2, infer SR2> ? CommandOut<Arg, SA2, SE2, SR2, Id, I18nKey, State>
|
|
1790
|
+
: B extends Effect.Effect<Stream.Stream<infer SA2, infer SE2, infer SR2>, infer EE2, infer ER2>
|
|
1791
|
+
? CommandOut<Arg, SA2, SE2 | EE2, SR2 | ER2, Id, I18nKey, State>
|
|
1792
|
+
: never
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
/**
|
|
1796
|
+
* Type for `streamFn` — non-generator overload accepting a function that returns a `Stream` directly,
|
|
1797
|
+
* or an `Effect` that resolves to a `Stream`.
|
|
1798
|
+
*/
|
|
1799
|
+
export type NonGenStream<RT, Id extends string, I18nKey extends string, State extends IntlRecord | undefined> = {
|
|
1800
|
+
<
|
|
1801
|
+
SA,
|
|
1802
|
+
SE,
|
|
1803
|
+
SR extends RT | CommandContext | `Commander.Command.${Id}.state`,
|
|
1804
|
+
Arg = void
|
|
1805
|
+
>(
|
|
1806
|
+
body: (arg: Arg, ctx: CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>
|
|
1807
|
+
): CommandOut<Arg, SA, SE, SR, Id, I18nKey, State>
|
|
1808
|
+
<
|
|
1809
|
+
SA,
|
|
1810
|
+
SE,
|
|
1811
|
+
SR,
|
|
1812
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>,
|
|
1813
|
+
Arg = void
|
|
1814
|
+
>(
|
|
1815
|
+
body: (arg: Arg, ctx: CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>,
|
|
1816
|
+
a: (
|
|
1817
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1818
|
+
arg: ArgForCombinator<Arg>,
|
|
1819
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1820
|
+
) => A
|
|
1821
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1822
|
+
<
|
|
1823
|
+
SA,
|
|
1824
|
+
SE,
|
|
1825
|
+
SR,
|
|
1826
|
+
EE,
|
|
1827
|
+
ER extends RT | CommandContext | `Commander.Command.${Id}.state`,
|
|
1828
|
+
Arg = void
|
|
1829
|
+
>(
|
|
1830
|
+
body: (
|
|
1831
|
+
arg: Arg,
|
|
1832
|
+
ctx: CommandContextLocal2<Id, I18nKey, State>
|
|
1833
|
+
) => Effect.Effect<Stream.Stream<SA, SE, SR>, EE, ER>
|
|
1834
|
+
): CommandOut<Arg, SA, SE | EE, SR | ER, Id, I18nKey, State>
|
|
1835
|
+
<
|
|
1836
|
+
SA,
|
|
1837
|
+
SE,
|
|
1838
|
+
SR,
|
|
1839
|
+
EE,
|
|
1840
|
+
ER,
|
|
1841
|
+
B,
|
|
1842
|
+
Arg = void
|
|
1843
|
+
>(
|
|
1844
|
+
body: (
|
|
1845
|
+
arg: Arg,
|
|
1846
|
+
ctx: CommandContextLocal2<Id, I18nKey, State>
|
|
1847
|
+
) => Effect.Effect<Stream.Stream<SA, SE, SR>, EE, ER>,
|
|
1848
|
+
a: (
|
|
1849
|
+
_: Effect.Effect<Stream.Stream<SA, SE, SR>, EE, ER>,
|
|
1850
|
+
arg: ArgForCombinator<Arg>,
|
|
1851
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1852
|
+
) => B
|
|
1853
|
+
): B extends Stream.Stream<infer SA2, infer SE2, infer SR2> ? CommandOut<Arg, SA2, SE2, SR2, Id, I18nKey, State>
|
|
1854
|
+
: B extends Effect.Effect<Stream.Stream<infer SA2, infer SE2, infer SR2>, infer EE2, infer ER2>
|
|
1855
|
+
? CommandOut<Arg, SA2, SE2 | EE2, SR2 | ER2, Id, I18nKey, State>
|
|
1856
|
+
: never
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* Type returned by `mutate.wrap` on a stream handler — analogous to `CommanderWrap` but for streams.
|
|
1861
|
+
* The handler is pre-baked (from the stream mutation), so this is called with only optional combinators.
|
|
1862
|
+
*/
|
|
1863
|
+
export type StreamerWrap<
|
|
1864
|
+
RT,
|
|
1865
|
+
Id extends string,
|
|
1866
|
+
I18nKey extends string,
|
|
1867
|
+
State extends IntlRecord | undefined,
|
|
1868
|
+
Arg,
|
|
1869
|
+
SA,
|
|
1870
|
+
SE,
|
|
1871
|
+
SR
|
|
1872
|
+
> =
|
|
1873
|
+
& CommandContextLocal<Id, I18nKey>
|
|
1874
|
+
& { readonly state: Context.Service<`Commander.Command.${Id}.state`, State> }
|
|
1875
|
+
& {
|
|
1876
|
+
(): Exclude<SR, RT> extends never ? CommandOut<Arg, SA, SE, SR, Id, I18nKey, State>
|
|
1877
|
+
: MissingDependencies<RT, SR> & {}
|
|
1878
|
+
<A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>>(
|
|
1879
|
+
a: (
|
|
1880
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1881
|
+
arg: ArgForCombinator<Arg>,
|
|
1882
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1883
|
+
) => A
|
|
1884
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1885
|
+
<
|
|
1886
|
+
B,
|
|
1887
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1888
|
+
>(
|
|
1889
|
+
a: (
|
|
1890
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1891
|
+
arg: ArgForCombinator<Arg>,
|
|
1892
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1893
|
+
) => B,
|
|
1894
|
+
b: (
|
|
1895
|
+
_: B,
|
|
1896
|
+
arg: ArgForCombinator<Arg>,
|
|
1897
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1898
|
+
) => A
|
|
1899
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1900
|
+
<
|
|
1901
|
+
B,
|
|
1902
|
+
C,
|
|
1903
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1904
|
+
>(
|
|
1905
|
+
a: (
|
|
1906
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1907
|
+
arg: ArgForCombinator<Arg>,
|
|
1908
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1909
|
+
) => B,
|
|
1910
|
+
b: (
|
|
1911
|
+
_: B,
|
|
1912
|
+
arg: ArgForCombinator<Arg>,
|
|
1913
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1914
|
+
) => C,
|
|
1915
|
+
c: (
|
|
1916
|
+
_: C,
|
|
1917
|
+
arg: ArgForCombinator<Arg>,
|
|
1918
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1919
|
+
) => A
|
|
1920
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1921
|
+
<
|
|
1922
|
+
B,
|
|
1923
|
+
C,
|
|
1924
|
+
D,
|
|
1925
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1926
|
+
>(
|
|
1927
|
+
a: (
|
|
1928
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1929
|
+
arg: ArgForCombinator<Arg>,
|
|
1930
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1931
|
+
) => B,
|
|
1932
|
+
b: (
|
|
1933
|
+
_: B,
|
|
1934
|
+
arg: ArgForCombinator<Arg>,
|
|
1935
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1936
|
+
) => C,
|
|
1937
|
+
c: (
|
|
1938
|
+
_: C,
|
|
1939
|
+
arg: ArgForCombinator<Arg>,
|
|
1940
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1941
|
+
) => D,
|
|
1942
|
+
d: (
|
|
1943
|
+
_: D,
|
|
1944
|
+
arg: ArgForCombinator<Arg>,
|
|
1945
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1946
|
+
) => A
|
|
1947
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1948
|
+
}
|
|
1660
1949
|
}
|
|
1661
1950
|
|
|
1662
1951
|
type ErrorRenderer<E, Args extends readonly any[]> = (e: E, action: string, ...args: Args) => string | undefined
|
|
1663
1952
|
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1953
|
+
type RegisteredErrorRenderer<A> = {
|
|
1954
|
+
guard: Refinement<unknown, A>
|
|
1955
|
+
render: (guarded: A) => string | undefined
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
export class CommanderErrorRenderers extends Context.Reference("Commander.ErrorRenderers", {
|
|
1959
|
+
defaultValue: () => [] as RegisteredErrorRenderer<any>[]
|
|
1960
|
+
}) {}
|
|
1961
|
+
|
|
1962
|
+
export const makeRegisteredErrorRenderer = <A>(
|
|
1963
|
+
guard: Predicate.Refinement<unknown, A>,
|
|
1964
|
+
render: (guarded: A) => string | undefined
|
|
1965
|
+
): RegisteredErrorRenderer<A> => ({
|
|
1966
|
+
guard,
|
|
1967
|
+
render
|
|
1968
|
+
})
|
|
1969
|
+
|
|
1970
|
+
const renderErrorMaker = Effect.gen(function*() {
|
|
1971
|
+
const { intl } = yield* I18n
|
|
1972
|
+
const registeredRenderers = yield* CommanderErrorRenderers
|
|
1973
|
+
return (
|
|
1974
|
+
<E, Args extends readonly any[]>(action: string, errorRenderer?: ErrorRenderer<E, Args>) =>
|
|
1975
|
+
(e: E, ...args: Args): string => {
|
|
1976
|
+
if (errorRenderer) {
|
|
1977
|
+
const m = errorRenderer(e, action, ...args)
|
|
1978
|
+
if (m !== undefined) {
|
|
1979
|
+
return m
|
|
1980
|
+
}
|
|
1672
1981
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
if ("message" in e) {
|
|
1677
|
-
return `${e.message}`
|
|
1982
|
+
for (const entry of registeredRenderers) {
|
|
1983
|
+
if (!entry.guard(e)) {
|
|
1984
|
+
continue
|
|
1678
1985
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1986
|
+
const m = entry.render(e)
|
|
1987
|
+
if (m !== undefined) {
|
|
1988
|
+
return m
|
|
1681
1989
|
}
|
|
1682
1990
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
SchemaError: (e) => {
|
|
1692
|
-
console.warn(e.toString())
|
|
1693
|
-
return intl.formatMessage({ id: "validation.failed" })
|
|
1991
|
+
if (!S.is(SupportedErrors)(e) && !S.isSchemaError(e)) {
|
|
1992
|
+
if (typeof e === "object" && e !== null) {
|
|
1993
|
+
if ("message" in e) {
|
|
1994
|
+
return `${e.message}`
|
|
1995
|
+
}
|
|
1996
|
+
if ("_tag" in e) {
|
|
1997
|
+
return `${e._tag}`
|
|
1998
|
+
}
|
|
1694
1999
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
)
|
|
2000
|
+
return ""
|
|
2001
|
+
}
|
|
2002
|
+
const e2: SupportedErrors | S.SchemaError = e
|
|
2003
|
+
if (e2._tag === "NotFoundError") {
|
|
2004
|
+
return intl.formatMessage({ id: "handle.not_found" }, { type: e2.type, id: String(e2.id) })
|
|
2005
|
+
}
|
|
2006
|
+
if (e2._tag === "SchemaError") {
|
|
2007
|
+
console.warn(e2.toString())
|
|
2008
|
+
return intl.formatMessage({ id: "validation.failed" })
|
|
2009
|
+
}
|
|
2010
|
+
return e2.message ?? e2._tag ?? e2
|
|
2011
|
+
}
|
|
2012
|
+
)
|
|
2013
|
+
})
|
|
1700
2014
|
|
|
1701
2015
|
const defaultFailureMessageHandler = <E, Args extends Array<unknown>, AME, AMR>(
|
|
1702
2016
|
actionMaker:
|
|
@@ -1719,23 +2033,18 @@ const defaultFailureMessageHandler = <E, Args extends Array<unknown>, AME, AMR>(
|
|
|
1719
2033
|
error: "" // TODO consider again Cause.pretty(cause), // will be reported to Sentry/Otel anyway.. and we shouldn't bother users with error dumps?
|
|
1720
2034
|
}
|
|
1721
2035
|
),
|
|
1722
|
-
onSome: (e) =>
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
{ id: "handle.with_warnings" },
|
|
1728
|
-
{ action }
|
|
1729
|
-
) + e.message
|
|
1730
|
-
? "\n" + e.message
|
|
1731
|
-
: ""
|
|
1732
|
-
}
|
|
1733
|
-
: `${
|
|
2036
|
+
onSome: (e) => {
|
|
2037
|
+
const rendered = renderError(action, errorRenderer)(e, ...args)
|
|
2038
|
+
return {
|
|
2039
|
+
level: "warn" as const,
|
|
2040
|
+
message: `${
|
|
1734
2041
|
intl.formatMessage(
|
|
1735
2042
|
{ id: "handle.with_errors" },
|
|
1736
2043
|
{ action }
|
|
1737
2044
|
)
|
|
1738
|
-
}:\n` +
|
|
2045
|
+
}:\n` + rendered
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
1739
2048
|
})
|
|
1740
2049
|
})
|
|
1741
2050
|
|
|
@@ -1745,9 +2054,69 @@ export const CommanderStatic = {
|
|
|
1745
2054
|
) =>
|
|
1746
2055
|
(self: In, arg: Arg, arg2: Arg2) => cb(arg, arg2)(self),
|
|
1747
2056
|
|
|
2057
|
+
/**
|
|
2058
|
+
* Stream pipe operator that maps each emitted value to a `Progress` entry and updates the
|
|
2059
|
+
* command's reactive `progress` ref via the `CommandProgress` service.
|
|
2060
|
+
*
|
|
2061
|
+
* The mapper receives an `AsyncResult<A, E>` (each emitted value wrapped as
|
|
2062
|
+
* `AsyncResult.success(value, { waiting: true })`), matching the same shape used by
|
|
2063
|
+
* `CommandButton`'s `:progress-map` prop.
|
|
2064
|
+
*
|
|
2065
|
+
* Designed to be used inside a `streamFn` handler (either directly with `.pipe()`, or as
|
|
2066
|
+
* a combinator argument):
|
|
2067
|
+
*
|
|
2068
|
+
* @example
|
|
2069
|
+
* ```ts
|
|
2070
|
+
* // Inside the handler body:
|
|
2071
|
+
* Command.streamFn("exportData")(function*(arg, ctx) {
|
|
2072
|
+
* return makeExportStream(arg.id).pipe(
|
|
2073
|
+
* Command.mapProgress((r) =>
|
|
2074
|
+
* AsyncResult.isSuccess(r) && r.value._tag === "OperationProgress"
|
|
2075
|
+
* ? { text: `${r.value.completed}/${r.value.total}`, percentage: r.value.completed / r.value.total * 100 }
|
|
2076
|
+
* : undefined
|
|
2077
|
+
* )
|
|
2078
|
+
* )
|
|
2079
|
+
* })
|
|
2080
|
+
*
|
|
2081
|
+
* // Or as a stream combinator argument:
|
|
2082
|
+
* Command.streamFn("exportData")(
|
|
2083
|
+
* function*(arg, ctx) { return makeExportStream(arg.id) },
|
|
2084
|
+
* (s) => s.pipe(Command.mapProgress((r) => AsyncResult.isSuccess(r) && r.value._tag === "OperationProgress" ? { text: `${r.value.completed}/${r.value.total}` } : undefined))
|
|
2085
|
+
* )
|
|
2086
|
+
* ```
|
|
2087
|
+
*/
|
|
2088
|
+
mapProgress:
|
|
2089
|
+
<A, E>(fn: (result: AsyncResult.AsyncResult<A, E>) => Progress | undefined) =>
|
|
2090
|
+
<R>(stream: Stream.Stream<A, E, R>): Stream.Stream<A, E, R> =>
|
|
2091
|
+
stream.pipe(
|
|
2092
|
+
Stream.tap((v) => {
|
|
2093
|
+
const p = fn(AsyncResult.success(v, { waiting: true }))
|
|
2094
|
+
return p !== undefined ? CommandProgress.use((s) => s.update(p)) : Effect.void
|
|
2095
|
+
})
|
|
2096
|
+
),
|
|
2097
|
+
|
|
2098
|
+
/**
|
|
2099
|
+
* Imperatively push a progress update from inside a `streamFn` handler.
|
|
2100
|
+
* Requires `CommandProgress` to be in context — provided automatically for all `streamFn` streams.
|
|
2101
|
+
*
|
|
2102
|
+
* @example
|
|
2103
|
+
* ```ts
|
|
2104
|
+
* // In a streamFn handler:
|
|
2105
|
+
* stream.pipe(
|
|
2106
|
+
* Stream.tap((event) =>
|
|
2107
|
+
* event._tag === "OperationProgress"
|
|
2108
|
+
* ? Command.updateProgress({ text: `${event.completed}/${event.total}`, percentage: event.completed / event.total * 100 })
|
|
2109
|
+
* : Effect.void
|
|
2110
|
+
* )
|
|
2111
|
+
* )
|
|
2112
|
+
* ```
|
|
2113
|
+
*/
|
|
2114
|
+
updateProgress: (progress: Progress | undefined): Effect.Effect<void> =>
|
|
2115
|
+
CommandProgress.use((s) => s.update(progress)),
|
|
2116
|
+
|
|
1748
2117
|
/** Version of @see confirmOrInterrupt that automatically includes the action name in the default messages */
|
|
1749
2118
|
confirmOrInterrupt: Effect.fnUntraced(function*(
|
|
1750
|
-
message
|
|
2119
|
+
message?: string
|
|
1751
2120
|
) {
|
|
1752
2121
|
const context = yield* CommandContext
|
|
1753
2122
|
const { intl } = yield* I18n
|
|
@@ -1762,7 +2131,7 @@ export const CommanderStatic = {
|
|
|
1762
2131
|
}),
|
|
1763
2132
|
/** Version of @see confirm that automatically includes the action name in the default messages */
|
|
1764
2133
|
confirm: Effect.fnUntraced(function*(
|
|
1765
|
-
message
|
|
2134
|
+
message?: string
|
|
1766
2135
|
) {
|
|
1767
2136
|
const context = yield* CommandContext
|
|
1768
2137
|
const { intl } = yield* I18n
|
|
@@ -1782,6 +2151,17 @@ export const CommanderStatic = {
|
|
|
1782
2151
|
CommandContext,
|
|
1783
2152
|
(c) => ({ ...c, action: update(c.action, ...input) })
|
|
1784
2153
|
),
|
|
2154
|
+
registerErrorRenderer: <A>(
|
|
2155
|
+
guard: Predicate.Refinement<unknown, A>,
|
|
2156
|
+
render: (guarded: A) => string | undefined
|
|
2157
|
+
) =>
|
|
2158
|
+
Layer.effect(
|
|
2159
|
+
CommanderErrorRenderers,
|
|
2160
|
+
Effect.gen(function*() {
|
|
2161
|
+
const current = yield* CommanderErrorRenderers
|
|
2162
|
+
return [...current, makeRegisteredErrorRenderer(guard, render)]
|
|
2163
|
+
})
|
|
2164
|
+
),
|
|
1785
2165
|
defaultFailureMessageHandler,
|
|
1786
2166
|
renderError: renderErrorMaker,
|
|
1787
2167
|
/**
|
|
@@ -1806,6 +2186,7 @@ export const CommanderStatic = {
|
|
|
1806
2186
|
| string
|
|
1807
2187
|
| ((id: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => true | string | undefined)
|
|
1808
2188
|
errorRenderer?: (e: E, action: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => string | undefined
|
|
2189
|
+
showSpanInfo?: false
|
|
1809
2190
|
onWaiting?:
|
|
1810
2191
|
| null
|
|
1811
2192
|
| undefined
|
|
@@ -1818,11 +2199,10 @@ export const CommanderStatic = {
|
|
|
1818
2199
|
| ((a: A, action: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => string | null | undefined)
|
|
1819
2200
|
}
|
|
1820
2201
|
) =>
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
Effect.gen(function*() {
|
|
2202
|
+
Effect.fnUntraced(function*(
|
|
2203
|
+
self: Effect.Effect<A, E, R>,
|
|
2204
|
+
...args: Args
|
|
2205
|
+
) {
|
|
1826
2206
|
const cc = yield* CommandContext
|
|
1827
2207
|
const { intl } = yield* I18n
|
|
1828
2208
|
const withToast = yield* WithToast
|
|
@@ -1859,23 +2239,216 @@ export const CommanderStatic = {
|
|
|
1859
2239
|
),
|
|
1860
2240
|
onSuccess: options?.onSuccess === null
|
|
1861
2241
|
? null
|
|
1862
|
-
: (
|
|
2242
|
+
: (_a, ..._args) =>
|
|
1863
2243
|
hasCustomSuccess
|
|
1864
2244
|
? intl.formatMessage(
|
|
1865
2245
|
{ id: customSuccess },
|
|
1866
2246
|
cc.state
|
|
1867
2247
|
)
|
|
1868
|
-
:
|
|
1869
|
-
+ (S.is(OperationSuccess)(a) && a.message ? "\n" + a.message : "")),
|
|
2248
|
+
: intl.formatMessage({ id: "handle.success" }, { action: cc.action }),
|
|
1870
2249
|
onFailure: defaultFailureMessageHandler(
|
|
1871
2250
|
hasCustomFailure ? intl.formatMessage({ id: customFailure }, cc.state) : cc.action,
|
|
1872
2251
|
options?.errorRenderer as ErrorRenderer<E, Args> | undefined
|
|
1873
2252
|
),
|
|
1874
|
-
stableToastId
|
|
2253
|
+
stableToastId,
|
|
2254
|
+
groupId: cc.id,
|
|
2255
|
+
...options?.showSpanInfo === false ? { showSpanInfo: options.showSpanInfo } : {}
|
|
1875
2256
|
})(_, ...args)
|
|
1876
2257
|
)
|
|
1877
2258
|
}),
|
|
1878
2259
|
|
|
2260
|
+
/**
|
|
2261
|
+
* Stream-aware version of `withDefaultToast`. Use this as a combinator inside `streamFn`
|
|
2262
|
+
* (or anywhere a `Stream` needs toast lifecycle handling) instead of `withDefaultToast`.
|
|
2263
|
+
*
|
|
2264
|
+
* Unlike `withDefaultToast` (which only wraps the initial `Effect`), this combinator:
|
|
2265
|
+
* - Shows the "waiting" toast **before** the stream starts
|
|
2266
|
+
* - Updates the waiting toast with progress text when `progress` is set and a new element arrives
|
|
2267
|
+
* - Shows the "success" toast only **after** the stream drains fully without error
|
|
2268
|
+
* - Shows the "failure" toast if the stream errors or fails
|
|
2269
|
+
*
|
|
2270
|
+
* Accepts either a `Stream<A, E, R>` or an `Effect<Stream<A, E, R>, EE, ER>` as input,
|
|
2271
|
+
* so it works in both the `NonGenStream` and `StreamGen` overloads of `streamFn`.
|
|
2272
|
+
*
|
|
2273
|
+
* @example
|
|
2274
|
+
* ```ts
|
|
2275
|
+
* Command.streamFn("exportData")(
|
|
2276
|
+
* function*(arg, ctx) { return makeExportStream(arg.id) },
|
|
2277
|
+
* Command.withDefaultToastStream({
|
|
2278
|
+
* progress: (r) =>
|
|
2279
|
+
* AsyncResult.isSuccess(r) && r.value._tag === "OperationProgress"
|
|
2280
|
+
* ? { text: `${r.value.completed}/${r.value.total}`, percentage: r.value.completed / r.value.total * 100 }
|
|
2281
|
+
* : undefined
|
|
2282
|
+
* })
|
|
2283
|
+
* )
|
|
2284
|
+
* ```
|
|
2285
|
+
*/
|
|
2286
|
+
withDefaultToastStream: <A, E, R, Args extends Array<unknown>>(
|
|
2287
|
+
options?: {
|
|
2288
|
+
stableToastId?:
|
|
2289
|
+
| undefined
|
|
2290
|
+
| true
|
|
2291
|
+
| string
|
|
2292
|
+
| ((id: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => true | string | undefined)
|
|
2293
|
+
errorRenderer?: (e: E, action: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => string | undefined
|
|
2294
|
+
showSpanInfo?: false
|
|
2295
|
+
onWaiting?:
|
|
2296
|
+
| null
|
|
2297
|
+
| undefined
|
|
2298
|
+
| string
|
|
2299
|
+
| ((id: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => string | null | undefined)
|
|
2300
|
+
onSuccess?:
|
|
2301
|
+
| null
|
|
2302
|
+
| undefined
|
|
2303
|
+
| string
|
|
2304
|
+
| ((a: A, action: string, arg: NoInfer<Args>[0], ctx: NoInfer<Args>[1]) => string | null | undefined)
|
|
2305
|
+
/** Map each stream element to a progress label. When non-`undefined`, updates both the active waiting toast and the `CommandProgress` service (for CommandButton progress display). */
|
|
2306
|
+
progress?: (result: AsyncResult.AsyncResult<A, E>) => Progress | undefined
|
|
2307
|
+
}
|
|
2308
|
+
) =>
|
|
2309
|
+
(
|
|
2310
|
+
self: Stream.Stream<A, E, R> | Effect.Effect<Stream.Stream<A, E, R>, any, any>,
|
|
2311
|
+
...args: Args
|
|
2312
|
+
): Stream.Stream<A, E, R | I18n | Toast | CommandContext> => {
|
|
2313
|
+
const rawStream: Stream.Stream<A, E, R> = Stream.isStream(self)
|
|
2314
|
+
? self
|
|
2315
|
+
: Stream.unwrap(self)
|
|
2316
|
+
|
|
2317
|
+
return Stream.unwrap(Effect.gen(function*() {
|
|
2318
|
+
const cc = yield* CommandContext
|
|
2319
|
+
const { intl } = yield* I18n
|
|
2320
|
+
const toast = yield* Toast
|
|
2321
|
+
|
|
2322
|
+
const customWaiting = cc.namespaced("waiting")
|
|
2323
|
+
const hasCustomWaiting = !!intl.messages[customWaiting]
|
|
2324
|
+
const customSuccess = cc.namespaced("success")
|
|
2325
|
+
const hasCustomSuccess = !!intl.messages[customSuccess]
|
|
2326
|
+
const customFailure = cc.namespaced("failure")
|
|
2327
|
+
const hasCustomFailure = !!intl.messages[customFailure]
|
|
2328
|
+
|
|
2329
|
+
const stableToastId: string | undefined = options?.stableToastId
|
|
2330
|
+
? typeof options.stableToastId === "string"
|
|
2331
|
+
? options.stableToastId
|
|
2332
|
+
: typeof options.stableToastId === "boolean"
|
|
2333
|
+
? cc.id
|
|
2334
|
+
: typeof options.stableToastId === "function"
|
|
2335
|
+
? (() => {
|
|
2336
|
+
const r = (options.stableToastId as (...a: any[]) => true | string | undefined)(cc.id, ...args)
|
|
2337
|
+
if (typeof r === "string") return r
|
|
2338
|
+
if (r === true) return cc.id
|
|
2339
|
+
return undefined
|
|
2340
|
+
})()
|
|
2341
|
+
: undefined
|
|
2342
|
+
: undefined
|
|
2343
|
+
|
|
2344
|
+
const baseTimeout = 3_000
|
|
2345
|
+
|
|
2346
|
+
const waitingMsg: string | null = options?.onWaiting === null
|
|
2347
|
+
? null
|
|
2348
|
+
: typeof options?.onWaiting === "string"
|
|
2349
|
+
? options.onWaiting
|
|
2350
|
+
: typeof options?.onWaiting === "function"
|
|
2351
|
+
? (options.onWaiting as (...a: any[]) => string | null | undefined)(cc.id, ...args) ?? null
|
|
2352
|
+
: hasCustomWaiting
|
|
2353
|
+
? intl.formatMessage({ id: customWaiting }, cc.state)
|
|
2354
|
+
: intl.formatMessage({ id: "handle.waiting" }, { action: cc.action })
|
|
2355
|
+
|
|
2356
|
+
const toastId: string | number | undefined = waitingMsg === null
|
|
2357
|
+
? stableToastId
|
|
2358
|
+
: stableToastId ?? `wait-${Math.random().toString(36).slice(2)}`
|
|
2359
|
+
|
|
2360
|
+
const requestId: string = yield* Effect.currentSpan.pipe(
|
|
2361
|
+
Effect.map((span) => span.traceId),
|
|
2362
|
+
Effect.orElseSucceed(() => S.StringId.make())
|
|
2363
|
+
)
|
|
2364
|
+
const meta = { groupId: cc.id, requestId }
|
|
2365
|
+
|
|
2366
|
+
if (waitingMsg !== null) {
|
|
2367
|
+
yield* toast.info(waitingMsg, { id: toastId!, timeout: Infinity, ...meta })
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
const failureHandler = defaultFailureMessageHandler<E, [], never, never>(
|
|
2371
|
+
hasCustomFailure ? intl.formatMessage({ id: customFailure }, cc.state) : cc.action,
|
|
2372
|
+
options?.errorRenderer as ErrorRenderer<E, []> | undefined
|
|
2373
|
+
)
|
|
2374
|
+
|
|
2375
|
+
let lastValue: A | undefined = undefined
|
|
2376
|
+
let didFail = false
|
|
2377
|
+
|
|
2378
|
+
const composed = rawStream.pipe(
|
|
2379
|
+
Stream.tap((v) =>
|
|
2380
|
+
Effect.gen(function*() {
|
|
2381
|
+
lastValue = v
|
|
2382
|
+
if (options?.progress !== undefined) {
|
|
2383
|
+
const p = options.progress(AsyncResult.success(v, { waiting: true }))
|
|
2384
|
+
if (p !== undefined) {
|
|
2385
|
+
// Update CommandProgress so CommandButton progress indicator is also driven
|
|
2386
|
+
yield* CommandProgress.use((s) => s.update(p))
|
|
2387
|
+
if (toastId !== undefined) {
|
|
2388
|
+
const progressText = typeof p === "string" ? p : p.text
|
|
2389
|
+
const msg = waitingMsg ? `${waitingMsg}\n${progressText}` : progressText
|
|
2390
|
+
yield* toast.info(msg, { id: toastId, timeout: Infinity, ...meta })
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
})
|
|
2395
|
+
),
|
|
2396
|
+
Stream.tapCause(Effect.fnUntraced(function*(cause) {
|
|
2397
|
+
didFail = true
|
|
2398
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
2399
|
+
if (toastId !== undefined) yield* toast.dismiss(toastId)
|
|
2400
|
+
return
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
const spanInfo = options?.showSpanInfo !== false
|
|
2404
|
+
? yield* Effect.currentSpan.pipe(
|
|
2405
|
+
Effect.map((span) => `\nTrace: ${span.traceId}\nSpan: ${span.spanId}`),
|
|
2406
|
+
Effect.orElseSucceed(() => "")
|
|
2407
|
+
)
|
|
2408
|
+
: ""
|
|
2409
|
+
|
|
2410
|
+
const t = yield* failureHandler(Cause.findErrorOption(cause))
|
|
2411
|
+
const opts = { timeout: baseTimeout * 2, ...meta }
|
|
2412
|
+
|
|
2413
|
+
if (typeof t === "object") {
|
|
2414
|
+
const message = t.message + spanInfo
|
|
2415
|
+
yield* t.level === "warn"
|
|
2416
|
+
? toast.warning(message, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
2417
|
+
: toast.error(message, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
2418
|
+
} else {
|
|
2419
|
+
yield* toast.error(t + spanInfo, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
2420
|
+
}
|
|
2421
|
+
}, Effect.uninterruptible)),
|
|
2422
|
+
Stream.ensuring(Effect.suspend(() => {
|
|
2423
|
+
if (didFail) return Effect.void
|
|
2424
|
+
|
|
2425
|
+
if (options?.onSuccess === null) return Effect.void
|
|
2426
|
+
|
|
2427
|
+
const successMsg: string | null = typeof options?.onSuccess === "string"
|
|
2428
|
+
? options.onSuccess
|
|
2429
|
+
: typeof options?.onSuccess === "function"
|
|
2430
|
+
? (options.onSuccess as (...a: any[]) => string | null | undefined)(lastValue, cc.action, ...args) ?? null
|
|
2431
|
+
: hasCustomSuccess
|
|
2432
|
+
? intl.formatMessage({ id: customSuccess }, cc.state)
|
|
2433
|
+
: intl.formatMessage({ id: "handle.success" }, { action: cc.action })
|
|
2434
|
+
|
|
2435
|
+
if (successMsg === null) return Effect.void
|
|
2436
|
+
|
|
2437
|
+
return toast.success(
|
|
2438
|
+
successMsg,
|
|
2439
|
+
toastId !== undefined
|
|
2440
|
+
? { id: toastId, timeout: baseTimeout, ...meta }
|
|
2441
|
+
: { timeout: baseTimeout, ...meta }
|
|
2442
|
+
)
|
|
2443
|
+
}))
|
|
2444
|
+
)
|
|
2445
|
+
|
|
2446
|
+
return (toastId !== undefined
|
|
2447
|
+
? composed.pipe(Stream.provideService(CurrentToastId, CurrentToastId.of({ toastId })))
|
|
2448
|
+
: composed) as unknown as Stream.Stream<A, E, R>
|
|
2449
|
+
}))
|
|
2450
|
+
},
|
|
2451
|
+
|
|
1879
2452
|
/** borrowing the idea from Families in Effect Atom */
|
|
1880
2453
|
family: <T extends object, Arg, ArgIn = Arg>(
|
|
1881
2454
|
maker: (arg: Arg) => T,
|
|
@@ -1936,7 +2509,11 @@ const unregisterWait = (id: string) => {
|
|
|
1936
2509
|
}
|
|
1937
2510
|
}
|
|
1938
2511
|
|
|
1939
|
-
const getStateValues = <
|
|
2512
|
+
const getStateValues = <
|
|
2513
|
+
const Id extends string,
|
|
2514
|
+
const I18nKey extends string,
|
|
2515
|
+
State extends IntlRecord | undefined
|
|
2516
|
+
>(
|
|
1940
2517
|
options?: FnOptions<Id, I18nKey, State>
|
|
1941
2518
|
): ComputedRef<State> => {
|
|
1942
2519
|
const state_ = options?.state
|
|
@@ -1948,11 +2525,18 @@ const getStateValues = <const Id extends string, const I18nKey extends string, S
|
|
|
1948
2525
|
|
|
1949
2526
|
// class preserves JSDoc throughout..
|
|
1950
2527
|
export class CommanderImpl<RT, RTHooks> {
|
|
2528
|
+
private readonly rt: Context.Context<RT>
|
|
2529
|
+
private readonly intl: I18n
|
|
2530
|
+
private readonly hooks: Layer.Layer<RTHooks, never, RT>
|
|
2531
|
+
|
|
1951
2532
|
constructor(
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2533
|
+
rt: Context.Context<RT>,
|
|
2534
|
+
intl: I18n,
|
|
2535
|
+
hooks: Layer.Layer<RTHooks, never, RT>
|
|
1955
2536
|
) {
|
|
2537
|
+
this.rt = rt
|
|
2538
|
+
this.intl = intl
|
|
2539
|
+
this.hooks = hooks
|
|
1956
2540
|
}
|
|
1957
2541
|
|
|
1958
2542
|
readonly makeContext = <const Id extends string, const I18nKey extends string = Id>(
|
|
@@ -2109,6 +2693,7 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2109
2693
|
const runFork = Effect.runForkWith(rt)
|
|
2110
2694
|
|
|
2111
2695
|
const handle = Object.assign((arg: Arg) => {
|
|
2696
|
+
arg = toRaw(arg) // remove outside vue proxy bs
|
|
2112
2697
|
// we capture the call site stack here
|
|
2113
2698
|
const limit = Error.stackTraceLimit
|
|
2114
2699
|
Error.stackTraceLimit = 2
|
|
@@ -2139,15 +2724,17 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2139
2724
|
}
|
|
2140
2725
|
}
|
|
2141
2726
|
|
|
2142
|
-
const command = currentState.pipe(Effect.flatMap((state) =>
|
|
2143
|
-
|
|
2727
|
+
const command = currentState.pipe(Effect.flatMap((state) => {
|
|
2728
|
+
const rawArg = deepToRaw(arg)
|
|
2729
|
+
const rawState = deepToRaw(state)
|
|
2730
|
+
return Effect.withSpan(
|
|
2144
2731
|
exec(arg, { ...context.value, state } as any),
|
|
2145
2732
|
id,
|
|
2146
2733
|
{
|
|
2147
2734
|
captureStackTrace,
|
|
2148
2735
|
attributes: {
|
|
2149
|
-
input:
|
|
2150
|
-
state,
|
|
2736
|
+
input: rawArg,
|
|
2737
|
+
state: rawState,
|
|
2151
2738
|
action: initialContext.action,
|
|
2152
2739
|
label: initialContext.label,
|
|
2153
2740
|
id: initialContext.id,
|
|
@@ -2155,7 +2742,7 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2155
2742
|
}
|
|
2156
2743
|
}
|
|
2157
2744
|
)
|
|
2158
|
-
))
|
|
2745
|
+
}))
|
|
2159
2746
|
|
|
2160
2747
|
return runFork(command)
|
|
2161
2748
|
}, { action, label })
|
|
@@ -2174,6 +2761,8 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2174
2761
|
|
|
2175
2762
|
/** reactive */
|
|
2176
2763
|
result,
|
|
2764
|
+
/** always undefined for non-stream commands */
|
|
2765
|
+
progress: undefined,
|
|
2177
2766
|
/** reactive */
|
|
2178
2767
|
waiting,
|
|
2179
2768
|
/** reactive */
|
|
@@ -2281,8 +2870,9 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2281
2870
|
options?: FnOptions<Id, I18nKey, State>
|
|
2282
2871
|
): Commander.Gen<RT | RTHooks, Id, I18nKey, State> & Commander.NonGen<RT | RTHooks, Id, I18nKey, State> & {
|
|
2283
2872
|
state: Context.Service<`Commander.Command.${Id}.state`, State>
|
|
2284
|
-
} =>
|
|
2285
|
-
|
|
2873
|
+
} => {
|
|
2874
|
+
const resolvedId: Id = typeof id === "string" ? id : id.id
|
|
2875
|
+
return Object.assign(
|
|
2286
2876
|
(
|
|
2287
2877
|
fn: any,
|
|
2288
2878
|
...combinators: any[]
|
|
@@ -2293,7 +2883,7 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2293
2883
|
const errorDef = new Error()
|
|
2294
2884
|
Error.stackTraceLimit = limit
|
|
2295
2885
|
|
|
2296
|
-
return this.makeCommand(
|
|
2886
|
+
return this.makeCommand(resolvedId, options, errorDef)(
|
|
2297
2887
|
Effect.fnUntraced(
|
|
2298
2888
|
// fnUntraced only supports generators as first arg, so we convert to generator if needed
|
|
2299
2889
|
isGeneratorFunction(fn) ? fn : function*(...args) {
|
|
@@ -2303,15 +2893,329 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2303
2893
|
) as any
|
|
2304
2894
|
)
|
|
2305
2895
|
},
|
|
2306
|
-
makeBaseInfo(
|
|
2896
|
+
makeBaseInfo(resolvedId, options),
|
|
2897
|
+
{
|
|
2898
|
+
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
2899
|
+
`Commander.Command.${resolvedId}.state`
|
|
2900
|
+
)
|
|
2901
|
+
}
|
|
2902
|
+
)
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
/**
|
|
2906
|
+
* Internal factory for stream-backed commands. Accepts a handler that returns a `Stream` directly.
|
|
2907
|
+
* Services (`CommandContext`, `stateTag`) are provided to the stream via `Stream.provideServiceEffect`.
|
|
2908
|
+
*/
|
|
2909
|
+
readonly makeStreamCommand = <
|
|
2910
|
+
const Id extends string,
|
|
2911
|
+
const State extends IntlRecord | undefined,
|
|
2912
|
+
const I18nKey extends string = Id
|
|
2913
|
+
>(
|
|
2914
|
+
id_: Id | { id: Id },
|
|
2915
|
+
options?: FnOptions<Id, I18nKey, State>,
|
|
2916
|
+
errorDef?: Error
|
|
2917
|
+
) => {
|
|
2918
|
+
const id = typeof id_ === "string" ? id_ : id_.id
|
|
2919
|
+
const state = getStateValues(options)
|
|
2920
|
+
|
|
2921
|
+
return Object.assign(
|
|
2922
|
+
<Arg, SA, SE, SR>(
|
|
2923
|
+
handler: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>
|
|
2924
|
+
) => {
|
|
2925
|
+
const limit = Error.stackTraceLimit
|
|
2926
|
+
Error.stackTraceLimit = 2
|
|
2927
|
+
const localErrorDef = new Error()
|
|
2928
|
+
Error.stackTraceLimit = limit
|
|
2929
|
+
if (!errorDef) {
|
|
2930
|
+
errorDef = localErrorDef
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
const key = `Commander.Command.${id}.state` as const
|
|
2934
|
+
const stateTag = Context.Service<typeof key, State>(key)
|
|
2935
|
+
|
|
2936
|
+
const makeContext_ = () => this.makeContext(id, { ...options, state: state?.value })
|
|
2937
|
+
const initialContext = makeContext_()
|
|
2938
|
+
const context = computed(() => makeContext_())
|
|
2939
|
+
const action = computed(() => context.value.action)
|
|
2940
|
+
const label = computed(() => context.value.label)
|
|
2941
|
+
|
|
2942
|
+
const currentState = Effect.sync(() => state.value)
|
|
2943
|
+
|
|
2944
|
+
// Reactive ref driven by the CommandProgress service — updated imperatively
|
|
2945
|
+
// from inside the stream via `Command.mapProgress(fn)` or `Command.updateProgress(p)`.
|
|
2946
|
+
const progressRef = ref<Progress | undefined>(undefined)
|
|
2947
|
+
const commandProgressService = {
|
|
2948
|
+
update: (p: Progress | undefined) =>
|
|
2949
|
+
Effect.sync(() => {
|
|
2950
|
+
progressRef.value = p
|
|
2951
|
+
})
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
const streamErrorReporter = <A, E, R>(self: Stream.Stream<A, E, R>) =>
|
|
2955
|
+
self.pipe(
|
|
2956
|
+
Stream.tapCause(
|
|
2957
|
+
Effect.fnUntraced(function*(cause) {
|
|
2958
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
2959
|
+
console.info(`Interrupted while trying to ${id}`)
|
|
2960
|
+
return
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
const fail = Cause.findErrorOption(cause)
|
|
2964
|
+
if (Option.isSome(fail)) {
|
|
2965
|
+
const message = `Failure trying to ${id}`
|
|
2966
|
+
yield* reportMessage(message, {
|
|
2967
|
+
action: id,
|
|
2968
|
+
error: fail.value
|
|
2969
|
+
})
|
|
2970
|
+
return
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
const ctx = yield* CommandContext
|
|
2974
|
+
const extra = {
|
|
2975
|
+
action: ctx.action,
|
|
2976
|
+
message: `Unexpected Error trying to ${id}`
|
|
2977
|
+
}
|
|
2978
|
+
yield* reportRuntimeError(cause, extra)
|
|
2979
|
+
}, Effect.uninterruptible)
|
|
2980
|
+
)
|
|
2981
|
+
)
|
|
2982
|
+
|
|
2983
|
+
const theStreamHandler = (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) =>
|
|
2984
|
+
handler(arg, ctx).pipe(
|
|
2985
|
+
streamErrorReporter,
|
|
2986
|
+
Stream.provideService(CommandProgress, commandProgressService),
|
|
2987
|
+
Stream.provideServiceEffect(stateTag, currentState),
|
|
2988
|
+
Stream.provideServiceEffect(CommandContext, Effect.sync(() => makeContext_()))
|
|
2989
|
+
)
|
|
2990
|
+
|
|
2991
|
+
const waitId = options?.waitKey ? options.waitKey(id) : undefined
|
|
2992
|
+
const blockId = options?.blockKey ? options.blockKey(id) : undefined
|
|
2993
|
+
|
|
2994
|
+
const [result, exec_] = asStreamResult(theStreamHandler)
|
|
2995
|
+
|
|
2996
|
+
const exec = Effect
|
|
2997
|
+
.fnUntraced(
|
|
2998
|
+
function*(...args: [any, any]) {
|
|
2999
|
+
if (waitId !== undefined) registerWait(waitId)
|
|
3000
|
+
if (blockId !== undefined && blockId !== waitId) {
|
|
3001
|
+
registerWait(blockId)
|
|
3002
|
+
}
|
|
3003
|
+
return yield* exec_(...args)
|
|
3004
|
+
},
|
|
3005
|
+
Effect.onExit(() =>
|
|
3006
|
+
Effect.sync(() => {
|
|
3007
|
+
if (waitId !== undefined) unregisterWait(waitId)
|
|
3008
|
+
if (blockId !== undefined && blockId !== waitId) {
|
|
3009
|
+
unregisterWait(blockId)
|
|
3010
|
+
}
|
|
3011
|
+
})
|
|
3012
|
+
)
|
|
3013
|
+
)
|
|
3014
|
+
|
|
3015
|
+
const waiting = waitId !== undefined
|
|
3016
|
+
? computed(() => result.value.waiting || (waitState.value[waitId] ?? 0) > 0)
|
|
3017
|
+
: computed(() => result.value.waiting)
|
|
3018
|
+
|
|
3019
|
+
const blocked = blockId !== undefined
|
|
3020
|
+
? computed(() => waiting.value || (waitState.value[blockId] ?? 0) > 0)
|
|
3021
|
+
: computed(() => waiting.value)
|
|
3022
|
+
|
|
3023
|
+
const computeAllowed = options?.allowed
|
|
3024
|
+
const allowed = computeAllowed ? computed(() => computeAllowed(id, state)) : true
|
|
3025
|
+
|
|
3026
|
+
const rt = Effect.context<RT | RTHooks>().pipe(Effect.provide(this.hooks)).pipe(Effect.runSyncWith(this.rt))
|
|
3027
|
+
const runFork = Effect.runForkWith(rt)
|
|
3028
|
+
|
|
3029
|
+
const progress = progressRef
|
|
3030
|
+
|
|
3031
|
+
const handle = Object.assign((arg: Arg) => {
|
|
3032
|
+
arg = toRaw(arg)
|
|
3033
|
+
progressRef.value = undefined // reset progress on new invocation
|
|
3034
|
+
const limit = Error.stackTraceLimit
|
|
3035
|
+
Error.stackTraceLimit = 2
|
|
3036
|
+
const errorCall = new Error()
|
|
3037
|
+
Error.stackTraceLimit = limit
|
|
3038
|
+
|
|
3039
|
+
let cache: false | string = false
|
|
3040
|
+
const captureStackTrace = () => {
|
|
3041
|
+
if (cache !== false) {
|
|
3042
|
+
return cache
|
|
3043
|
+
}
|
|
3044
|
+
if (errorCall.stack) {
|
|
3045
|
+
const stackDef = errorDef!.stack!.trim().split("\n")
|
|
3046
|
+
const stackCall = errorCall.stack.trim().split("\n")
|
|
3047
|
+
let endStackDef = stackDef.slice(2).join("\n").trim()
|
|
3048
|
+
if (!endStackDef.includes(`(`)) {
|
|
3049
|
+
endStackDef = endStackDef.replace(/at (.*)/, "at ($1)")
|
|
3050
|
+
}
|
|
3051
|
+
let endStackCall = stackCall.slice(2).join("\n").trim()
|
|
3052
|
+
if (!endStackCall.includes(`(`)) {
|
|
3053
|
+
endStackCall = endStackCall.replace(/at (.*)/, "at ($1)")
|
|
3054
|
+
}
|
|
3055
|
+
cache = `${endStackDef}\n${endStackCall}`
|
|
3056
|
+
return cache
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
const command = currentState.pipe(Effect.flatMap((state) => {
|
|
3061
|
+
const rawArg = deepToRaw(arg)
|
|
3062
|
+
const rawState = deepToRaw(state)
|
|
3063
|
+
return Effect.withSpan(
|
|
3064
|
+
exec(arg, { ...context.value, state } as any),
|
|
3065
|
+
id,
|
|
3066
|
+
{
|
|
3067
|
+
captureStackTrace,
|
|
3068
|
+
attributes: {
|
|
3069
|
+
input: rawArg,
|
|
3070
|
+
state: rawState,
|
|
3071
|
+
action: initialContext.action,
|
|
3072
|
+
label: initialContext.label,
|
|
3073
|
+
id: initialContext.id,
|
|
3074
|
+
i18nKey: initialContext.i18nKey
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
)
|
|
3078
|
+
}))
|
|
3079
|
+
|
|
3080
|
+
return runFork(command as any)
|
|
3081
|
+
}, { action, label })
|
|
3082
|
+
|
|
3083
|
+
return reactive({
|
|
3084
|
+
id,
|
|
3085
|
+
i18nKey: initialContext.i18nKey,
|
|
3086
|
+
namespace: initialContext.namespace,
|
|
3087
|
+
namespaced: initialContext.namespaced,
|
|
3088
|
+
result,
|
|
3089
|
+
/** reactive – progress driven by `Command.mapProgress` or `Command.updateProgress` inside the stream */
|
|
3090
|
+
progress,
|
|
3091
|
+
waiting,
|
|
3092
|
+
blocked,
|
|
3093
|
+
allowed,
|
|
3094
|
+
action,
|
|
3095
|
+
label,
|
|
3096
|
+
state,
|
|
3097
|
+
handle
|
|
3098
|
+
})
|
|
3099
|
+
},
|
|
3100
|
+
{ id }
|
|
3101
|
+
)
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
/**
|
|
3105
|
+
* Define a stream-backed Command for handling user actions.
|
|
3106
|
+
*
|
|
3107
|
+
* Like `fn`, but the body generator (or function) must **return** a `Stream` rather than
|
|
3108
|
+
* an `Effect`. The command's `waiting` state stays `true` while the stream is running and
|
|
3109
|
+
* is set to `false` once it terminates. The reactive `result` ref is updated for every
|
|
3110
|
+
* value emitted by the stream.
|
|
3111
|
+
*
|
|
3112
|
+
* Three handler shapes are accepted:
|
|
3113
|
+
* 1. **Generator returning a Stream** (primary) — may yield Effects freely before returning the stream:
|
|
3114
|
+
* ```ts
|
|
3115
|
+
* Command.streamFn("exportData")(
|
|
3116
|
+
* function*(arg, ctx) {
|
|
3117
|
+
* const token = yield* getAuthToken
|
|
3118
|
+
* return Stream.fromEffect(startExport(token, arg.id)).pipe(
|
|
3119
|
+
* Stream.flatMap((job) => pollProgress(job.id))
|
|
3120
|
+
* )
|
|
3121
|
+
* }
|
|
3122
|
+
* )
|
|
3123
|
+
* ```
|
|
3124
|
+
* 2. **Function returning a Stream directly**: `(arg, ctx) => Stream.make(1, 2, 3)`
|
|
3125
|
+
* 3. **Function returning `Effect<Stream>`**: `(arg, ctx) => Effect.map(setup, (s) => s.stream)`
|
|
3126
|
+
*
|
|
3127
|
+
* @param id The internal identifier for the action (used for tracing and i18n lookup).
|
|
3128
|
+
* @param options Same options as `fn` (`state`, `blockKey`, `waitKey`, `allowed`, `i18nCustomKey`).
|
|
3129
|
+
*
|
|
3130
|
+
* **Progress** — use `Command.mapProgress(fn)` as a stream pipe operator; the mapper receives
|
|
3131
|
+
* `AsyncResult<A, E>` (each value wrapped as `AsyncResult.success(v, { waiting: true })`),
|
|
3132
|
+
* matching the same shape as CommandButton’s `:progress-map` prop. Or call
|
|
3133
|
+
* `Command.updateProgress(p)` for imperative control:
|
|
3134
|
+
*
|
|
3135
|
+
* ```ts
|
|
3136
|
+
* // mapProgress as a combinator arg (outside the handler):
|
|
3137
|
+
* Command.streamFn("exportData")(
|
|
3138
|
+
* function*(arg, ctx) { return makeExportStream(arg.id) },
|
|
3139
|
+
* (s) => s.pipe(Command.mapProgress((r) => AsyncResult.isSuccess(r) && r.value._tag === "OperationProgress" ? { text: `${r.value.completed}/${r.value.total}` } : undefined))
|
|
3140
|
+
* )
|
|
3141
|
+
*
|
|
3142
|
+
* // Or inline inside the handler body:
|
|
3143
|
+
* Command.streamFn("exportData")(function*(arg, ctx) {
|
|
3144
|
+
* return makeExportStream(arg.id).pipe(Command.mapProgress((r) => AsyncResult.isSuccess(r) ? ... : undefined))
|
|
3145
|
+
* })
|
|
3146
|
+
* ```
|
|
3147
|
+
*
|
|
3148
|
+
* **Pipeable combinators** — the 2nd–Nth args follow the same pattern as `fn`: each combinator
|
|
3149
|
+
* receives `(stream, arg, ctx)` and returns a transformed stream:
|
|
3150
|
+
* ```ts
|
|
3151
|
+
* Command.streamFn("exportData")(
|
|
3152
|
+
* handler,
|
|
3153
|
+
* (s, arg, ctx) => s.pipe(Command.mapProgress(fn), Stream.take(100))
|
|
3154
|
+
* )
|
|
3155
|
+
* ```
|
|
3156
|
+
*
|
|
3157
|
+
* **Returned Properties**: `action`, `label`, `result`, `progress`, `waiting`, `blocked`,
|
|
3158
|
+
* `allowed`, `handle`, `i18nKey`, `namespace`, `namespaced`.
|
|
3159
|
+
*/
|
|
3160
|
+
streamFn = <
|
|
3161
|
+
const Id extends string,
|
|
3162
|
+
const State extends IntlRecord = IntlRecord,
|
|
3163
|
+
const I18nKey extends string = Id
|
|
3164
|
+
>(
|
|
3165
|
+
id: Id | { id: Id },
|
|
3166
|
+
options?: FnOptions<Id, I18nKey, State>
|
|
3167
|
+
):
|
|
3168
|
+
& Commander.StreamGen<RT | RTHooks, Id, I18nKey, State>
|
|
3169
|
+
& Commander.NonGenStream<RT | RTHooks, Id, I18nKey, State>
|
|
3170
|
+
& {
|
|
3171
|
+
state: Context.Service<`Commander.Command.${Id}.state`, State>
|
|
3172
|
+
} =>
|
|
3173
|
+
{
|
|
3174
|
+
const resolvedId = typeof id === "string" ? id : id.id
|
|
3175
|
+
|
|
3176
|
+
type StreamOrEffect = Stream.Stream<any, any, any> | Effect.Effect<Stream.Stream<any, any, any>, any, any>
|
|
3177
|
+
|
|
3178
|
+
const toRawHandler = (fn: any): (arg: any, ctx: any) => StreamOrEffect => {
|
|
3179
|
+
if (isGeneratorFunction(fn)) {
|
|
3180
|
+
return Effect.fnUntraced(function*(arg: any, ctx: any) {
|
|
3181
|
+
return yield* (fn as (arg: any, ctx: any) => Generator<any, Stream.Stream<any, any, any>>)(arg, ctx)
|
|
3182
|
+
})
|
|
3183
|
+
}
|
|
3184
|
+
return fn
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
const toFinalStream = (value: StreamOrEffect): Stream.Stream<any, any, any> =>
|
|
3188
|
+
Stream.isStream(value) ? value : Stream.unwrap(value as Effect.Effect<Stream.Stream<any, any, any>, any, any>)
|
|
3189
|
+
|
|
3190
|
+
return Object.assign(
|
|
3191
|
+
(fn: any, ...combinators: Array<(s: any, arg: any, ctx: any) => any>): any => {
|
|
3192
|
+
const limit = Error.stackTraceLimit
|
|
3193
|
+
Error.stackTraceLimit = 2
|
|
3194
|
+
const errorDef = new Error()
|
|
3195
|
+
Error.stackTraceLimit = limit
|
|
3196
|
+
|
|
3197
|
+
const rawHandler = toRawHandler(fn)
|
|
3198
|
+
const handler = (arg: any, ctx: any) => {
|
|
3199
|
+
let current: any = rawHandler(arg, ctx)
|
|
3200
|
+
for (const combinator of combinators) {
|
|
3201
|
+
current = combinator(current, arg, ctx)
|
|
3202
|
+
}
|
|
3203
|
+
return toFinalStream(current)
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
return this.makeStreamCommand(id, options, errorDef)(handler)
|
|
3207
|
+
},
|
|
3208
|
+
makeBaseInfo(resolvedId, options),
|
|
2307
3209
|
{
|
|
2308
3210
|
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
2309
|
-
`Commander.Command.${
|
|
3211
|
+
`Commander.Command.${resolvedId}.state`
|
|
2310
3212
|
)
|
|
2311
3213
|
}
|
|
2312
3214
|
)
|
|
3215
|
+
}
|
|
2313
3216
|
|
|
2314
3217
|
/** @deprecated */
|
|
3218
|
+
|
|
2315
3219
|
alt2: <
|
|
2316
3220
|
const Id extends string,
|
|
2317
3221
|
MutArg,
|
|
@@ -2403,6 +3307,46 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2403
3307
|
* **User Feedback**: Use the `withDefaultToast` helper for status notifications, or render
|
|
2404
3308
|
* the `result` inline for custom UI feedback.
|
|
2405
3309
|
*/
|
|
3310
|
+
streamWrap = <
|
|
3311
|
+
const Id extends string,
|
|
3312
|
+
Arg,
|
|
3313
|
+
SA,
|
|
3314
|
+
SE,
|
|
3315
|
+
SR,
|
|
3316
|
+
const State extends IntlRecord = IntlRecord,
|
|
3317
|
+
I18nKey extends string = Id
|
|
3318
|
+
>(
|
|
3319
|
+
handler: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>,
|
|
3320
|
+
id: Id,
|
|
3321
|
+
options?: FnOptions<Id, I18nKey, State>
|
|
3322
|
+
): Commander.StreamerWrap<RT | RTHooks, Id, I18nKey, State, Arg, SA, SE, SR> => {
|
|
3323
|
+
return Object.assign(
|
|
3324
|
+
(...combinators: any[]): any => {
|
|
3325
|
+
const limit = Error.stackTraceLimit
|
|
3326
|
+
Error.stackTraceLimit = 2
|
|
3327
|
+
const errorDef = new Error()
|
|
3328
|
+
Error.stackTraceLimit = limit
|
|
3329
|
+
return this.makeStreamCommand(id, options, errorDef)(
|
|
3330
|
+
combinators.length === 0
|
|
3331
|
+
? handler
|
|
3332
|
+
: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => {
|
|
3333
|
+
let current: any = handler(arg, ctx)
|
|
3334
|
+
for (const combinator of combinators) {
|
|
3335
|
+
current = combinator(current, arg, ctx)
|
|
3336
|
+
}
|
|
3337
|
+
return current
|
|
3338
|
+
}
|
|
3339
|
+
)
|
|
3340
|
+
},
|
|
3341
|
+
makeBaseInfo(id, options),
|
|
3342
|
+
{
|
|
3343
|
+
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
3344
|
+
`Commander.Command.${id}.state`
|
|
3345
|
+
)
|
|
3346
|
+
}
|
|
3347
|
+
)
|
|
3348
|
+
}
|
|
3349
|
+
|
|
2406
3350
|
wrap = <
|
|
2407
3351
|
const Id extends string,
|
|
2408
3352
|
Arg,
|
|
@@ -2416,8 +3360,9 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2416
3360
|
| { mutate: (arg: Arg) => Effect.Effect<A, E, R>; id: Id }
|
|
2417
3361
|
| ((arg: Arg) => Effect.Effect<A, E, R>) & { id: Id },
|
|
2418
3362
|
options?: FnOptions<Id, I18nKey, State>
|
|
2419
|
-
): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> =>
|
|
2420
|
-
|
|
3363
|
+
): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
|
|
3364
|
+
const callMutation = mutation
|
|
3365
|
+
return Object.assign(
|
|
2421
3366
|
(
|
|
2422
3367
|
...combinators: any[]
|
|
2423
3368
|
): any => {
|
|
@@ -2426,9 +3371,11 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2426
3371
|
Error.stackTraceLimit = 2
|
|
2427
3372
|
const errorDef = new Error()
|
|
2428
3373
|
Error.stackTraceLimit = limit
|
|
2429
|
-
const mutate = "mutate" in
|
|
3374
|
+
const mutate = "mutate" in callMutation
|
|
3375
|
+
? callMutation.mutate
|
|
3376
|
+
: callMutation
|
|
2430
3377
|
|
|
2431
|
-
return this.makeCommand(
|
|
3378
|
+
return this.makeCommand(callMutation.id, options, errorDef)(
|
|
2432
3379
|
Effect.fnUntraced(
|
|
2433
3380
|
// fnUntraced only supports generators as first arg, so we convert to generator if needed
|
|
2434
3381
|
isGeneratorFunction(mutate) ? mutate : function*(arg: Arg) {
|
|
@@ -2438,13 +3385,14 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2438
3385
|
) as any
|
|
2439
3386
|
)
|
|
2440
3387
|
},
|
|
2441
|
-
makeBaseInfo(
|
|
3388
|
+
makeBaseInfo(callMutation.id, options),
|
|
2442
3389
|
{
|
|
2443
3390
|
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
2444
|
-
`Commander.Command.${
|
|
3391
|
+
`Commander.Command.${callMutation.id}.state`
|
|
2445
3392
|
)
|
|
2446
3393
|
}
|
|
2447
3394
|
)
|
|
3395
|
+
}
|
|
2448
3396
|
}
|
|
2449
3397
|
|
|
2450
3398
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|