@effect-app/vue 4.0.0-beta.186 → 4.0.0-beta.188

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/src/commander.ts CHANGED
@@ -3,7 +3,6 @@ import { asResult, asStreamResult, deepToRaw, type MissingDependencies, reportRu
3
3
  import { reportMessage } from "@effect-app/vue/errorReporter"
4
4
  import { Cause, Context, Effect, type Exit, type 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 – set when the command wraps a stream (`wrapStream` / `wrap` with `mutateToResult`)
222
- * or when the `progress` option is provided to `fn`.
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 S.is(OperationFailure)(e)
1970
- ? {
1971
- level: "warn" as const,
1972
- message: `${
1973
- intl.formatMessage(
1974
- { id: "handle.with_warnings" },
1975
- { action }
1976
- )
1977
- }${rendered ? "\n" + rendered : ""}`
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
- : (a, ..._args) =>
2221
+ : (_a, ..._args) =>
2184
2222
  hasCustomSuccess
2185
2223
  ? intl.formatMessage(
2186
2224
  { id: customSuccess },
2187
2225
  cc.state
2188
2226
  )
2189
- : (intl.formatMessage({ id: "handle.success" }, { action: cc.action })
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
@@ -2362,7 +2399,6 @@ export const CommanderStatic = {
2362
2399
  : hasCustomSuccess
2363
2400
  ? intl.formatMessage({ id: customSuccess }, cc.state)
2364
2401
  : intl.formatMessage({ id: "handle.success" }, { action: cc.action })
2365
- + (S.is(OperationSuccess)(lastValue) && lastValue.message ? "\n" + lastValue.message : "")
2366
2402
 
2367
2403
  if (successMsg === null) return Effect.void
2368
2404
 
@@ -2495,17 +2531,11 @@ export class CommanderImpl<RT, RTHooks> {
2495
2531
  readonly makeCommand = <
2496
2532
  const Id extends string,
2497
2533
  const State extends IntlRecord | undefined,
2498
- const I18nKey extends string = Id,
2499
- RunningA = unknown,
2500
- RunningE = unknown
2534
+ const I18nKey extends string = Id
2501
2535
  >(
2502
2536
  id_: Id | { id: Id },
2503
2537
  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
- }
2538
+ errorDef?: Error
2509
2539
  ) => {
2510
2540
  const id = typeof id_ === "string" ? id_ : id_.id
2511
2541
  const state = getStateValues(options)
@@ -2690,12 +2720,8 @@ export class CommanderImpl<RT, RTHooks> {
2690
2720
 
2691
2721
  /** reactive */
2692
2722
  result,
2693
- /** reactive live AsyncResult of the underlying stream, exposed only when
2694
- * the stream factory was called with a `progress` formatter */
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,
2723
+ /** always undefined for non-stream commands */
2724
+ progress: undefined,
2699
2725
  /** reactive */
2700
2726
  waiting,
2701
2727
  /** reactive */
@@ -2797,40 +2823,14 @@ export class CommanderImpl<RT, RTHooks> {
2797
2823
  fn = <
2798
2824
  const Id extends string,
2799
2825
  const State extends IntlRecord = IntlRecord,
2800
- const I18nKey extends string = Id,
2801
- RunningA = unknown,
2802
- RunningE = unknown
2826
+ const I18nKey extends string = Id
2803
2827
  >(
2804
- id:
2805
- | Id
2806
- | { id: Id }
2807
- | StreamMutationCallable<Id, any, RunningA, RunningE, any>
2808
- | StreamMutationFactory<Id, any, RunningA, RunningE, any>,
2828
+ id: Id | { id: Id },
2809
2829
  options?: FnOptions<Id, I18nKey, State>
2810
2830
  ): Commander.Gen<RT | RTHooks, Id, I18nKey, State> & Commander.NonGen<RT | RTHooks, Id, I18nKey, State> & {
2811
2831
  state: Context.Service<`Commander.Command.${Id}.state`, State>
2812
2832
  } => {
2813
- // Resolve id and (optionally) per-build stream metadata.
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
- }
2833
+ const resolvedId: Id = typeof id === "string" ? id : id.id
2834
2834
  return Object.assign(
2835
2835
  (
2836
2836
  fn: any,
@@ -2842,9 +2842,7 @@ export class CommanderImpl<RT, RTHooks> {
2842
2842
  const errorDef = new Error()
2843
2843
  Error.stackTraceLimit = limit
2844
2844
 
2845
- const streamMeta = resolveStreamMeta()
2846
-
2847
- return this.makeCommand(resolvedId, options, errorDef, streamMeta)(
2845
+ return this.makeCommand(resolvedId, options, errorDef)(
2848
2846
  Effect.fnUntraced(
2849
2847
  // fnUntraced only supports generators as first arg, so we convert to generator if needed
2850
2848
  isGeneratorFunction(fn) ? fn : function*(...args) {
@@ -3047,8 +3045,6 @@ export class CommanderImpl<RT, RTHooks> {
3047
3045
  namespace: initialContext.namespace,
3048
3046
  namespaced: initialContext.namespaced,
3049
3047
  result,
3050
- /** always undefined for streamFn commands — `result` already exposes the live stream state */
3051
- running: undefined,
3052
3048
  /** reactive – progress driven by `Command.mapProgress` or `Command.updateProgress` inside the stream */
3053
3049
  progress,
3054
3050
  waiting,
@@ -3270,147 +3266,77 @@ export class CommanderImpl<RT, RTHooks> {
3270
3266
  * **User Feedback**: Use the `withDefaultToast` helper for status notifications, or render
3271
3267
  * the `result` inline for custom UI feedback.
3272
3268
  */
3273
- wrap = <
3269
+ streamWrap = <
3274
3270
  const Id extends string,
3275
3271
  Arg,
3276
- A,
3277
- E,
3278
- R,
3272
+ SA,
3273
+ SE,
3274
+ SR,
3279
3275
  const State extends IntlRecord = IntlRecord,
3280
3276
  I18nKey extends string = Id
3281
3277
  >(
3282
- mutation:
3283
- | { mutate: (arg: Arg) => Effect.Effect<A, E, R>; id: Id }
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>,
3278
+ handler: (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => Stream.Stream<SA, SE, SR>,
3279
+ id: Id,
3293
3280
  options?: FnOptions<Id, I18nKey, State>
3294
- ): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
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 })
3281
+ ): Commander.StreamerWrap<RT | RTHooks, Id, I18nKey, State, Arg, SA, SE, SR> => {
3305
3282
  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
3283
+ (...combinators: any[]): any => {
3310
3284
  const limit = Error.stackTraceLimit
3311
3285
  Error.stackTraceLimit = 2
3312
3286
  const errorDef = new Error()
3313
3287
  Error.stackTraceLimit = limit
3314
- const mutate = "mutate" in callMutation
3315
- ? callMutation.mutate
3316
- : callMutation
3317
-
3318
- return this.makeCommand(callMutation.id, options, errorDef)(
3319
- Effect.fnUntraced(
3320
- // fnUntraced only supports generators as first arg, so we convert to generator if needed
3321
- isGeneratorFunction(mutate) ? mutate : function*(arg: Arg) {
3322
- return yield* mutate(arg)
3323
- },
3324
- ...combinators as [any]
3325
- ) as any
3288
+ return this.makeStreamCommand(id, options, errorDef)(
3289
+ combinators.length === 0
3290
+ ? handler
3291
+ : (arg: Arg, ctx: Commander.CommandContextLocal2<Id, I18nKey, State>) => {
3292
+ let current: any = handler(arg, ctx)
3293
+ for (const combinator of combinators) {
3294
+ current = combinator(current, arg, ctx)
3295
+ }
3296
+ return current
3297
+ }
3326
3298
  )
3327
3299
  },
3328
- makeBaseInfo(callMutation.id, options),
3300
+ makeBaseInfo(id, options),
3329
3301
  {
3330
3302
  state: Context.Service<`Commander.Command.${Id}.state`, State>(
3331
- `Commander.Command.${callMutation.id}.state`
3303
+ `Commander.Command.${id}.state`
3332
3304
  )
3333
3305
  }
3334
3306
  )
3335
3307
  }
3336
3308
 
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 = <
3309
+ wrap = <
3366
3310
  const Id extends string,
3367
3311
  Arg,
3368
3312
  A,
3369
3313
  E,
3370
3314
  R,
3371
3315
  const State extends IntlRecord = IntlRecord,
3372
- const I18nKey extends string = Id
3316
+ I18nKey extends string = Id
3373
3317
  >(
3374
3318
  mutation:
3375
- | {
3376
- id: Id
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>,
3319
+ | { mutate: (arg: Arg) => Effect.Effect<A, E, R>; id: Id }
3320
+ | ((arg: Arg) => Effect.Effect<A, E, R>) & { id: Id },
3383
3321
  options?: FnOptions<Id, I18nKey, State>
3384
3322
  ): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
3385
- const id = mutation.id
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>
3323
+ const callMutation = mutation
3395
3324
  return Object.assign(
3396
- (...combinators: any[]): any => {
3325
+ (
3326
+ ...combinators: any[]
3327
+ ): any => {
3397
3328
  // we capture the definition stack here, so we can append it to later stack traces
3398
3329
  const limit = Error.stackTraceLimit
3399
3330
  Error.stackTraceLimit = 2
3400
3331
  const errorDef = new Error()
3401
3332
  Error.stackTraceLimit = limit
3333
+ const mutate = "mutate" in callMutation
3334
+ ? callMutation.mutate
3335
+ : callMutation
3402
3336
 
3403
- // Fresh per build: invoke the factory once per command instance so each
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)(
3337
+ return this.makeCommand(callMutation.id, options, errorDef)(
3413
3338
  Effect.fnUntraced(
3339
+ // fnUntraced only supports generators as first arg, so we convert to generator if needed
3414
3340
  isGeneratorFunction(mutate) ? mutate : function*(arg: Arg) {
3415
3341
  return yield* mutate(arg)
3416
3342
  },
@@ -3418,10 +3344,10 @@ export class CommanderImpl<RT, RTHooks> {
3418
3344
  ) as any
3419
3345
  )
3420
3346
  },
3421
- makeBaseInfo(id, options),
3347
+ makeBaseInfo(callMutation.id, options),
3422
3348
  {
3423
3349
  state: Context.Service<`Commander.Command.${Id}.state`, State>(
3424
- `Commander.Command.${id}.state`
3350
+ `Commander.Command.${callMutation.id}.state`
3425
3351
  )
3426
3352
  }
3427
3353
  )