@effect-app/vue 4.0.0-beta.181 → 4.0.0-beta.182
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 +16 -0
- package/dist/commander.d.ts +17 -11
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +23 -23
- package/dist/makeClient.d.ts +15 -10
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +13 -13
- package/dist/mutate.d.ts +3 -2
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +6 -6
- package/examples/streamMutation.ts +8 -4
- package/package.json +2 -2
- package/src/commander.ts +45 -35
- package/src/makeClient.ts +28 -23
- package/src/mutate.ts +10 -6
- package/test/makeClient.test.ts +2 -2
- package/test/streamFinal.test.ts +2 -2
|
@@ -74,11 +74,15 @@ export const useExportMutation = () => {
|
|
|
74
74
|
// When a request schema has `type: "stream"`, `clientFor` exposes:
|
|
75
75
|
//
|
|
76
76
|
// client.exportData.mutateStream
|
|
77
|
-
// // -> () =>
|
|
78
|
-
// // Always invoke `()` to get a fresh
|
|
79
|
-
// const
|
|
77
|
+
// // -> (options?) => ((input: I) => Effect<Final, never, R>) & { id, running?, progress? }
|
|
78
|
+
// // Always invoke `()` (optionally with `{ progress }`) to get a fresh callable.
|
|
79
|
+
// const execute = client.exportData.mutateStream()
|
|
80
|
+
// const executeWithProgress = client.exportData.mutateStream({
|
|
81
|
+
// progress: (r) => r._tag === "Success" ? `${r.value.completed}/${r.value.total}` : undefined
|
|
82
|
+
// })
|
|
83
|
+
// // The callable can also be passed directly to Command.fn / Command.wrap / Command.wrapStream.
|
|
80
84
|
//
|
|
81
|
-
// which
|
|
85
|
+
// which wraps `asStreamResult(client.exportData.handler)` internally.
|
|
82
86
|
//
|
|
83
87
|
// The `.helpers` object also includes `exportDataStream` (the camelCase key
|
|
84
88
|
// plus "Stream" suffix) with the same factory shape.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/vue",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.182",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/effect-ts-app/libs/tree/main/packages/vue",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@vueuse/core": "^14.2.1",
|
|
12
12
|
"change-case": "^5.4.4",
|
|
13
13
|
"query-string": "^9.3.1",
|
|
14
|
-
"effect-app": "4.0.0-beta.
|
|
14
|
+
"effect-app": "4.0.0-beta.182"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"@effect/atom-vue": "^4.0.0-beta.59",
|
package/src/commander.ts
CHANGED
|
@@ -31,20 +31,31 @@ export type StreamMutationCallOptions<A, E> = {
|
|
|
31
31
|
progress?: (result: AsyncResult.AsyncResult<A, E>) => Progress | undefined
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The result of invoking a `mutateStream` factory: the `execute` function (or
|
|
36
|
+
* `Effect`, when the request takes no input) carries `id`, plus `running` and
|
|
37
|
+
* `progress` when the factory was called with a `progress` formatter. Pass
|
|
38
|
+
* directly to `Command.fn` / `Command.wrap` / `Command.wrapStream`, or invoke
|
|
39
|
+
* to run the stream.
|
|
40
|
+
*/
|
|
41
|
+
type StreamMutationCallable<Id extends string, Arg, A, E, R> =
|
|
42
|
+
& (((arg: Arg) => Effect.Effect<any, E, R>) | Effect.Effect<any, E, R>)
|
|
39
43
|
& {
|
|
40
44
|
readonly id: Id
|
|
45
|
+
readonly _streamCallable: true
|
|
41
46
|
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
42
47
|
readonly progress?: ComputedRef<Progress | undefined>
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
type StreamMutationFactory<Id extends string, Arg, A, E, R> =
|
|
46
|
-
& ((options?: StreamMutationCallOptions<A, E>) =>
|
|
47
|
-
& { readonly id: Id }
|
|
51
|
+
& ((options?: StreamMutationCallOptions<A, E>) => StreamMutationCallable<Id, Arg, A, E, R>)
|
|
52
|
+
& { readonly id: Id; readonly _streamFactory: true }
|
|
53
|
+
|
|
54
|
+
const isStreamFactory = (x: unknown): x is StreamMutationFactory<string, any, any, any, any> =>
|
|
55
|
+
typeof x === "function" && (x as any)._streamFactory === true
|
|
56
|
+
|
|
57
|
+
const isStreamCallable = (x: unknown): x is StreamMutationCallable<string, any, any, any, any> =>
|
|
58
|
+
x !== null && x !== undefined && (x as any)._streamCallable === true
|
|
48
59
|
type FnOptions<
|
|
49
60
|
Id extends string,
|
|
50
61
|
I18nCustomKey extends string,
|
|
@@ -2395,7 +2406,7 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2395
2406
|
id:
|
|
2396
2407
|
| Id
|
|
2397
2408
|
| { id: Id }
|
|
2398
|
-
|
|
|
2409
|
+
| StreamMutationCallable<Id, any, RunningA, RunningE, any>
|
|
2399
2410
|
| StreamMutationFactory<Id, any, RunningA, RunningE, any>,
|
|
2400
2411
|
options?: FnOptions<Id, I18nKey, State>
|
|
2401
2412
|
): Commander.Gen<RT | RTHooks, Id, I18nKey, State> & Commander.NonGen<RT | RTHooks, Id, I18nKey, State> & {
|
|
@@ -2403,8 +2414,8 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2403
2414
|
} => {
|
|
2404
2415
|
// Resolve id and (optionally) per-build stream metadata.
|
|
2405
2416
|
const resolvedId: Id = typeof id === "string" ? id : (id as { id: Id }).id
|
|
2406
|
-
const
|
|
2407
|
-
const
|
|
2417
|
+
const factory = isStreamFactory(id)
|
|
2418
|
+
const callable = !factory && isStreamCallable(id)
|
|
2408
2419
|
const resolveStreamMeta = ():
|
|
2409
2420
|
| {
|
|
2410
2421
|
running?: ComputedRef<AsyncResult.AsyncResult<RunningA, RunningE>> | undefined
|
|
@@ -2412,13 +2423,13 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2412
2423
|
}
|
|
2413
2424
|
| undefined =>
|
|
2414
2425
|
{
|
|
2415
|
-
if (
|
|
2416
|
-
const
|
|
2417
|
-
return { running:
|
|
2426
|
+
if (factory) {
|
|
2427
|
+
const c = id()
|
|
2428
|
+
return { running: c.running, progress: c.progress }
|
|
2418
2429
|
}
|
|
2419
|
-
if (
|
|
2420
|
-
const
|
|
2421
|
-
return { running:
|
|
2430
|
+
if (callable) {
|
|
2431
|
+
const c = id as StreamMutationCallable<Id, any, RunningA, RunningE, any>
|
|
2432
|
+
return { running: c.running, progress: c.progress }
|
|
2422
2433
|
}
|
|
2423
2434
|
return undefined
|
|
2424
2435
|
}
|
|
@@ -2563,18 +2574,15 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2563
2574
|
id: Id
|
|
2564
2575
|
mutateStream:
|
|
2565
2576
|
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
2566
|
-
|
|
|
2577
|
+
| StreamMutationCallable<Id, Arg, A, E, R>
|
|
2567
2578
|
}
|
|
2568
|
-
|
|
|
2579
|
+
| StreamMutationCallable<Id, Arg, A, E, R>,
|
|
2569
2580
|
options?: FnOptions<Id, I18nKey, State>
|
|
2570
2581
|
): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
|
|
2571
2582
|
if (mutation !== null && typeof mutation === "object" && "mutateStream" in mutation) {
|
|
2572
2583
|
return this.wrapStream(mutation as any, options) as any
|
|
2573
2584
|
}
|
|
2574
|
-
if (
|
|
2575
|
-
return this.wrapStream(mutation as any, options) as any
|
|
2576
|
-
}
|
|
2577
|
-
if (typeof mutation === "function" && "id" in mutation && (mutation as any).length <= 1) {
|
|
2585
|
+
if (isStreamCallable(mutation) || isStreamFactory(mutation)) {
|
|
2578
2586
|
return this.wrapStream(mutation as any, options) as any
|
|
2579
2587
|
}
|
|
2580
2588
|
// At this point mutation is either { mutate, id } or (fn & { id })
|
|
@@ -2655,19 +2663,22 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2655
2663
|
id: Id
|
|
2656
2664
|
mutateStream:
|
|
2657
2665
|
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
2658
|
-
|
|
|
2666
|
+
| StreamMutationCallable<Id, Arg, A, E, R>
|
|
2659
2667
|
}
|
|
2660
2668
|
| StreamMutationFactory<Id, Arg, A, E, R>
|
|
2661
|
-
|
|
|
2669
|
+
| StreamMutationCallable<Id, Arg, A, E, R>,
|
|
2662
2670
|
options?: FnOptions<Id, I18nKey, State>
|
|
2663
2671
|
): Commander.CommanderWrap<RT | RTHooks, Id, I18nKey, State, Arg, A, E, R> => {
|
|
2664
2672
|
const id = mutation.id
|
|
2665
|
-
// Resolve `source` to the factory or already-
|
|
2666
|
-
const source: StreamMutationFactory<Id, Arg, A, E, R> |
|
|
2673
|
+
// Resolve `source` to the factory or already-invoked callable.
|
|
2674
|
+
const source: StreamMutationFactory<Id, Arg, A, E, R> | StreamMutationCallable<Id, Arg, A, E, R> =
|
|
2667
2675
|
mutation !== null && typeof mutation === "object" && "mutateStream" in mutation
|
|
2668
2676
|
? (mutation.mutateStream as any)
|
|
2669
2677
|
: (mutation as any)
|
|
2670
|
-
const
|
|
2678
|
+
const resolveCallable = (): StreamMutationCallable<Id, Arg, A, E, R> =>
|
|
2679
|
+
(isStreamFactory(source)
|
|
2680
|
+
? (source as StreamMutationFactory<Id, Arg, A, E, R>)()
|
|
2681
|
+
: source) as StreamMutationCallable<Id, Arg, A, E, R>
|
|
2671
2682
|
return Object.assign(
|
|
2672
2683
|
(...combinators: any[]): any => {
|
|
2673
2684
|
// we capture the definition stack here, so we can append it to later stack traces
|
|
@@ -2676,15 +2687,14 @@ export class CommanderImpl<RT, RTHooks> {
|
|
|
2676
2687
|
const errorDef = new Error()
|
|
2677
2688
|
Error.stackTraceLimit = limit
|
|
2678
2689
|
|
|
2679
|
-
// Fresh per build:
|
|
2680
|
-
// wrap call gets its own
|
|
2690
|
+
// Fresh per build: invoke the factory once per command instance so each
|
|
2691
|
+
// wrap call gets its own state + execute pair. `running`/`progress`
|
|
2681
2692
|
// are only surfaced when the factory was called with a `progress` formatter.
|
|
2682
|
-
const
|
|
2683
|
-
const
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
const streamMeta = { running: tuple.running, progress: tuple.progress }
|
|
2693
|
+
const callable = resolveCallable()
|
|
2694
|
+
const mutate: (_arg: Arg) => Effect.Effect<any, E, R> = Effect.isEffect(callable)
|
|
2695
|
+
? (_arg: Arg) => callable
|
|
2696
|
+
: callable as (arg: Arg) => Effect.Effect<any, E, R>
|
|
2697
|
+
const streamMeta = { running: callable.running, progress: callable.progress }
|
|
2688
2698
|
|
|
2689
2699
|
return this.makeCommand(id, options, errorDef, streamMeta)(
|
|
2690
2700
|
Effect.fnUntraced(
|
package/src/makeClient.ts
CHANGED
|
@@ -237,33 +237,36 @@ export type MutateStreamCallOptions<A, E> = {
|
|
|
237
237
|
|
|
238
238
|
/**
|
|
239
239
|
* The `mutateStream` factory for a stream-type request handler. Always invoke
|
|
240
|
-
* (optionally with `{ progress }`) to get a fresh `
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
240
|
+
* (optionally with `{ progress }`) to get a fresh callable `execute` — each call
|
|
241
|
+
* produces a new state + execute pair so independent invocations don't share
|
|
242
|
+
* state. The callable updates its underlying ref live with each emitted value
|
|
243
|
+
* and carries `id`, plus `running` and `progress` when the factory was called
|
|
244
|
+
* with a `progress` formatter. When the request declares a `final` schema,
|
|
245
|
+
* the callable resolves with the last emitted value typed as `Final`; otherwise
|
|
246
|
+
* it resolves with the success type. The factory itself carries the request
|
|
247
|
+
* `id` so it can be passed to `Command.fn` / `Command.wrapStream` directly.
|
|
247
248
|
*/
|
|
248
249
|
export type StreamMutationWithExtensions<Req> = Req extends
|
|
249
250
|
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id, infer Final> ?
|
|
250
251
|
& ((options?: MutateStreamCallOptions<A, E>) =>
|
|
251
|
-
&
|
|
252
|
+
& ((input: I) => Effect.Effect<Final, E, R>)
|
|
252
253
|
& {
|
|
253
254
|
readonly id: Id
|
|
255
|
+
readonly _streamCallable: true
|
|
254
256
|
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
255
257
|
readonly progress?: ComputedRef<Progress | undefined>
|
|
256
258
|
})
|
|
257
|
-
& { readonly id: Id }
|
|
259
|
+
& { readonly id: Id; readonly _streamFactory: true }
|
|
258
260
|
: Req extends RequestStreamHandler<infer A, infer E, infer R, infer _Request, infer Id, infer Final> ?
|
|
259
261
|
& ((options?: MutateStreamCallOptions<A, E>) =>
|
|
260
|
-
&
|
|
262
|
+
& Effect.Effect<Final, E, R>
|
|
261
263
|
& {
|
|
262
264
|
readonly id: Id
|
|
265
|
+
readonly _streamCallable: true
|
|
263
266
|
readonly running?: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
264
267
|
readonly progress?: ComputedRef<Progress | undefined>
|
|
265
268
|
})
|
|
266
|
-
& { readonly id: Id }
|
|
269
|
+
& { readonly id: Id; readonly _streamFactory: true }
|
|
267
270
|
: never
|
|
268
271
|
|
|
269
272
|
/**
|
|
@@ -841,20 +844,21 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
841
844
|
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
842
845
|
const smFactory = Object.assign(
|
|
843
846
|
(opts?: { progress?: (result: AsyncResult.AsyncResult<any, any>) => Progress | undefined }) => {
|
|
844
|
-
const
|
|
847
|
+
const [resultRef, execute] = streamMutation(client[key] as any, mergedInvalidation)
|
|
845
848
|
const extras: {
|
|
846
849
|
id: string
|
|
850
|
+
_streamCallable: true
|
|
847
851
|
running?: ComputedRef<AsyncResult.AsyncResult<any, any>>
|
|
848
852
|
progress?: ComputedRef<Progress | undefined>
|
|
849
|
-
} = { id: client[key].id }
|
|
853
|
+
} = { id: client[key].id, _streamCallable: true }
|
|
850
854
|
if (opts?.progress) {
|
|
851
855
|
const fmt = opts.progress
|
|
852
|
-
extras.running =
|
|
853
|
-
extras.progress = computed(() => fmt(
|
|
856
|
+
extras.running = resultRef
|
|
857
|
+
extras.progress = computed(() => fmt(resultRef.value))
|
|
854
858
|
}
|
|
855
|
-
return Object.assign(
|
|
859
|
+
return Object.assign(execute, extras)
|
|
856
860
|
},
|
|
857
|
-
{ id: client[key].id }
|
|
861
|
+
{ id: client[key].id, _streamFactory: true as const }
|
|
858
862
|
)
|
|
859
863
|
;(acc as any)[camelCase(key) + "Stream"] = Object.assign(smFactory, {
|
|
860
864
|
fn: Command.fn(client[key].id)
|
|
@@ -938,20 +942,21 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
938
942
|
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
939
943
|
const streamMutFactory = Object.assign(
|
|
940
944
|
(opts?: { progress?: (result: AsyncResult.AsyncResult<any, any>) => Progress | undefined }) => {
|
|
941
|
-
const
|
|
945
|
+
const [resultRef, execute] = streamMutation(client[key] as any, mergedInvalidation)
|
|
942
946
|
const extras: {
|
|
943
947
|
id: string
|
|
948
|
+
_streamCallable: true
|
|
944
949
|
running?: ComputedRef<AsyncResult.AsyncResult<any, any>>
|
|
945
950
|
progress?: ComputedRef<Progress | undefined>
|
|
946
|
-
} = { id: client[key].id }
|
|
951
|
+
} = { id: client[key].id, _streamCallable: true }
|
|
947
952
|
if (opts?.progress) {
|
|
948
953
|
const fmt = opts.progress
|
|
949
|
-
extras.running =
|
|
950
|
-
extras.progress = computed(() => fmt(
|
|
954
|
+
extras.running = resultRef
|
|
955
|
+
extras.progress = computed(() => fmt(resultRef.value))
|
|
951
956
|
}
|
|
952
|
-
return Object.assign(
|
|
957
|
+
return Object.assign(execute, extras)
|
|
953
958
|
},
|
|
954
|
-
{ id: client[key].id }
|
|
959
|
+
{ id: client[key].id, _streamFactory: true as const }
|
|
955
960
|
)
|
|
956
961
|
return {
|
|
957
962
|
...client[key],
|
package/src/mutate.ts
CHANGED
|
@@ -421,6 +421,7 @@ export const useMakeMutation = () => {
|
|
|
421
421
|
*
|
|
422
422
|
* When the request declares a `final` schema, `execute` resolves with the last emitted value
|
|
423
423
|
* typed as `Final`; otherwise it resolves with the last emitted value typed as the success type.
|
|
424
|
+
* Stream failures bubble through the execute effect's typed error channel `E`.
|
|
424
425
|
*
|
|
425
426
|
* Must be called inside a Vue setup context (uses `useQueryClient` internally).
|
|
426
427
|
*/
|
|
@@ -437,7 +438,7 @@ export const makeStreamMutation = () => {
|
|
|
437
438
|
) => {
|
|
438
439
|
const state = shallowRef<AsyncResult.AsyncResult<any, any>>(AsyncResult.initial())
|
|
439
440
|
|
|
440
|
-
const runStream = (stream: Stream.Stream<any, any, any>, input?: unknown): Effect.Effect<any,
|
|
441
|
+
const runStream = (stream: Stream.Stream<any, any, any>, input?: unknown): Effect.Effect<any, any, any> => {
|
|
441
442
|
const invCache = buildInvalidateCache(queryClient, self, mergedInvalidation)
|
|
442
443
|
const keysRef = Ref.makeUnsafe<ReadonlyArray<InvalidationKey>>([])
|
|
443
444
|
// V3: pass onAdded so each mid-stream metadata chunk triggers query
|
|
@@ -476,11 +477,14 @@ export const makeStreamMutation = () => {
|
|
|
476
477
|
const lastValue = AsyncResult.isSuccess(current) ? current.value : undefined
|
|
477
478
|
const invExit = exit._tag === "Success" ? Exit.succeed(lastValue) : exit
|
|
478
479
|
const serverKeys = Ref.getUnsafe(keysRef)
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
480
|
+
// Stream failures bubble through the execute effect's typed error
|
|
481
|
+
// channel. The reactive `state` ref still mirrors the failure as
|
|
482
|
+
// `AsyncResult.failure` for live progress UI.
|
|
483
|
+
return invCache(input, invExit, serverKeys).pipe(
|
|
484
|
+
Effect.flatMap(() =>
|
|
485
|
+
exit._tag === "Success" ? Effect.succeed(lastValue) : Effect.failCause(exit.cause)
|
|
486
|
+
)
|
|
487
|
+
)
|
|
484
488
|
})
|
|
485
489
|
)
|
|
486
490
|
)
|
package/test/makeClient.test.ts
CHANGED
|
@@ -278,8 +278,8 @@ it.skip("stream final type tests", () => {
|
|
|
278
278
|
const { clientFor } = useClient()
|
|
279
279
|
const client = clientFor(Something, undefined, somethingInvalidationResources)
|
|
280
280
|
|
|
281
|
-
const
|
|
282
|
-
const
|
|
281
|
+
const execNoFinal = client.StreamWithoutFinal.mutateStream()
|
|
282
|
+
const execWithFinal = client.StreamWithFinal.mutateStream()
|
|
283
283
|
|
|
284
284
|
// Without `final`: execute input is {id: string} and resolves with void
|
|
285
285
|
const _execNoFinalResult: ReturnType<typeof execNoFinal> = execNoFinal({ id: "test" })
|
package/test/streamFinal.test.ts
CHANGED
|
@@ -47,7 +47,7 @@ it.skip("mutateStream without final: execute resolves void (type-level)", () =>
|
|
|
47
47
|
const { clientFor } = useClient()
|
|
48
48
|
const client = clientFor(Something, undefined, somethingInvalidationResources)
|
|
49
49
|
|
|
50
|
-
const
|
|
50
|
+
const execute = client.StreamWithoutFinal.mutateStream()
|
|
51
51
|
|
|
52
52
|
// execute returns void — assigning to ExportComplete Effect should fail
|
|
53
53
|
const result = execute({ id: "test" })
|
|
@@ -64,7 +64,7 @@ it.skip("mutateStream with final: execute resolves with ExportComplete (type-lev
|
|
|
64
64
|
const { clientFor } = useClient()
|
|
65
65
|
const client = clientFor(Something, undefined, somethingInvalidationResources)
|
|
66
66
|
|
|
67
|
-
const
|
|
67
|
+
const execute = client.StreamWithFinal.mutateStream()
|
|
68
68
|
|
|
69
69
|
// execute returns ExportComplete — assignment should compile cleanly
|
|
70
70
|
const result = execute({ id: "test" })
|