@effect-app/vue 4.0.0-beta.187 → 4.0.0-beta.189
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 +22 -0
- package/dist/commander.d.ts +24 -83
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +42 -115
- package/dist/makeClient.d.ts +5 -59
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +4 -59
- package/dist/makeUseCommand.d.ts +1 -1
- package/dist/mutate.d.ts +5 -11
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +5 -53
- package/dist/withToast.d.ts.map +1 -1
- package/dist/withToast.js +9 -4
- package/examples/streamMutation.ts +4 -23
- package/package.json +3 -3
- package/src/commander.ts +167 -230
- package/src/makeClient.ts +6 -167
- package/src/makeUseCommand.ts +1 -1
- package/src/mutate.ts +4 -80
- package/src/withToast.ts +14 -5
- package/test/Mutation.test.ts +0 -26
- package/test/dist/stubs.d.ts +2 -151
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/makeClient.test.ts +2 -24
- package/test/streamFinal.test.ts +2 -49
- package/test/streamFn.test.ts +219 -1
package/src/commander.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { asResult, asStreamResult, deepToRaw, type MissingDependencies, reportRuntimeError } from "@effect-app/vue"
|
|
3
3
|
import { reportMessage } from "@effect-app/vue/errorReporter"
|
|
4
|
-
import { Cause, Context, Effect, type Exit,
|
|
4
|
+
import { Cause, Context, Effect, type Exit, Fiber, flow, Layer, Match, MutableHashMap, Option, Predicate, S } from "effect-app"
|
|
5
5
|
import { SupportedErrors } from "effect-app/client"
|
|
6
|
-
import { OperationFailure, OperationSuccess } from "effect-app/Operations"
|
|
7
6
|
import { isGeneratorFunction, wrapEffect } from "effect-app/utils"
|
|
8
7
|
import { type Refinement } from "effect/Predicate"
|
|
9
8
|
import * as Stream from "effect/Stream"
|
|
@@ -23,41 +22,6 @@ type IntlRecord = Record<string, PrimitiveType | FormatXMLElementFn<string, stri
|
|
|
23
22
|
*/
|
|
24
23
|
export type Progress = string | { readonly text: string; readonly percentage: number }
|
|
25
24
|
|
|
26
|
-
/**
|
|
27
|
-
* Options accepted when calling a stream mutation factory.
|
|
28
|
-
* Supplying `progress` causes the resulting command to expose `running`
|
|
29
|
-
* (the live AsyncResult ref) and `progress` (formatted loading info).
|
|
30
|
-
* When omitted, neither is exposed on the command.
|
|
31
|
-
*/
|
|
32
|
-
export type StreamMutationCallOptions<A, E> = {
|
|
33
|
-
progress?: (result: AsyncResult.AsyncResult<A, E>) => Progress | undefined
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The result of invoking a `mutateToResult` factory: the `execute` function (or
|
|
38
|
-
* `Effect`, when the request takes no input) carries `id`, plus `running` and
|
|
39
|
-
* `progress` when the factory was called with a `progress` formatter. Pass
|
|
40
|
-
* directly to `Command.fn` / `Command.wrap` / `Command.wrapStream`, or invoke
|
|
41
|
-
* to run the stream.
|
|
42
|
-
*/
|
|
43
|
-
type StreamMutationCallable<Id extends string, Arg, A, E, R> =
|
|
44
|
-
& (((arg: Arg) => Effect.Effect<any, E, R>) | Effect.Effect<any, E, R>)
|
|
45
|
-
& {
|
|
46
|
-
readonly id: Id
|
|
47
|
-
readonly _streamCallable: true
|
|
48
|
-
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
49
|
-
readonly progress?: ComputedRef<Progress | undefined>
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type StreamMutationFactory<Id extends string, Arg, A, E, R> =
|
|
53
|
-
& ((options?: StreamMutationCallOptions<A, E>) => StreamMutationCallable<Id, Arg, A, E, R>)
|
|
54
|
-
& { readonly id: Id; readonly _streamFactory: true }
|
|
55
|
-
|
|
56
|
-
const isStreamFactory = (x: unknown): x is StreamMutationFactory<string, any, any, any, any> =>
|
|
57
|
-
typeof x === "function" && (x as any)._streamFactory === true
|
|
58
|
-
|
|
59
|
-
const isStreamCallable = (x: unknown): x is StreamMutationCallable<string, any, any, any, any> =>
|
|
60
|
-
x !== null && x !== undefined && (x as any)._streamCallable === true
|
|
61
25
|
type FnOptions<
|
|
62
26
|
Id extends string,
|
|
63
27
|
I18nCustomKey extends string,
|
|
@@ -218,15 +182,8 @@ export declare namespace Commander {
|
|
|
218
182
|
/** reactive */
|
|
219
183
|
result: AsyncResult.AsyncResult<A, E>
|
|
220
184
|
/**
|
|
221
|
-
* reactive –
|
|
222
|
-
*
|
|
223
|
-
* Reflects the live AsyncResult of the underlying stream.
|
|
224
|
-
*/
|
|
225
|
-
running: AsyncResult.AsyncResult<any, any> | undefined
|
|
226
|
-
/**
|
|
227
|
-
* reactive – formatted progress info computed from `running` via the
|
|
228
|
-
* `progress` option. Useful as the loading state on a `CommandButton`.
|
|
229
|
-
* Undefined when no `progress` formatter was supplied.
|
|
185
|
+
* reactive – formatted progress info driven by `Command.mapProgress` or `Command.updateProgress`
|
|
186
|
+
* inside a `streamFn` handler. Undefined for non-stream commands.
|
|
230
187
|
*/
|
|
231
188
|
progress: Progress | undefined
|
|
232
189
|
/** reactive */
|
|
@@ -1873,6 +1830,97 @@ export declare namespace Commander {
|
|
|
1873
1830
|
? CommandOut<Arg, SA2, SE2 | EE2, SR2 | ER2, Id, I18nKey, State>
|
|
1874
1831
|
: never
|
|
1875
1832
|
}
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* Type returned by `mutate.wrap` on a stream handler — analogous to `CommanderWrap` but for streams.
|
|
1836
|
+
* The handler is pre-baked (from the stream mutation), so this is called with only optional combinators.
|
|
1837
|
+
*/
|
|
1838
|
+
export type StreamerWrap<
|
|
1839
|
+
RT,
|
|
1840
|
+
Id extends string,
|
|
1841
|
+
I18nKey extends string,
|
|
1842
|
+
State extends IntlRecord | undefined,
|
|
1843
|
+
Arg,
|
|
1844
|
+
SA,
|
|
1845
|
+
SE,
|
|
1846
|
+
SR
|
|
1847
|
+
> =
|
|
1848
|
+
& CommandContextLocal<Id, I18nKey>
|
|
1849
|
+
& { readonly state: Context.Service<`Commander.Command.${Id}.state`, State> }
|
|
1850
|
+
& {
|
|
1851
|
+
(): Exclude<SR, RT> extends never ? CommandOut<Arg, SA, SE, SR, Id, I18nKey, State>
|
|
1852
|
+
: MissingDependencies<RT, SR> & {}
|
|
1853
|
+
<A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>>(
|
|
1854
|
+
a: (
|
|
1855
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1856
|
+
arg: ArgForCombinator<Arg>,
|
|
1857
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1858
|
+
) => A
|
|
1859
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1860
|
+
<
|
|
1861
|
+
B,
|
|
1862
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1863
|
+
>(
|
|
1864
|
+
a: (
|
|
1865
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1866
|
+
arg: ArgForCombinator<Arg>,
|
|
1867
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1868
|
+
) => B,
|
|
1869
|
+
b: (
|
|
1870
|
+
_: B,
|
|
1871
|
+
arg: ArgForCombinator<Arg>,
|
|
1872
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1873
|
+
) => A
|
|
1874
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1875
|
+
<
|
|
1876
|
+
B,
|
|
1877
|
+
C,
|
|
1878
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1879
|
+
>(
|
|
1880
|
+
a: (
|
|
1881
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1882
|
+
arg: ArgForCombinator<Arg>,
|
|
1883
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1884
|
+
) => B,
|
|
1885
|
+
b: (
|
|
1886
|
+
_: B,
|
|
1887
|
+
arg: ArgForCombinator<Arg>,
|
|
1888
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1889
|
+
) => C,
|
|
1890
|
+
c: (
|
|
1891
|
+
_: C,
|
|
1892
|
+
arg: ArgForCombinator<Arg>,
|
|
1893
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1894
|
+
) => A
|
|
1895
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1896
|
+
<
|
|
1897
|
+
B,
|
|
1898
|
+
C,
|
|
1899
|
+
D,
|
|
1900
|
+
A extends Stream.Stream<any, any, RT | CommandContext | `Commander.Command.${Id}.state`>
|
|
1901
|
+
>(
|
|
1902
|
+
a: (
|
|
1903
|
+
_: Stream.Stream<SA, SE, SR>,
|
|
1904
|
+
arg: ArgForCombinator<Arg>,
|
|
1905
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1906
|
+
) => B,
|
|
1907
|
+
b: (
|
|
1908
|
+
_: B,
|
|
1909
|
+
arg: ArgForCombinator<Arg>,
|
|
1910
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1911
|
+
) => C,
|
|
1912
|
+
c: (
|
|
1913
|
+
_: C,
|
|
1914
|
+
arg: ArgForCombinator<Arg>,
|
|
1915
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1916
|
+
) => D,
|
|
1917
|
+
d: (
|
|
1918
|
+
_: D,
|
|
1919
|
+
arg: ArgForCombinator<Arg>,
|
|
1920
|
+
ctx: CommandContextLocal2<NoInfer<Id>, NoInfer<I18nKey>, NoInfer<State>>
|
|
1921
|
+
) => A
|
|
1922
|
+
): CommandOut<Arg, Stream.Success<A>, Stream.Error<A>, Stream.Services<A>, Id, I18nKey, State>
|
|
1923
|
+
}
|
|
1876
1924
|
}
|
|
1877
1925
|
|
|
1878
1926
|
type ErrorRenderer<E, Args extends readonly any[]> = (e: E, action: string, ...args: Args) => string | undefined
|
|
@@ -1966,25 +2014,15 @@ const defaultFailureMessageHandler = <E, Args extends Array<unknown>, AME, AMR>(
|
|
|
1966
2014
|
),
|
|
1967
2015
|
onSome: (e) => {
|
|
1968
2016
|
const rendered = renderError(action, errorRenderer)(e, ...args)
|
|
1969
|
-
return
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
}
|
|
1979
|
-
: {
|
|
1980
|
-
level: "warn" as const,
|
|
1981
|
-
message: `${
|
|
1982
|
-
intl.formatMessage(
|
|
1983
|
-
{ id: "handle.with_errors" },
|
|
1984
|
-
{ action }
|
|
1985
|
-
)
|
|
1986
|
-
}:\n` + rendered
|
|
1987
|
-
}
|
|
2017
|
+
return {
|
|
2018
|
+
level: "warn" as const,
|
|
2019
|
+
message: `${
|
|
2020
|
+
intl.formatMessage(
|
|
2021
|
+
{ id: "handle.with_errors" },
|
|
2022
|
+
{ action }
|
|
2023
|
+
)
|
|
2024
|
+
}:\n` + rendered
|
|
2025
|
+
}
|
|
1988
2026
|
}
|
|
1989
2027
|
})
|
|
1990
2028
|
})
|
|
@@ -2180,14 +2218,13 @@ export const CommanderStatic = {
|
|
|
2180
2218
|
),
|
|
2181
2219
|
onSuccess: options?.onSuccess === null
|
|
2182
2220
|
? null
|
|
2183
|
-
: (
|
|
2221
|
+
: (_a, ..._args) =>
|
|
2184
2222
|
hasCustomSuccess
|
|
2185
2223
|
? intl.formatMessage(
|
|
2186
2224
|
{ id: customSuccess },
|
|
2187
2225
|
cc.state
|
|
2188
2226
|
)
|
|
2189
|
-
:
|
|
2190
|
-
+ (S.is(OperationSuccess)(a) && a.message ? "\n" + a.message : "")),
|
|
2227
|
+
: intl.formatMessage({ id: "handle.success" }, { action: cc.action }),
|
|
2191
2228
|
onFailure: defaultFailureMessageHandler(
|
|
2192
2229
|
hasCustomFailure ? intl.formatMessage({ id: customFailure }, cc.state) : cc.action,
|
|
2193
2230
|
options?.errorRenderer as ErrorRenderer<E, Args> | undefined
|
|
@@ -2296,7 +2333,14 @@ export const CommanderStatic = {
|
|
|
2296
2333
|
|
|
2297
2334
|
const toastId: string | number | undefined = waitingMsg === null
|
|
2298
2335
|
? stableToastId
|
|
2299
|
-
:
|
|
2336
|
+
: stableToastId ?? `wait-${Math.random().toString(36).slice(2)}`
|
|
2337
|
+
|
|
2338
|
+
const waitingFiber = waitingMsg === null ? undefined : yield* Effect.forkDetach(
|
|
2339
|
+
Effect.sleep("1 seconds").pipe(
|
|
2340
|
+
Effect.andThen(toast.info(waitingMsg, { id: toastId!, timeout: Infinity }))
|
|
2341
|
+
)
|
|
2342
|
+
)
|
|
2343
|
+
const interruptWaiting = waitingFiber ? Fiber.interrupt(waitingFiber) : Effect.void
|
|
2300
2344
|
|
|
2301
2345
|
const failureHandler = defaultFailureMessageHandler<E, [], never, never>(
|
|
2302
2346
|
hasCustomFailure ? intl.formatMessage({ id: customFailure }, cc.state) : cc.action,
|
|
@@ -2316,9 +2360,10 @@ export const CommanderStatic = {
|
|
|
2316
2360
|
// Update CommandProgress so CommandButton progress indicator is also driven
|
|
2317
2361
|
yield* CommandProgress.use((s) => s.update(p))
|
|
2318
2362
|
if (toastId !== undefined) {
|
|
2363
|
+
yield* interruptWaiting
|
|
2319
2364
|
const progressText = typeof p === "string" ? p : p.text
|
|
2320
2365
|
const msg = waitingMsg ? `${waitingMsg}\n${progressText}` : progressText
|
|
2321
|
-
yield* toast.info(msg, { id: toastId })
|
|
2366
|
+
yield* toast.info(msg, { id: toastId, timeout: Infinity })
|
|
2322
2367
|
}
|
|
2323
2368
|
}
|
|
2324
2369
|
}
|
|
@@ -2326,6 +2371,7 @@ export const CommanderStatic = {
|
|
|
2326
2371
|
),
|
|
2327
2372
|
Stream.tapCause(Effect.fnUntraced(function*(cause) {
|
|
2328
2373
|
didFail = true
|
|
2374
|
+
yield* interruptWaiting
|
|
2329
2375
|
if (Cause.hasInterruptsOnly(cause)) {
|
|
2330
2376
|
if (toastId !== undefined) yield* toast.dismiss(toastId)
|
|
2331
2377
|
return
|
|
@@ -2351,9 +2397,9 @@ export const CommanderStatic = {
|
|
|
2351
2397
|
}
|
|
2352
2398
|
}, Effect.uninterruptible)),
|
|
2353
2399
|
Stream.ensuring(Effect.suspend(() => {
|
|
2354
|
-
if (didFail) return
|
|
2400
|
+
if (didFail) return interruptWaiting
|
|
2355
2401
|
|
|
2356
|
-
if (options?.onSuccess === null) return
|
|
2402
|
+
if (options?.onSuccess === null) return interruptWaiting
|
|
2357
2403
|
|
|
2358
2404
|
const successMsg: string | null = typeof options?.onSuccess === "string"
|
|
2359
2405
|
? options.onSuccess
|
|
@@ -2362,13 +2408,14 @@ export const CommanderStatic = {
|
|
|
2362
2408
|
: hasCustomSuccess
|
|
2363
2409
|
? intl.formatMessage({ id: customSuccess }, cc.state)
|
|
2364
2410
|
: intl.formatMessage({ id: "handle.success" }, { action: cc.action })
|
|
2365
|
-
+ (S.is(OperationSuccess)(lastValue) && lastValue.message ? "\n" + lastValue.message : "")
|
|
2366
2411
|
|
|
2367
|
-
if (successMsg === null) return
|
|
2412
|
+
if (successMsg === null) return interruptWaiting
|
|
2368
2413
|
|
|
2369
|
-
return
|
|
2370
|
-
|
|
2371
|
-
|
|
2414
|
+
return interruptWaiting.pipe(
|
|
2415
|
+
Effect.andThen(toast.success(
|
|
2416
|
+
successMsg,
|
|
2417
|
+
toastId !== undefined ? { id: toastId, timeout: baseTimeout } : { timeout: baseTimeout }
|
|
2418
|
+
))
|
|
2372
2419
|
)
|
|
2373
2420
|
}))
|
|
2374
2421
|
)
|
|
@@ -2495,17 +2542,11 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2495
2542
|
readonly makeCommand = <
|
|
2496
2543
|
const Id extends string,
|
|
2497
2544
|
const State extends IntlRecord | undefined,
|
|
2498
|
-
const I18nKey extends string = Id
|
|
2499
|
-
RunningA = unknown,
|
|
2500
|
-
RunningE = unknown
|
|
2545
|
+
const I18nKey extends string = Id
|
|
2501
2546
|
>(
|
|
2502
2547
|
id_: Id | { id: Id },
|
|
2503
2548
|
options?: FnOptions<Id, I18nKey, State>,
|
|
2504
|
-
errorDef?: Error
|
|
2505
|
-
streamMeta?: {
|
|
2506
|
-
running?: ComputedRef<AsyncResult.AsyncResult<RunningA, RunningE>> | undefined
|
|
2507
|
-
progress?: ComputedRef<Progress | undefined> | undefined
|
|
2508
|
-
}
|
|
2549
|
+
errorDef?: Error
|
|
2509
2550
|
) => {
|
|
2510
2551
|
const id = typeof id_ === "string" ? id_ : id_.id
|
|
2511
2552
|
const state = getStateValues(options)
|
|
@@ -2690,12 +2731,8 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2690
2731
|
|
|
2691
2732
|
/** reactive */
|
|
2692
2733
|
result,
|
|
2693
|
-
/**
|
|
2694
|
-
|
|
2695
|
-
running: streamMeta?.running,
|
|
2696
|
-
/** reactive – formatted progress info for current `running` state, when `progress`
|
|
2697
|
-
* formatter was supplied to the stream factory */
|
|
2698
|
-
progress: streamMeta?.progress,
|
|
2734
|
+
/** always undefined for non-stream commands */
|
|
2735
|
+
progress: undefined,
|
|
2699
2736
|
/** reactive */
|
|
2700
2737
|
waiting,
|
|
2701
2738
|
/** reactive */
|
|
@@ -2797,40 +2834,14 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2797
2834
|
fn = <
|
|
2798
2835
|
const Id extends string,
|
|
2799
2836
|
const State extends IntlRecord = IntlRecord,
|
|
2800
|
-
const I18nKey extends string = Id
|
|
2801
|
-
RunningA = unknown,
|
|
2802
|
-
RunningE = unknown
|
|
2837
|
+
const I18nKey extends string = Id
|
|
2803
2838
|
>(
|
|
2804
|
-
id:
|
|
2805
|
-
| Id
|
|
2806
|
-
| { id: Id }
|
|
2807
|
-
| StreamMutationCallable<Id, any, RunningA, RunningE, any>
|
|
2808
|
-
| StreamMutationFactory<Id, any, RunningA, RunningE, any>,
|
|
2839
|
+
id: Id | { id: Id },
|
|
2809
2840
|
options?: FnOptions<Id, I18nKey, State>
|
|
2810
2841
|
): Commander.Gen<RT | RTHooks, Id, I18nKey, State> & Commander.NonGen<RT | RTHooks, Id, I18nKey, State> & {
|
|
2811
2842
|
state: Context.Service<`Commander.Command.${Id}.state`, State>
|
|
2812
2843
|
} => {
|
|
2813
|
-
|
|
2814
|
-
const resolvedId: Id = typeof id === "string" ? id : (id as { id: Id }).id
|
|
2815
|
-
const factory = isStreamFactory(id)
|
|
2816
|
-
const callable = !factory && isStreamCallable(id)
|
|
2817
|
-
const resolveStreamMeta = ():
|
|
2818
|
-
| {
|
|
2819
|
-
running?: ComputedRef<AsyncResult.AsyncResult<RunningA, RunningE>> | undefined
|
|
2820
|
-
progress?: ComputedRef<Progress | undefined> | undefined
|
|
2821
|
-
}
|
|
2822
|
-
| undefined =>
|
|
2823
|
-
{
|
|
2824
|
-
if (factory) {
|
|
2825
|
-
const c = id()
|
|
2826
|
-
return { running: c.running, progress: c.progress }
|
|
2827
|
-
}
|
|
2828
|
-
if (callable) {
|
|
2829
|
-
const c = id as StreamMutationCallable<Id, any, RunningA, RunningE, any>
|
|
2830
|
-
return { running: c.running, progress: c.progress }
|
|
2831
|
-
}
|
|
2832
|
-
return undefined
|
|
2833
|
-
}
|
|
2844
|
+
const resolvedId: Id = typeof id === "string" ? id : id.id
|
|
2834
2845
|
return Object.assign(
|
|
2835
2846
|
(
|
|
2836
2847
|
fn: any,
|
|
@@ -2842,9 +2853,7 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2842
2853
|
const errorDef = new Error()
|
|
2843
2854
|
Error.stackTraceLimit = limit
|
|
2844
2855
|
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
return this.makeCommand(resolvedId, options, errorDef, streamMeta)(
|
|
2856
|
+
return this.makeCommand(resolvedId, options, errorDef)(
|
|
2848
2857
|
Effect.fnUntraced(
|
|
2849
2858
|
// fnUntraced only supports generators as first arg, so we convert to generator if needed
|
|
2850
2859
|
isGeneratorFunction(fn) ? fn : function*(...args) {
|
|
@@ -3047,8 +3056,6 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
3047
3056
|
namespace: initialContext.namespace,
|
|
3048
3057
|
namespaced: initialContext.namespaced,
|
|
3049
3058
|
result,
|
|
3050
|
-
/** always undefined for streamFn commands — `result` already exposes the live stream state */
|
|
3051
|
-
running: undefined,
|
|
3052
3059
|
/** reactive – progress driven by `Command.mapProgress` or `Command.updateProgress` inside the stream */
|
|
3053
3060
|
progress,
|
|
3054
3061
|
waiting,
|
|
@@ -3270,147 +3277,77 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
3270
3277
|
* **User Feedback**: Use the `withDefaultToast` helper for status notifications, or render
|
|
3271
3278
|
* the `result` inline for custom UI feedback.
|
|
3272
3279
|
*/
|
|
3273
|
-
|
|
3280
|
+
streamWrap = <
|
|
3274
3281
|
const Id extends string,
|
|
3275
3282
|
Arg,
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3283
|
+
SA,
|
|
3284
|
+
SE,
|
|
3285
|
+
SR,
|
|
3279
3286
|
const State extends IntlRecord = IntlRecord,
|
|
3280
3287
|
I18nKey extends string = Id
|
|
3281
3288
|
>(
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
| ((arg: Arg) => Effect.Effect<A, E, R>) & { id: Id }
|
|
3285
|
-
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
3286
|
-
| {
|
|
3287
|
-
id: Id
|
|
3288
|
-
mutateToResult:
|
|
3289
|
-
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
3290
|
-
| StreamMutationCallable<Id, Arg, A, E, R>
|
|
3291
|
-
}
|
|
3292
|
-
| StreamMutationCallable<Id, Arg, A, E, R>,
|
|
3289
|
+
handler: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>,
|
|
3290
|
+
id: Id,
|
|
3293
3291
|
options?: FnOptions<Id, I18nKey, State>
|
|
3294
|
-
): Commander.
|
|
3295
|
-
if (mutation !== null && typeof mutation === "object" && "mutateToResult" in mutation) {
|
|
3296
|
-
return this.wrapStream(mutation as any, options) as any
|
|
3297
|
-
}
|
|
3298
|
-
if (isStreamCallable(mutation) || isStreamFactory(mutation)) {
|
|
3299
|
-
return this.wrapStream(mutation as any, options) as any
|
|
3300
|
-
}
|
|
3301
|
-
// At this point mutation is either { mutate, id } or (fn & { id })
|
|
3302
|
-
const callMutation = mutation as
|
|
3303
|
-
| { mutate: (arg: Arg) => Effect.Effect<A, E, R>; id: Id }
|
|
3304
|
-
| (((arg: Arg) => Effect.Effect<A, E, R>) & { id: Id })
|
|
3292
|
+
): Commander.StreamerWrap<RT | RTHooks, Id, I18nKey, State, Arg, SA, SE, SR> => {
|
|
3305
3293
|
return Object.assign(
|
|
3306
|
-
(
|
|
3307
|
-
...combinators: any[]
|
|
3308
|
-
): any => {
|
|
3309
|
-
// we capture the definition stack here, so we can append it to later stack traces
|
|
3294
|
+
(...combinators: any[]): any => {
|
|
3310
3295
|
const limit = Error.stackTraceLimit
|
|
3311
3296
|
Error.stackTraceLimit = 2
|
|
3312
3297
|
const errorDef = new Error()
|
|
3313
3298
|
Error.stackTraceLimit = limit
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
return
|
|
3323
|
-
}
|
|
3324
|
-
...combinators as [any]
|
|
3325
|
-
) as any
|
|
3299
|
+
return this.makeStreamCommand(id, options, errorDef)(
|
|
3300
|
+
combinators.length === 0
|
|
3301
|
+
? handler
|
|
3302
|
+
: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => {
|
|
3303
|
+
let current: any = handler(arg, ctx)
|
|
3304
|
+
for (const combinator of combinators) {
|
|
3305
|
+
current = combinator(current, arg, ctx)
|
|
3306
|
+
}
|
|
3307
|
+
return current
|
|
3308
|
+
}
|
|
3326
3309
|
)
|
|
3327
3310
|
},
|
|
3328
|
-
makeBaseInfo(
|
|
3311
|
+
makeBaseInfo(id, options),
|
|
3329
3312
|
{
|
|
3330
3313
|
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
3331
|
-
`Commander.Command.${
|
|
3314
|
+
`Commander.Command.${id}.state`
|
|
3332
3315
|
)
|
|
3333
3316
|
}
|
|
3334
3317
|
)
|
|
3335
3318
|
}
|
|
3336
3319
|
|
|
3337
|
-
|
|
3338
|
-
* Define a Command from a stream-type mutation (`mutateToResult` factory).
|
|
3339
|
-
* The stream's reactive `AsyncResult` ref is exposed as `running` for independent progress tracking.
|
|
3340
|
-
* The command's own `result` reflects the execution outcome of the `execute` function.
|
|
3341
|
-
* Supports the same combinator pipeline as `wrap` (e.g. `withDefaultToast`).
|
|
3342
|
-
*
|
|
3343
|
-
* Each invocation of the resulting wrap call produces a fresh `[ref, execute]` pair
|
|
3344
|
-
* (the `mutateToResult` factory is called once per build), so independent commands
|
|
3345
|
-
* don't share progress state.
|
|
3346
|
-
*
|
|
3347
|
-
* Accepts either:
|
|
3348
|
-
* - An object with `id` and `mutateToResult` factory (e.g. a client entry)
|
|
3349
|
-
* - The `mutateToResult` factory directly (callable, with `id`)
|
|
3350
|
-
* - An already-called factory result (`[resultRef, execute] & { id }`) — shared ref across builds
|
|
3351
|
-
*
|
|
3352
|
-
* @example
|
|
3353
|
-
* ```ts
|
|
3354
|
-
* // Via client entry (recommended):
|
|
3355
|
-
* const exportCmd = Command.wrapStream(client.myExport)()
|
|
3356
|
-
*
|
|
3357
|
-
* // Via factory directly:
|
|
3358
|
-
* const exportCmd = Command.wrapStream(client.myExport.mutateToResult)()
|
|
3359
|
-
*
|
|
3360
|
-
* // Via already-called factory (shared ref):
|
|
3361
|
-
* const stream = client.myExport.mutateToResult()
|
|
3362
|
-
* const exportCmd = Command.wrapStream(stream)()
|
|
3363
|
-
* ```
|
|
3364
|
-
*/
|
|
3365
|
-
wrapStream = <
|
|
3320
|
+
wrap = <
|
|
3366
3321
|
const Id extends string,
|
|
3367
3322
|
Arg,
|
|
3368
3323
|
A,
|
|
3369
3324
|
E,
|
|
3370
3325
|
R,
|
|
3371
3326
|
const State extends IntlRecord = IntlRecord,
|
|
3372
|
-
|
|
3327
|
+
I18nKey extends string = Id
|
|
3373
3328
|
>(
|
|
3374
3329
|
mutation:
|
|
3375
|
-
| {
|
|
3376
|
-
|
|
3377
|
-
mutateToResult:
|
|
3378
|
-
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
3379
|
-
| StreamMutationCallable<Id, Arg, A, E, R>
|
|
3380
|
-
}
|
|
3381
|
-
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
3382
|
-
| StreamMutationCallable<Id, Arg, A, E, R>,
|
|
3330
|
+
| { mutate: (arg: Arg) => Effect.Effect<A, E, R>; id: Id }
|
|
3331
|
+
| ((arg: Arg) => Effect.Effect<A, E, R>) & { id: Id },
|
|
3383
3332
|
options?: FnOptions<Id, I18nKey, State>
|
|
3384
3333
|
): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
|
|
3385
|
-
const
|
|
3386
|
-
// Resolve `source` to the factory or already-invoked callable.
|
|
3387
|
-
const source: StreamMutationFactory<Id, Arg, A, E, R> | StreamMutationCallable<Id, Arg, A, E, R> =
|
|
3388
|
-
mutation !== null && typeof mutation === "object" && "mutateToResult" in mutation
|
|
3389
|
-
? (mutation.mutateToResult as any)
|
|
3390
|
-
: (mutation as any)
|
|
3391
|
-
const resolveCallable = (): StreamMutationCallable<Id, Arg, A, E, R> =>
|
|
3392
|
-
(isStreamFactory(source)
|
|
3393
|
-
? (source as StreamMutationFactory<Id, Arg, A, E, R>)()
|
|
3394
|
-
: source) as StreamMutationCallable<Id, Arg, A, E, R>
|
|
3334
|
+
const callMutation = mutation
|
|
3395
3335
|
return Object.assign(
|
|
3396
|
-
(
|
|
3336
|
+
(
|
|
3337
|
+
...combinators: any[]
|
|
3338
|
+
): any => {
|
|
3397
3339
|
// we capture the definition stack here, so we can append it to later stack traces
|
|
3398
3340
|
const limit = Error.stackTraceLimit
|
|
3399
3341
|
Error.stackTraceLimit = 2
|
|
3400
3342
|
const errorDef = new Error()
|
|
3401
3343
|
Error.stackTraceLimit = limit
|
|
3344
|
+
const mutate = "mutate" in callMutation
|
|
3345
|
+
? callMutation.mutate
|
|
3346
|
+
: callMutation
|
|
3402
3347
|
|
|
3403
|
-
|
|
3404
|
-
// wrap call gets its own state + execute pair. `running`/`progress`
|
|
3405
|
-
// are only surfaced when the factory was called with a `progress` formatter.
|
|
3406
|
-
const callable = resolveCallable()
|
|
3407
|
-
const mutate: (_arg: Arg) => Effect.Effect<any, E, R> = Effect.isEffect(callable)
|
|
3408
|
-
? (_arg: Arg) => callable
|
|
3409
|
-
: callable as (arg: Arg) => Effect.Effect<any, E, R>
|
|
3410
|
-
const streamMeta = { running: callable.running, progress: callable.progress }
|
|
3411
|
-
|
|
3412
|
-
return this.makeCommand(id, options, errorDef, streamMeta)(
|
|
3348
|
+
return this.makeCommand(callMutation.id, options, errorDef)(
|
|
3413
3349
|
Effect.fnUntraced(
|
|
3350
|
+
// fnUntraced only supports generators as first arg, so we convert to generator if needed
|
|
3414
3351
|
isGeneratorFunction(mutate) ? mutate : function*(arg: Arg) {
|
|
3415
3352
|
return yield* mutate(arg)
|
|
3416
3353
|
},
|
|
@@ -3418,10 +3355,10 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
3418
3355
|
) as any
|
|
3419
3356
|
)
|
|
3420
3357
|
},
|
|
3421
|
-
makeBaseInfo(id, options),
|
|
3358
|
+
makeBaseInfo(callMutation.id, options),
|
|
3422
3359
|
{
|
|
3423
3360
|
state: Context.Service<`Commander.Command.${Id}.state`, State>(
|
|
3424
|
-
`Commander.Command.${id}.state`
|
|
3361
|
+
`Commander.Command.${callMutation.id}.state`
|
|
3425
3362
|
)
|
|
3426
3363
|
}
|
|
3427
3364
|
)
|