@effect-app/vue 4.0.0-beta.98 → 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 +2707 -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/makeClient.ts
CHANGED
|
@@ -1,157 +1,351 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { type InvalidateOptions, type InvalidateQueryFilters, isCancelledError, type QueryObserverResult, type RefetchOptions, type UseQueryReturnType } from "@tanstack/vue-query"
|
|
3
2
|
import { camelCase } from "change-case"
|
|
4
|
-
import { type Context, Effect, Exit, type Layer, type ManagedRuntime, Struct } from "effect-app"
|
|
5
3
|
import { type ApiClientFactory, type Req } from "effect-app/client"
|
|
6
|
-
import type { ExtractModuleName,
|
|
7
|
-
import {
|
|
4
|
+
import type { ExtractModuleName, HandlerInput, RequestHandlers, RequestHandlerWithInput, RequestsAny, RequestStreamHandlerWithInput } from "effect-app/client/clientFor"
|
|
5
|
+
import type { CauseException } from "effect-app/client/errors"
|
|
6
|
+
import type { InvalidationCallback } from "effect-app/client/makeClient"
|
|
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 S from "effect-app/Schema"
|
|
11
|
+
import * as Cause from "effect/Cause"
|
|
12
|
+
import * as Data from "effect/Data"
|
|
13
|
+
import * as Exit from "effect/Exit"
|
|
8
14
|
import { type Fiber } from "effect/Fiber"
|
|
9
|
-
import * as
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import { type
|
|
16
|
-
import {
|
|
17
|
-
import { type
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
import * as Hash from "effect/Hash"
|
|
16
|
+
import type * as ManagedRuntime from "effect/ManagedRuntime"
|
|
17
|
+
import type * as AsyncResult from "effect/reactivity/AsyncResult"
|
|
18
|
+
import * as Reactivity from "effect/reactivity/Reactivity"
|
|
19
|
+
import type * as Stream from "effect/Stream"
|
|
20
|
+
import * as Struct from "effect/Struct"
|
|
21
|
+
import { type ComputedRef, effectScope, onBeforeUnmount, onScopeDispose, ref, type WatchSource } from "vue"
|
|
22
|
+
import { type AtomClientRuntime, invalidateAndAwait, invalidateSoft, makeAtomClientRuntime } from "./atomQuery.ts"
|
|
23
|
+
import { type Commander, CommanderStatic, type Progress } from "./commander.ts"
|
|
24
|
+
import { makeTanstackQuery, makeTanstackQueryCacheUpdater, makeTanstackQueryClient, makeTanstackQueryInvalidator } from "./internal/tanstackQuery.ts"
|
|
25
|
+
import { type I18n } from "./intl.ts"
|
|
26
|
+
import { type CommanderResolved, makeUseCommand } from "./makeUseCommand.ts"
|
|
27
|
+
import { atomQueryInvalidator, combineQueryInvalidators, type InvalidationEntry, makeMutation, makeStreamMutation2, type MutationOptionsBase, type QueryInvalidator, useMakeMutation } from "./mutate.ts"
|
|
28
|
+
import { atomQueryCacheUpdater, type AtomQueryNewOptions, combineQueryCacheUpdaters, type CustomUndefinedInitialQueryOptions, makeQuery, makeQueryAtom, makeQueryFamily, makeQueryNew, makeStreamQuery, makeStreamQueryAtom, makeStreamQueryFamily, makeStreamQueryNew, optionalAtomQueryCacheUpdater, type QueryObserverResult, type RefetchOptions, setQueryCacheUpdater, type StreamQueryAtomFamily, type SuspenseQueryView, type UseQueryReturnType } from "./query.ts"
|
|
29
|
+
import { makeRunPromise } from "./runtime.ts"
|
|
30
|
+
import { latestDefined } from "./suspense.ts"
|
|
31
|
+
import { type Toast } from "./toast.ts"
|
|
32
|
+
|
|
33
|
+
export type { Progress }
|
|
34
|
+
|
|
35
|
+
const useScopedSuspenseSetup = <A>(setup: () => A) => {
|
|
36
|
+
const scope = effectScope()
|
|
37
|
+
const controller = new AbortController()
|
|
38
|
+
const value = scope.run(setup)
|
|
39
|
+
if (value === undefined) {
|
|
40
|
+
throw new Error("Internal Error: suspense setup scope did not initialize")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isMounted = ref(true)
|
|
44
|
+
let stopped = false
|
|
45
|
+
const stop = () => {
|
|
46
|
+
if (stopped) return
|
|
47
|
+
stopped = true
|
|
48
|
+
isMounted.value = false
|
|
49
|
+
controller.abort()
|
|
50
|
+
scope.stop()
|
|
51
|
+
}
|
|
52
|
+
onBeforeUnmount(stop)
|
|
53
|
+
onScopeDispose(stop)
|
|
54
|
+
|
|
55
|
+
return [value, isMounted, controller.signal] as const
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A suspense query settled with an interrupt-only cause while its component was still MOUNTED.
|
|
60
|
+
*
|
|
61
|
+
* That combination classifies the interrupt's origin: a navigation/unmount cancel interrupts the
|
|
62
|
+
* suspense *fiber* (via the scope's AbortSignal) or is hidden by effect-core before it can settle —
|
|
63
|
+
* it never yields a still-mounted `Failure` exit. A `Failure(interrupt-only)` observed while mounted
|
|
64
|
+
* therefore came from the QUERY side — e.g. the server returned an RPC interrupt `Exit` (over a 200),
|
|
65
|
+
* or the fetch was superseded — and silencing it leaves the user staring at a blank page.
|
|
66
|
+
*
|
|
67
|
+
* Error boundaries can rely on this split: keep silencing raw interrupt-only causes (those are
|
|
68
|
+
* genuinely "user navigated away"), and render this error as "the request was interrupted — reload?".
|
|
69
|
+
*/
|
|
70
|
+
export class SuspenseInterruptedError extends Data.TaggedError("SuspenseInterruptedError")<{
|
|
71
|
+
readonly originalCause: Cause.Cause<unknown>
|
|
72
|
+
readonly source: "suspense" | "suspenseNew"
|
|
73
|
+
}> {
|
|
74
|
+
override get message() {
|
|
75
|
+
return "The request was interrupted while the page was still active"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Structural check (by `_tag`) so consumers compiled against older published types can detect it. */
|
|
80
|
+
export const isSuspenseInterruptedError = (e: unknown): e is SuspenseInterruptedError =>
|
|
81
|
+
typeof e === "object" && e !== null && "_tag" in e && e._tag === "SuspenseInterruptedError"
|
|
82
|
+
|
|
83
|
+
// TODO: optimize - work from encoded shape directly
|
|
84
|
+
const projectHandler = <
|
|
85
|
+
I,
|
|
86
|
+
A,
|
|
87
|
+
E,
|
|
88
|
+
R,
|
|
89
|
+
SuccessSchema extends S.Top & { readonly "EncodingServices": R },
|
|
90
|
+
ProjSchema extends S.Top & { readonly "DecodingServices": R }
|
|
91
|
+
>(
|
|
92
|
+
handler: (i: I) => Effect.Effect<A, E, R>,
|
|
93
|
+
successSchema: SuccessSchema,
|
|
94
|
+
projectionSchema: ProjSchema
|
|
95
|
+
) => {
|
|
96
|
+
const encode = S.encodeUnknownEffect(successSchema)
|
|
97
|
+
const decode = S.decodeUnknownEffect(projectionSchema)
|
|
98
|
+
return (i: I) => handler(i).pipe(Effect.flatMap(encode), Effect.flatMap(decode))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const projectionSchemaHash = (schema: S.Top) => String(Hash.hash(schema.ast))
|
|
102
|
+
|
|
103
|
+
export interface CommandRequestExtensions<RT, Id extends string, I, A, E, R> {
|
|
25
104
|
/** Defines a Command based on this call, taking the `id` of the call as the `id` of the Command.
|
|
26
105
|
* The Request function will be taken as the first member of the Command, the Command required input will be the Request input.
|
|
27
106
|
* see Command.wrap for details */
|
|
28
|
-
wrap:
|
|
107
|
+
wrap: <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
108
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
109
|
+
) => Commander.CommanderWrap<RT, Id, I18nKey, State, I, A, E, R>
|
|
29
110
|
/** Defines a Command based on this call, taking the `id` of the call as the `id` of the Command.
|
|
30
111
|
* see Command.fn for details */
|
|
31
|
-
fn:
|
|
112
|
+
fn: <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
113
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
114
|
+
) => Commander.CommanderFn<RT, Id, I18nKey, State>
|
|
32
115
|
}
|
|
33
116
|
|
|
34
117
|
/** my other doc */
|
|
35
|
-
export interface
|
|
118
|
+
export interface RequestExt<
|
|
36
119
|
RT,
|
|
37
120
|
Id extends string,
|
|
38
121
|
I,
|
|
39
122
|
A,
|
|
40
123
|
E,
|
|
41
124
|
R
|
|
42
|
-
> extends
|
|
125
|
+
> extends
|
|
126
|
+
Commander.CommandContextLocal<Id, Id>,
|
|
127
|
+
Commander.CommanderWrap<RT, Id, Id, undefined, I, A, E, R>,
|
|
128
|
+
CommandRequestExtensions<RT, Id, I, A, E, R>
|
|
129
|
+
{
|
|
43
130
|
/**
|
|
44
|
-
*
|
|
131
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
132
|
+
* This does not perform query cache invalidation.
|
|
45
133
|
*/
|
|
46
|
-
(i: I)
|
|
134
|
+
request: (i: I) => Effect.Effect<A, E, R>
|
|
47
135
|
}
|
|
48
136
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
>
|
|
59
|
-
Commander.CommandContextLocal<Id, Id>,
|
|
60
|
-
Commander.CommanderWrap<RT, Id, Id, undefined, void, A, E, R>,
|
|
61
|
-
RequestExtensions<RT, Id, void, A, E, R>,
|
|
62
|
-
Effect.Effect<A, E, R>
|
|
63
|
-
{
|
|
137
|
+
export type CommandRequestWithExtensions<RT, Req> = Req extends
|
|
138
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id> ? RequestExt<RT, Id, I, A, E, R>
|
|
139
|
+
: never
|
|
140
|
+
|
|
141
|
+
export interface QueryExtensions<I, A, E, R> {
|
|
142
|
+
/**
|
|
143
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
144
|
+
* This does not set up query state tracking.
|
|
145
|
+
*/
|
|
146
|
+
request: (i: I) => Effect.Effect<A, E, R>
|
|
64
147
|
}
|
|
65
148
|
|
|
66
|
-
export type
|
|
67
|
-
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer
|
|
68
|
-
|
|
69
|
-
|
|
149
|
+
export type QueryRequestWithExtensions<Req> = Req extends
|
|
150
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer _Id> ? QueryExtensions<I, A, E, R>
|
|
151
|
+
: never
|
|
152
|
+
|
|
153
|
+
type QueryHandler<Req> = Req extends
|
|
154
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
155
|
+
? Request["type"] extends "query" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
|
|
156
|
+
: never
|
|
157
|
+
|
|
158
|
+
type CommandHandler<Req> = Req extends
|
|
159
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
160
|
+
? Request["type"] extends "command" ? RequestHandlerWithInput<I, A, E, R, Request, Id> : never
|
|
161
|
+
: never
|
|
162
|
+
|
|
163
|
+
type QueryStreamHandler<Req> = Req extends
|
|
164
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id, infer Final>
|
|
165
|
+
? [Request["stream"], Request["type"]] extends [true, "query"]
|
|
166
|
+
? RequestStreamHandlerWithInput<I, A, E, R, Request, Id, Final>
|
|
167
|
+
: never
|
|
168
|
+
: never
|
|
169
|
+
|
|
170
|
+
type CommandStreamHandler<Req> = Req extends
|
|
171
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id, infer Final>
|
|
172
|
+
? [Request["stream"], Request["type"]] extends [true, "command"]
|
|
173
|
+
? RequestStreamHandlerWithInput<I, A, E, R, Request, Id, Final>
|
|
174
|
+
: never
|
|
70
175
|
: never
|
|
71
176
|
|
|
72
177
|
export interface MutationExtensions<RT, Id extends string, I, A, E, R> {
|
|
73
178
|
/** Defines a Command based on this mutation, taking the `id` of the mutation as the `id` of the Command.
|
|
74
179
|
* The Mutation function will be taken as the first member of the Command, the Command required input will be the Mutation input.
|
|
75
180
|
* see Command.wrap for details */
|
|
76
|
-
wrap:
|
|
77
|
-
|
|
181
|
+
wrap: <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
182
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
183
|
+
) => Commander.CommanderWrap<RT, Id, I18nKey, State, I, A, E, R>
|
|
184
|
+
/** Defines a Command based on this call, taking the `id` of the mutation as the `id` of the Command.
|
|
78
185
|
* see Command.fn for details */
|
|
79
|
-
fn:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/** my other doc */
|
|
83
|
-
export interface MutationExtWithInput<
|
|
84
|
-
RT,
|
|
85
|
-
Id extends string,
|
|
86
|
-
I,
|
|
87
|
-
A,
|
|
88
|
-
E,
|
|
89
|
-
R
|
|
90
|
-
> extends Commander.CommandContextLocal<Id, Id>, MutationExtensions<RT, Id, I, A, E, R> {
|
|
91
|
-
/**
|
|
92
|
-
* Call the endpoint with input
|
|
93
|
-
* Invalidate queries based on namespace of this mutation.
|
|
94
|
-
* Do not use for queries.
|
|
95
|
-
*/
|
|
96
|
-
(i: I): Effect.Effect<A, E, R>
|
|
186
|
+
fn: <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
187
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
188
|
+
) => Commander.CommanderFn<RT, Id, I18nKey, State>
|
|
97
189
|
}
|
|
98
190
|
|
|
99
191
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
192
|
+
* Send the request to the endpoint and return the raw Effect response.
|
|
193
|
+
* Also invalidates query caches using the request namespace by default.
|
|
194
|
+
* Namespace invalidation targets parent namespace keys
|
|
195
|
+
* (for example `$project/$configuration.get` invalidates `$project`).
|
|
196
|
+
* Override invalidation in client options via `queryInvalidation`.
|
|
197
|
+
*
|
|
198
|
+
* Pass `options` to attach a `select` Effect that runs after the mutation
|
|
199
|
+
* succeeds (its output is returned to the caller) and/or override the default
|
|
200
|
+
* `queryInvalidation`.
|
|
201
|
+
*
|
|
202
|
+
* When `I = void` the input argument may be omitted.
|
|
103
203
|
*/
|
|
104
204
|
export interface MutationExt<
|
|
105
205
|
RT,
|
|
106
206
|
Id extends string,
|
|
207
|
+
I,
|
|
107
208
|
A,
|
|
108
209
|
E,
|
|
109
|
-
R
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
210
|
+
R,
|
|
211
|
+
EA = unknown
|
|
212
|
+
> extends MutationExtensions<RT, Id, I, A, E, R> {
|
|
213
|
+
<B = A, E2 = never, R2 = never>(
|
|
214
|
+
input: I,
|
|
215
|
+
options?: MutationOptionsBase<A, B, E2, R2>
|
|
216
|
+
): Effect.Effect<B, E | E2, R | R2>
|
|
217
|
+
|
|
218
|
+
project: <ProjSchema extends S.Top>(
|
|
219
|
+
schema: EA extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
220
|
+
) => MutationExt<
|
|
221
|
+
RT,
|
|
222
|
+
Id,
|
|
223
|
+
I,
|
|
224
|
+
ProjSchema["Type"],
|
|
225
|
+
E | S.SchemaError,
|
|
226
|
+
R | ProjSchema["DecodingServices"],
|
|
227
|
+
ProjSchema["Encoded"]
|
|
228
|
+
>
|
|
116
229
|
}
|
|
117
230
|
|
|
118
231
|
export type MutationWithExtensions<RT, Req> = Req extends
|
|
119
|
-
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer
|
|
120
|
-
?
|
|
121
|
-
:
|
|
232
|
+
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
233
|
+
? MutationExt<RT, Id, I, A, E, R, Request["success"]["Encoded"]>
|
|
234
|
+
: never
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The `streamFn` builder for a stream-type request handler, using the stream-specific overloads.
|
|
238
|
+
*/
|
|
239
|
+
export type StreamFnStreamExtension<RT, Req> = Req extends
|
|
240
|
+
RequestStreamHandlerWithInput<infer _I, infer _A, infer _E, infer _R, infer _Request, infer Id, infer _Final>
|
|
241
|
+
? <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
242
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
243
|
+
) => Commander.StreamGen<RT, Id, I18nKey, State> & Commander.NonGenStream<RT, Id, I18nKey, State>
|
|
244
|
+
: never
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* `mutate` factory — wraps per-invocation invalidation scaffolding
|
|
248
|
+
* into the stream itself (via `Stream.unwrap`) for use with `streamFn` combinators.
|
|
249
|
+
*/
|
|
250
|
+
export type StreamMutation2WithExtensions<RT, Req> = Req extends
|
|
251
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer _Request, infer Id, infer _Final> ?
|
|
252
|
+
& ((input: I) => Stream.Stream<A, E, R>)
|
|
253
|
+
& {
|
|
254
|
+
readonly id: Id
|
|
255
|
+
readonly wrap: <I18nKey extends string = Id, State extends Commander.IntlRecord | undefined = undefined>(
|
|
256
|
+
options?: Commander.FnOptions<Id, I18nKey, State>
|
|
257
|
+
) => Commander.StreamerWrap<RT, Id, I18nKey, State, I, A, E, R>
|
|
258
|
+
}
|
|
122
259
|
: never
|
|
123
260
|
|
|
124
261
|
// we don't really care about the RT, as we are in charge of ensuring runtime safety anyway
|
|
125
262
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
126
263
|
declare const useQuery_: QueryImpl<any>["useQuery"]
|
|
127
264
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
265
|
+
declare const useQueryNew_: QueryImpl<any>["useQueryNew"]
|
|
266
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
267
|
+
declare const useQueryAtom_: QueryImpl<any>["useQueryAtom"]
|
|
268
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
269
|
+
declare const useQueryFamily_: QueryImpl<any>["useQueryFamily"]
|
|
270
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
271
|
+
declare const useSuspenseQueryNew_: QueryImpl<any>["useSuspenseQueryNew"]
|
|
272
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
128
273
|
declare const useSuspenseQuery_: QueryImpl<any>["useSuspenseQuery"]
|
|
274
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
275
|
+
declare const useStreamQuery_: QueryImpl<any>["useStreamQuery"]
|
|
276
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
277
|
+
declare const useStreamQueryFamily_: QueryImpl<any>["useStreamQueryFamily"]
|
|
278
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
279
|
+
declare const useStreamQueryAtom_: QueryImpl<any>["useStreamQueryAtom"]
|
|
280
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
281
|
+
declare const useStreamQueryNew_: QueryImpl<any>["useStreamQueryNew"]
|
|
282
|
+
|
|
283
|
+
export interface ProjectResult<RT, I, B, E, R, Request extends Req, Id extends string> {
|
|
284
|
+
request: (i: I) => Effect.Effect<B, E, R>
|
|
285
|
+
family: Exclude<R, RT> extends never ? ReturnType<typeof useQueryFamily_<I, E, B, Request, Id>>
|
|
286
|
+
: MissingDependencies<RT, R> & {}
|
|
287
|
+
query: Exclude<R, RT> extends never ? ReturnType<typeof useQuery_<I, E, B, Request, Id>>
|
|
288
|
+
: MissingDependencies<RT, R> & {}
|
|
289
|
+
queryNew: Exclude<R, RT> extends never ? ReturnType<typeof useQueryNew_<I, E, B, Request, Id>>
|
|
290
|
+
: MissingDependencies<RT, R> & {}
|
|
291
|
+
suspense: Exclude<R, RT> extends never ? ReturnType<typeof useSuspenseQuery_<I, E, B, Request, Id>>
|
|
292
|
+
: MissingDependencies<RT, R> & {}
|
|
293
|
+
suspenseNew: Exclude<R, RT> extends never ? ReturnType<typeof useSuspenseQueryNew_<I, E, B, Request, Id>>
|
|
294
|
+
: MissingDependencies<RT, R> & {}
|
|
295
|
+
atom: Exclude<R, RT> extends never ? ReturnType<typeof useQueryAtom_<I, E, B, Request, Id>>
|
|
296
|
+
: MissingDependencies<RT, R> & {}
|
|
297
|
+
}
|
|
129
298
|
|
|
130
|
-
export
|
|
299
|
+
export type QueryProjection<RT, HandlerReq> = HandlerReq extends
|
|
300
|
+
RequestHandlerWithInput<infer I, infer _A, infer E, infer R, infer Request, infer Id>
|
|
301
|
+
? Request["type"] extends "query" ? {
|
|
302
|
+
project: <ProjSchema extends S.Top>(
|
|
303
|
+
schema: Request["success"]["Encoded"] extends ProjSchema["Encoded"] ? ProjSchema : never
|
|
304
|
+
) => ProjectResult<
|
|
305
|
+
RT,
|
|
306
|
+
I,
|
|
307
|
+
ProjSchema["Type"],
|
|
308
|
+
E | S.SchemaError,
|
|
309
|
+
R | ProjSchema["DecodingServices"],
|
|
310
|
+
Request,
|
|
311
|
+
Id
|
|
312
|
+
>
|
|
313
|
+
}
|
|
314
|
+
: {}
|
|
315
|
+
: {}
|
|
316
|
+
|
|
317
|
+
export interface QueryResultExtensions<Request extends Req, Id extends string, I, A, E> {
|
|
131
318
|
/**
|
|
132
|
-
*
|
|
319
|
+
* Read helper for query requests.
|
|
320
|
+
* Runs as a tracked Vue Query and returns reactive state.
|
|
321
|
+
* Queries read state and should not be used to mutate it.
|
|
322
|
+
* When `I = void` the input argument may be omitted.
|
|
133
323
|
*/
|
|
134
324
|
query: ReturnType<typeof useQuery_<I, E, A, Request, Id>>
|
|
325
|
+
/**
|
|
326
|
+
* Atom-native query helper with object return shape.
|
|
327
|
+
* Additive migration surface; `.query()` remains the compatibility tuple API.
|
|
328
|
+
*/
|
|
329
|
+
queryNew: ReturnType<typeof useQueryNew_<I, E, A, Request, Id>>
|
|
135
330
|
// TODO or suspense as Option?
|
|
136
331
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* So that Suspense and error boundaries can be used.
|
|
332
|
+
* Like `.query`, but returns a Promise for setup-time awaiting.
|
|
333
|
+
* Use this when integrating with Vue Suspense / error boundaries.
|
|
140
334
|
*/
|
|
141
335
|
suspense: ReturnType<typeof useSuspenseQuery_<I, E, A, Request, Id>>
|
|
142
|
-
}
|
|
143
|
-
export interface QueriesWithoutInput<Request extends Req, Id extends string, A, E> {
|
|
144
336
|
/**
|
|
145
|
-
*
|
|
337
|
+
* Promise-based setup helper with object return shape.
|
|
338
|
+
* Additive migration surface; `.suspense()` remains the compatibility tuple API.
|
|
146
339
|
*/
|
|
147
|
-
|
|
148
|
-
// TODO or suspense as Option?
|
|
340
|
+
suspenseNew: ReturnType<typeof useSuspenseQueryNew_<I, E, A, Request, Id>>
|
|
149
341
|
/**
|
|
150
|
-
*
|
|
151
|
-
|
|
152
|
-
|
|
342
|
+
* Raw query atom for composition outside Vue refs.
|
|
343
|
+
*/
|
|
344
|
+
atom: ReturnType<typeof useQueryAtom_<I, E, A, Request, Id>>
|
|
345
|
+
/**
|
|
346
|
+
* Raw query atom family for composing query graphs before choosing a Vue observer.
|
|
153
347
|
*/
|
|
154
|
-
|
|
348
|
+
family: ReturnType<typeof useQueryFamily_<I, E, A, Request, Id>>
|
|
155
349
|
}
|
|
156
350
|
|
|
157
351
|
export type MissingDependencies<RT, R> = {
|
|
@@ -161,24 +355,59 @@ export type MissingDependencies<RT, R> = {
|
|
|
161
355
|
|
|
162
356
|
export type Queries<RT, Req> = Req extends
|
|
163
357
|
RequestHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id>
|
|
164
|
-
? Exclude<R, RT> extends never ?
|
|
358
|
+
? Request["type"] extends "query" ? Exclude<R, RT> extends never ? QueryResultExtensions<Request, Id, I, A, E>
|
|
359
|
+
: {
|
|
360
|
+
atom: MissingDependencies<RT, R> & {}
|
|
361
|
+
family: MissingDependencies<RT, R> & {}
|
|
362
|
+
query: MissingDependencies<RT, R> & {}
|
|
363
|
+
queryNew: MissingDependencies<RT, R> & {}
|
|
364
|
+
suspense: MissingDependencies<RT, R> & {}
|
|
365
|
+
suspenseNew: MissingDependencies<RT, R> & {}
|
|
366
|
+
}
|
|
367
|
+
: never
|
|
368
|
+
: never
|
|
369
|
+
|
|
370
|
+
export interface StreamQueryExtensions<Request extends Req, Id extends string, I, A, E> {
|
|
371
|
+
atom: ReturnType<typeof useStreamQueryAtom_<I, E, A, Request, Id>>
|
|
372
|
+
family: StreamQueryAtomFamily<I, A, E>
|
|
373
|
+
/**
|
|
374
|
+
* Stream helper for query-stream requests.
|
|
375
|
+
* Legacy compatibility helper. Collects the whole stream into an array before
|
|
376
|
+
* publishing data.
|
|
377
|
+
* When `I = void` the input argument may be omitted.
|
|
378
|
+
*/
|
|
379
|
+
query: ReturnType<typeof useStreamQuery_<I, E, A, Request, Id>>
|
|
380
|
+
/**
|
|
381
|
+
* Atom-native stream query helper. Exposes incremental pull state and a `pull`
|
|
382
|
+
* command instead of collecting the whole stream first.
|
|
383
|
+
*/
|
|
384
|
+
queryNew: ReturnType<typeof useStreamQueryNew_<I, E, A, Request, Id>>
|
|
385
|
+
}
|
|
386
|
+
export type StreamQueries<RT, HandlerReq> = HandlerReq extends
|
|
387
|
+
RequestStreamHandlerWithInput<infer I, infer A, infer E, infer R, infer Request, infer Id, infer _Final>
|
|
388
|
+
? Exclude<R, RT> extends never ? StreamQueryExtensions<Request, Id, I, A, E>
|
|
165
389
|
: {
|
|
390
|
+
atom: MissingDependencies<RT, R> & {}
|
|
391
|
+
family: MissingDependencies<RT, R> & {}
|
|
166
392
|
query: MissingDependencies<RT, R> & {}
|
|
167
|
-
|
|
393
|
+
queryNew: MissingDependencies<RT, R> & {}
|
|
168
394
|
}
|
|
169
|
-
: Req extends RequestHandler<infer A, infer E, infer R, infer Request, infer Id>
|
|
170
|
-
? Exclude<R, RT> extends never ? QueriesWithoutInput<Request, Id, A, E>
|
|
171
|
-
: { query: MissingDependencies<RT, R> & {}; suspense: MissingDependencies<RT, R> & {} }
|
|
172
395
|
: never
|
|
173
396
|
|
|
174
|
-
const _useMutation = makeMutation()
|
|
397
|
+
const _useMutation = makeMutation(atomQueryInvalidator)
|
|
398
|
+
|
|
399
|
+
const wrapWithSpan = (self: { id: string }, mut: any) => {
|
|
400
|
+
const span = (eff: Effect.Effect<any, any, any>) =>
|
|
401
|
+
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })(eff)
|
|
402
|
+
return (input: any, options?: MutationOptionsBase) => span(mut(input, options))
|
|
403
|
+
}
|
|
175
404
|
|
|
176
405
|
/**
|
|
177
406
|
* Pass an Effect or a function that returns an Effect, e.g from a client action
|
|
178
407
|
* Executes query cache invalidation based on default rules or provided option.
|
|
179
408
|
* adds a span with the mutation id
|
|
180
409
|
*/
|
|
181
|
-
export const useMutation: typeof _useMutation = <
|
|
410
|
+
export const useMutation: typeof _useMutation = (<
|
|
182
411
|
I,
|
|
183
412
|
E,
|
|
184
413
|
A,
|
|
@@ -186,25 +415,21 @@ export const useMutation: typeof _useMutation = <
|
|
|
186
415
|
Request extends Req,
|
|
187
416
|
Name extends string
|
|
188
417
|
>(
|
|
189
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
190
|
-
options?: MutationOptionsBase
|
|
418
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
191
419
|
) =>
|
|
192
420
|
Object.assign(
|
|
193
|
-
|
|
194
|
-
_useMutation(self as any, options),
|
|
195
|
-
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })
|
|
196
|
-
) as any,
|
|
421
|
+
wrapWithSpan(self, _useMutation(self as any)),
|
|
197
422
|
{ id: self.id }
|
|
198
|
-
)
|
|
423
|
+
)) as any
|
|
199
424
|
|
|
200
425
|
/**
|
|
201
426
|
* Pass an Effect or a function that returns an Effect, e.g from a client action
|
|
202
427
|
* Executes query cache invalidation based on default rules or provided option.
|
|
203
428
|
* adds a span with the mutation id
|
|
204
429
|
*/
|
|
205
|
-
export const useMutationInt = (): typeof _useMutation => {
|
|
206
|
-
const _useMutation = useMakeMutation()
|
|
207
|
-
return <
|
|
430
|
+
export const useMutationInt = (queryInvalidator: QueryInvalidator): typeof _useMutation => {
|
|
431
|
+
const _useMutation = useMakeMutation(queryInvalidator)
|
|
432
|
+
return (<
|
|
208
433
|
I,
|
|
209
434
|
E,
|
|
210
435
|
A,
|
|
@@ -212,23 +437,41 @@ export const useMutationInt = (): typeof _useMutation => {
|
|
|
212
437
|
Request extends Req,
|
|
213
438
|
Name extends string
|
|
214
439
|
>(
|
|
215
|
-
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
216
|
-
options?: MutationOptionsBase
|
|
440
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
217
441
|
) =>
|
|
218
442
|
Object.assign(
|
|
219
|
-
|
|
220
|
-
_useMutation(self as any, options),
|
|
221
|
-
Effect.withSpan(`mutation ${self.id}`, {}, { captureStackTrace: false })
|
|
222
|
-
) as any,
|
|
443
|
+
wrapWithSpan(self, _useMutation(self as any)),
|
|
223
444
|
{ id: self.id }
|
|
224
|
-
)
|
|
445
|
+
)) as any
|
|
225
446
|
}
|
|
226
447
|
|
|
227
448
|
export type ClientFrom<M extends RequestsAny> = RequestHandlers<never, never, M, ExtractModuleName<M>>
|
|
228
449
|
|
|
450
|
+
export interface MakeClientOptions {
|
|
451
|
+
/**
|
|
452
|
+
* Selects the engine behind legacy `.query()` / `.suspense()` helpers.
|
|
453
|
+
* Atom-native `.atom()` / `.family()` / `.queryNew()` / `.suspenseNew()` always use Atom.
|
|
454
|
+
*/
|
|
455
|
+
readonly legacyQueryEngine?: "atom" | "tanstack"
|
|
456
|
+
}
|
|
457
|
+
|
|
229
458
|
export class QueryImpl<R> {
|
|
230
|
-
|
|
231
|
-
|
|
459
|
+
readonly getRuntime: () => Context.Context<R>
|
|
460
|
+
|
|
461
|
+
constructor(
|
|
462
|
+
getRuntime: () => Context.Context<R>,
|
|
463
|
+
getAtomRt: () => AtomClientRuntime,
|
|
464
|
+
legacyUseQuery?: ReturnType<typeof makeQuery<R>>
|
|
465
|
+
) {
|
|
466
|
+
this.getRuntime = getRuntime
|
|
467
|
+
this.useQuery = legacyUseQuery ?? makeQuery(this.getRuntime, getAtomRt)
|
|
468
|
+
this.useQueryNew = makeQueryNew(this.getRuntime, getAtomRt)
|
|
469
|
+
this.useQueryAtom = makeQueryAtom(this.getRuntime, getAtomRt)
|
|
470
|
+
this.useQueryFamily = makeQueryFamily(this.getRuntime, getAtomRt)
|
|
471
|
+
this.useStreamQuery = makeStreamQuery(this.getRuntime, getAtomRt)
|
|
472
|
+
this.useStreamQueryFamily = makeStreamQueryFamily(this.getRuntime, getAtomRt)
|
|
473
|
+
this.useStreamQueryAtom = makeStreamQueryAtom(this.getRuntime, getAtomRt)
|
|
474
|
+
this.useStreamQueryNew = makeStreamQueryNew(this.getRuntime, getAtomRt)
|
|
232
475
|
}
|
|
233
476
|
/**
|
|
234
477
|
* Effect results are passed to the caller, including errors.
|
|
@@ -236,6 +479,24 @@ export class QueryImpl<R> {
|
|
|
236
479
|
*/
|
|
237
480
|
readonly useQuery: ReturnType<typeof makeQuery<R>>
|
|
238
481
|
|
|
482
|
+
readonly useQueryNew: ReturnType<typeof makeQueryNew<R>>
|
|
483
|
+
|
|
484
|
+
readonly useQueryAtom: ReturnType<typeof makeQueryAtom<R>>
|
|
485
|
+
|
|
486
|
+
readonly useQueryFamily: ReturnType<typeof makeQueryFamily<R>>
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Stream results are accumulated as an array of chunks and returned as reactive state.
|
|
490
|
+
* @deprecated use client helpers instead (.query())
|
|
491
|
+
*/
|
|
492
|
+
readonly useStreamQuery: ReturnType<typeof makeStreamQuery<R>>
|
|
493
|
+
|
|
494
|
+
readonly useStreamQueryFamily: ReturnType<typeof makeStreamQueryFamily<R>>
|
|
495
|
+
|
|
496
|
+
readonly useStreamQueryAtom: ReturnType<typeof makeStreamQueryAtom<R>>
|
|
497
|
+
|
|
498
|
+
readonly useStreamQueryNew: ReturnType<typeof makeStreamQueryNew<R>>
|
|
499
|
+
|
|
239
500
|
/**
|
|
240
501
|
* The difference with useQuery is that this function will return a Promise you can await in the Setup,
|
|
241
502
|
* which ensures that either there always is a latest value, or an error occurs on load.
|
|
@@ -247,22 +508,21 @@ export class QueryImpl<R> {
|
|
|
247
508
|
* The difference with useQuery is that this function will return a Promise you can await in the Setup,
|
|
248
509
|
* which ensures that either there always is a latest value, or an error occurs on load.
|
|
249
510
|
* So that Suspense and error boundaries can be used.
|
|
250
|
-
*
|
|
511
|
+
* When `I = void` the input argument may be omitted.
|
|
251
512
|
*/
|
|
252
513
|
<
|
|
514
|
+
I,
|
|
253
515
|
E,
|
|
254
516
|
A,
|
|
255
517
|
Request extends Req,
|
|
256
518
|
Name extends string
|
|
257
519
|
>(
|
|
258
|
-
self:
|
|
520
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
259
521
|
): {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
*/
|
|
265
|
-
<TData = A>(options?: CustomUndefinedInitialQueryOptions<A, E, TData>): Promise<
|
|
522
|
+
<TData = A>(
|
|
523
|
+
arg: I | WatchSource<I>,
|
|
524
|
+
options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
525
|
+
): Promise<
|
|
266
526
|
readonly [
|
|
267
527
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
268
528
|
ComputedRef<TData>,
|
|
@@ -273,85 +533,115 @@ export class QueryImpl<R> {
|
|
|
273
533
|
]
|
|
274
534
|
>
|
|
275
535
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
536
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
537
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
538
|
+
) => {
|
|
539
|
+
const runPromise = makeRunPromise(this.getRuntime())
|
|
540
|
+
const q = this.useQuery(self)
|
|
541
|
+
return <TData = A>(
|
|
542
|
+
argOrOptions: I | WatchSource<I>,
|
|
543
|
+
options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
544
|
+
) => {
|
|
545
|
+
const [
|
|
546
|
+
[resultRef, latestDefinedRef, fetch, uqrt],
|
|
547
|
+
isMounted,
|
|
548
|
+
signal
|
|
549
|
+
] = useScopedSuspenseSetup(() => {
|
|
550
|
+
const [resultRef, latestRef, fetch, uqrt] = q<TData>(argOrOptions, options)
|
|
551
|
+
const latestDefinedRef = latestDefined(() => latestRef.value, "suspense")
|
|
552
|
+
return [resultRef, latestDefinedRef, fetch, uqrt] as const
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
// @effect-diagnostics effect/missingEffectError:off
|
|
556
|
+
const eff = Effect.gen(function*() {
|
|
557
|
+
const exit = yield* uqrt.awaitResult().pipe(Effect.exit)
|
|
558
|
+
if (!isMounted.value) {
|
|
559
|
+
return yield* Effect.interrupt
|
|
560
|
+
}
|
|
561
|
+
if (Exit.isFailure(exit)) {
|
|
562
|
+
// Still mounted, so this interrupt came from the query side (e.g. a server RPC interrupt
|
|
563
|
+
// Exit), not from navigation/unmount — surface it as a renderable typed error instead of a
|
|
564
|
+
// raw interrupt the error boundary would silence into a blank page.
|
|
565
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
566
|
+
return yield* new SuspenseInterruptedError({ originalCause: exit.cause, source: "suspense" })
|
|
567
|
+
}
|
|
568
|
+
return yield* Exit.failCause(exit.cause)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return [resultRef, latestDefinedRef, fetch, uqrt] as const
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
return runPromise(eff, { signal })
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
readonly useSuspenseQueryNew: {
|
|
281
579
|
<
|
|
282
|
-
|
|
580
|
+
I,
|
|
283
581
|
E,
|
|
284
582
|
A,
|
|
285
583
|
Request extends Req,
|
|
286
584
|
Name extends string
|
|
287
585
|
>(
|
|
288
|
-
self: RequestHandlerWithInput<
|
|
586
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
289
587
|
): {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
*/
|
|
295
|
-
<TData = A>(arg: Arg | WatchSource<Arg>, options?: CustomUndefinedInitialQueryOptions<A, E, TData>): Promise<
|
|
296
|
-
readonly [
|
|
297
|
-
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
298
|
-
ComputedRef<TData>,
|
|
299
|
-
(
|
|
300
|
-
options?: RefetchOptions
|
|
301
|
-
) => Effect.Effect<QueryObserverResult<TData, E>>,
|
|
302
|
-
UseQueryReturnType<any, any>
|
|
303
|
-
]
|
|
304
|
-
>
|
|
588
|
+
<TData = A>(
|
|
589
|
+
arg: I | WatchSource<I>,
|
|
590
|
+
options?: AtomQueryNewOptions<A, TData>
|
|
591
|
+
): Promise<SuspenseQueryView<TData, E>>
|
|
305
592
|
}
|
|
306
|
-
} = <
|
|
307
|
-
self: RequestHandlerWithInput<
|
|
593
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
594
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
308
595
|
) => {
|
|
309
596
|
const runPromise = makeRunPromise(this.getRuntime())
|
|
310
|
-
const q = this.
|
|
311
|
-
return
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const isMounted =
|
|
316
|
-
|
|
317
|
-
|
|
597
|
+
const q = this.useQueryNew(self)
|
|
598
|
+
return <TData = A>(
|
|
599
|
+
argOrOptions: I | WatchSource<I>,
|
|
600
|
+
options?: AtomQueryNewOptions<A, TData>
|
|
601
|
+
) => {
|
|
602
|
+
const [{ view, data }, isMounted, signal] = useScopedSuspenseSetup(() => {
|
|
603
|
+
const view = q<TData>(argOrOptions, options)
|
|
604
|
+
const data = latestDefined(() => view.data.value, "suspenseNew")
|
|
605
|
+
return { view, data }
|
|
318
606
|
})
|
|
319
607
|
|
|
320
608
|
// @effect-diagnostics effect/missingEffectError:off
|
|
321
609
|
const eff = Effect.gen(function*() {
|
|
322
|
-
|
|
323
|
-
// what's the difference with just calling `fetch` ?
|
|
324
|
-
// we will receive a CancelledError which we will have to ignore in our ErrorBoundary, otherwise the user ends up on an error page even if the user e.g cancelled a navigation
|
|
325
|
-
const r = yield* Effect.tryPromise(() => uqrt.suspense()).pipe(
|
|
326
|
-
Effect.catchTag("UnknownError", (err) =>
|
|
327
|
-
isCancelledError(err.cause)
|
|
328
|
-
? Effect.interrupt
|
|
329
|
-
: Effect.die(err.cause))
|
|
330
|
-
)
|
|
610
|
+
const exit = yield* view.awaitResult().pipe(Effect.exit)
|
|
331
611
|
if (!isMounted.value) {
|
|
332
612
|
return yield* Effect.interrupt
|
|
333
613
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
r,
|
|
341
|
-
resultRef
|
|
342
|
-
})
|
|
343
|
-
return yield* Effect.die(
|
|
344
|
-
"Internal Error: Promise should be resolved already"
|
|
345
|
-
)
|
|
346
|
-
}
|
|
347
|
-
if (AsyncResult.isFailure(result)) {
|
|
348
|
-
return yield* Exit.failCause(result.cause)
|
|
614
|
+
if (Exit.isFailure(exit)) {
|
|
615
|
+
// Still mounted → query-side interrupt (see SuspenseInterruptedError) — render, don't silence.
|
|
616
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
617
|
+
return yield* new SuspenseInterruptedError({ originalCause: exit.cause, source: "suspenseNew" })
|
|
618
|
+
}
|
|
619
|
+
return yield* Exit.failCause(exit.cause)
|
|
349
620
|
}
|
|
350
621
|
|
|
351
|
-
|
|
622
|
+
const fetch = (_options?: RefetchOptions) => view.refetch()
|
|
623
|
+
const handle = {
|
|
624
|
+
awaitResult: view.awaitResult,
|
|
625
|
+
refetch: view.refetch,
|
|
626
|
+
refresh: view.refresh,
|
|
627
|
+
registry: view.registry,
|
|
628
|
+
atom: view.atom
|
|
629
|
+
}
|
|
630
|
+
return Object.assign(
|
|
631
|
+
[
|
|
632
|
+
view.result,
|
|
633
|
+
data,
|
|
634
|
+
fetch,
|
|
635
|
+
handle
|
|
636
|
+
] as const,
|
|
637
|
+
{
|
|
638
|
+
...view,
|
|
639
|
+
data
|
|
640
|
+
}
|
|
641
|
+
)
|
|
352
642
|
})
|
|
353
643
|
|
|
354
|
-
return runPromise(eff)
|
|
644
|
+
return runPromise(eff, { signal })
|
|
355
645
|
}
|
|
356
646
|
}
|
|
357
647
|
}
|
|
@@ -361,11 +651,89 @@ const managedRuntimeRt = <A, E>(mrt: ManagedRuntime.ManagedRuntime<A, E>) => mrt
|
|
|
361
651
|
|
|
362
652
|
type Base = I18n | Toast
|
|
363
653
|
type Mix = ApiClientFactory | Commander | Base
|
|
654
|
+
|
|
655
|
+
type InvalidationResources = Record<string, Record<string, unknown>>
|
|
656
|
+
type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I
|
|
657
|
+
: never
|
|
658
|
+
|
|
659
|
+
type CommandInvalidationResources<Req> = Req extends {
|
|
660
|
+
readonly type: "command"
|
|
661
|
+
readonly "~invalidationResources"?: infer Resources
|
|
662
|
+
} ? NonNullable<Resources> extends InvalidationResources ? NonNullable<Resources> : never
|
|
663
|
+
: Req extends {
|
|
664
|
+
readonly type: "command"
|
|
665
|
+
readonly config?: infer Config
|
|
666
|
+
} ? Config extends {
|
|
667
|
+
readonly invalidationResources?: infer LegacyResources
|
|
668
|
+
} ? NonNullable<LegacyResources> extends InvalidationResources ? NonNullable<LegacyResources> : never
|
|
669
|
+
: Config extends {
|
|
670
|
+
readonly invalidatesQueries?: InvalidationCallback<infer LegacyResources, any, any, any>
|
|
671
|
+
} ? NonNullable<LegacyResources> extends InvalidationResources ? NonNullable<LegacyResources> : never
|
|
672
|
+
: never
|
|
673
|
+
: never
|
|
674
|
+
|
|
675
|
+
type InvalidationResourcesForUnion<M extends RequestsAny> = {
|
|
676
|
+
[K in keyof M]: CommandInvalidationResources<M[K]>
|
|
677
|
+
}[keyof M]
|
|
678
|
+
|
|
679
|
+
type InvalidationResourcesFor<M extends RequestsAny> = [InvalidationResourcesForUnion<M>] extends [never] ? never
|
|
680
|
+
: UnionToIntersection<InvalidationResourcesForUnion<M>> extends infer R ? R extends InvalidationResources ? R
|
|
681
|
+
: never
|
|
682
|
+
: never
|
|
683
|
+
|
|
684
|
+
type QueryInvalidationFactory<M extends RequestsAny> = (client: ClientFrom<M>) => QueryInvalidation<M>
|
|
685
|
+
|
|
686
|
+
type StrictResourcesArg<Shape, Actual extends Shape = Shape> =
|
|
687
|
+
& Actual
|
|
688
|
+
& Record<Exclude<keyof Actual, keyof Shape>, never>
|
|
689
|
+
|
|
690
|
+
type ClientForArgs<
|
|
691
|
+
M extends RequestsAny,
|
|
692
|
+
Resources extends InvalidationResourcesFor<M> = InvalidationResourcesFor<M>
|
|
693
|
+
> = [InvalidationResourcesFor<M>] extends [never] ? [
|
|
694
|
+
queryInvalidation?: QueryInvalidationFactory<M>,
|
|
695
|
+
invalidationResources?: StrictResourcesArg<
|
|
696
|
+
InvalidationResourcesFor<M>,
|
|
697
|
+
Resources
|
|
698
|
+
>
|
|
699
|
+
]
|
|
700
|
+
: [
|
|
701
|
+
queryInvalidation: QueryInvalidationFactory<M> | undefined,
|
|
702
|
+
invalidationResources: StrictResourcesArg<
|
|
703
|
+
InvalidationResourcesFor<M>,
|
|
704
|
+
Resources
|
|
705
|
+
>
|
|
706
|
+
]
|
|
707
|
+
|
|
708
|
+
const makeResolvedAtomQueryInvalidator = <R>(getContext: () => Context.Context<R>): QueryInvalidator => {
|
|
709
|
+
let reactivity: Context.Service.Shape<typeof Reactivity.Reactivity> | undefined
|
|
710
|
+
const getReactivity = () => {
|
|
711
|
+
if (reactivity !== undefined) return reactivity
|
|
712
|
+
const service = Context.getOrUndefined(getContext(), Reactivity.Reactivity)
|
|
713
|
+
if (service === undefined) {
|
|
714
|
+
throw new Error("Reactivity service is missing from the client runtime")
|
|
715
|
+
}
|
|
716
|
+
return reactivity = service
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
invalidateAndAwait: (keys) =>
|
|
721
|
+
invalidateAndAwait(keys).pipe(
|
|
722
|
+
Effect.provideService(Reactivity.Reactivity, getReactivity())
|
|
723
|
+
),
|
|
724
|
+
invalidateSoft: (keys) =>
|
|
725
|
+
invalidateSoft(keys).pipe(
|
|
726
|
+
Effect.provideService(Reactivity.Reactivity, getReactivity())
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
364
731
|
export const makeClient = <RT_, RTHooks>(
|
|
365
732
|
// global, but only accessible after startup has completed
|
|
366
733
|
getBaseMrt: () => ManagedRuntime.ManagedRuntime<RT_ | Mix, never>,
|
|
367
734
|
clientFor_: ReturnType<typeof ApiClientFactory["makeFor"]>,
|
|
368
|
-
rtHooks: Layer.Layer<RTHooks, never, Mix
|
|
735
|
+
rtHooks: Layer.Layer<RTHooks, never, Mix>,
|
|
736
|
+
options?: MakeClientOptions
|
|
369
737
|
) => {
|
|
370
738
|
type RT = RT_ | Mix
|
|
371
739
|
const getBaseRt = () => managedRuntimeRt(getBaseMrt())
|
|
@@ -373,39 +741,241 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
373
741
|
let cmd: Effect.Success<typeof makeCommand>
|
|
374
742
|
const useCommand = () => cmd ??= getBaseMrt().runSync(makeCommand)
|
|
375
743
|
|
|
744
|
+
// one AtomRuntime for the query engine, built lazily from the live app context;
|
|
745
|
+
// shares the ManagedRuntime memoMap so layers + Reactivity are the same instances.
|
|
746
|
+
let atomRt: AtomClientRuntime | undefined
|
|
747
|
+
const getAtomRt =
|
|
748
|
+
() => (atomRt ??= makeAtomClientRuntime(() => Layer.succeedContext(getBaseRt()), getBaseMrt().memoMap))
|
|
749
|
+
|
|
750
|
+
const legacyQueryEngine = options?.legacyQueryEngine ?? "tanstack"
|
|
751
|
+
let tanstackQueryClient: ReturnType<typeof makeTanstackQueryClient> | undefined
|
|
752
|
+
const getTanstackQueryClient = () => tanstackQueryClient ??= makeTanstackQueryClient()
|
|
753
|
+
const atomInvalidator = makeResolvedAtomQueryInvalidator(getBaseRt)
|
|
754
|
+
const queryInvalidator = legacyQueryEngine === "tanstack"
|
|
755
|
+
? combineQueryInvalidators(atomInvalidator, makeTanstackQueryInvalidator(getTanstackQueryClient()))
|
|
756
|
+
: atomInvalidator
|
|
757
|
+
setQueryCacheUpdater(
|
|
758
|
+
legacyQueryEngine === "tanstack"
|
|
759
|
+
? combineQueryCacheUpdaters(
|
|
760
|
+
makeTanstackQueryCacheUpdater(getTanstackQueryClient()),
|
|
761
|
+
optionalAtomQueryCacheUpdater
|
|
762
|
+
)
|
|
763
|
+
: atomQueryCacheUpdater
|
|
764
|
+
)
|
|
765
|
+
|
|
376
766
|
let m: ReturnType<typeof useMutationInt>
|
|
377
|
-
const useMutation = () => m ??= useMutationInt()
|
|
767
|
+
const useMutation = () => m ??= useMutationInt(queryInvalidator)
|
|
768
|
+
|
|
769
|
+
let sm2: ReturnType<typeof makeStreamMutation2>
|
|
770
|
+
const useStreamMutation2 = () => sm2 ??= makeStreamMutation2(queryInvalidator)
|
|
378
771
|
|
|
379
|
-
const
|
|
772
|
+
const legacyUseQuery = legacyQueryEngine === "tanstack"
|
|
773
|
+
? makeTanstackQuery(getBaseRt, getTanstackQueryClient())
|
|
774
|
+
: undefined
|
|
775
|
+
const query = new QueryImpl(getBaseRt, getAtomRt, legacyUseQuery)
|
|
380
776
|
const useQuery = query.useQuery
|
|
777
|
+
const useQueryNew = query.useQueryNew
|
|
778
|
+
const useQueryAtom = query.useQueryAtom
|
|
779
|
+
const useQueryFamily = query.useQueryFamily
|
|
381
780
|
const useSuspenseQuery = query.useSuspenseQuery
|
|
781
|
+
const useSuspenseQueryNew = query.useSuspenseQueryNew
|
|
782
|
+
const useStreamQuery = query.useStreamQuery
|
|
783
|
+
const useStreamQueryFamily = query.useStreamQueryFamily
|
|
784
|
+
const useStreamQueryAtom = query.useStreamQueryAtom
|
|
785
|
+
const useStreamQueryNew = query.useStreamQueryNew
|
|
786
|
+
|
|
787
|
+
const isQueryHandler = <H extends { readonly Request: Req }>(handler: H): handler is QueryHandler<H> =>
|
|
788
|
+
handler.Request.type === "query" && !handler.Request.stream
|
|
789
|
+
|
|
790
|
+
const isStreamQueryHandler = <H extends { readonly Request: Req }>(handler: H): handler is QueryStreamHandler<H> =>
|
|
791
|
+
handler.Request.type === "query" && handler.Request.stream
|
|
792
|
+
|
|
793
|
+
const queryHelpersFor = <I, A, E, Request extends Req, Name extends string>(
|
|
794
|
+
handler: RequestHandlerWithInput<I, A, E, RT, Request, Name>
|
|
795
|
+
) => ({
|
|
796
|
+
family: useQueryFamily(handler),
|
|
797
|
+
atom: useQueryAtom(handler),
|
|
798
|
+
query: useQuery(handler),
|
|
799
|
+
queryNew: useQueryNew(handler),
|
|
800
|
+
suspense: useSuspenseQuery(handler),
|
|
801
|
+
suspenseNew: useSuspenseQueryNew(handler)
|
|
802
|
+
})
|
|
803
|
+
|
|
804
|
+
const streamQueryHelpersFor = <I, A, E, Request extends Req, Name extends string>(
|
|
805
|
+
handler: RequestStreamHandlerWithInput<I, A, E, RT, Request, Name>
|
|
806
|
+
) => ({
|
|
807
|
+
family: useStreamQueryFamily(handler),
|
|
808
|
+
atom: useStreamQueryAtom(handler),
|
|
809
|
+
query: useStreamQuery(handler),
|
|
810
|
+
queryNew: useStreamQueryNew(handler)
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
const projectQueryFor = <
|
|
814
|
+
I,
|
|
815
|
+
A,
|
|
816
|
+
E,
|
|
817
|
+
Request extends Req & { readonly success: S.Top & { readonly "EncodingServices": RT } },
|
|
818
|
+
Name extends string
|
|
819
|
+
>(
|
|
820
|
+
handler: RequestHandlerWithInput<I, A, E, RT, Request, Name>
|
|
821
|
+
) =>
|
|
822
|
+
<ProjSchema extends S.Top & { readonly "DecodingServices": RT }>(projectionSchema: ProjSchema) => {
|
|
823
|
+
const successSchema = handler.Request.success
|
|
824
|
+
const projectionHash = projectionSchemaHash(projectionSchema)
|
|
825
|
+
const projected = projectHandler(handler.handler, successSchema, projectionSchema)
|
|
826
|
+
const projectedHandler = {
|
|
827
|
+
handler: projected,
|
|
828
|
+
id: handler.id,
|
|
829
|
+
Request: handler.Request,
|
|
830
|
+
queryKeyProjectionHash: projectionHash
|
|
831
|
+
}
|
|
832
|
+
if (handler.options) {
|
|
833
|
+
return {
|
|
834
|
+
request: projected,
|
|
835
|
+
...queryHelpersFor({ ...projectedHandler, options: handler.options })
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return {
|
|
839
|
+
request: projected,
|
|
840
|
+
...queryHelpersFor(projectedHandler)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const mergeInvalidation = (
|
|
845
|
+
a?: MutationOptionsBase["queryInvalidation"],
|
|
846
|
+
b?: MutationOptionsBase["queryInvalidation"]
|
|
847
|
+
): MutationOptionsBase["queryInvalidation"] | undefined => {
|
|
848
|
+
if (!a && !b) {
|
|
849
|
+
return undefined
|
|
850
|
+
}
|
|
851
|
+
return (defaultKey, name, input, output) => [
|
|
852
|
+
...(a?.(defaultKey, name, input, output) ?? []),
|
|
853
|
+
...(b?.(defaultKey, name, input, output) ?? [])
|
|
854
|
+
]
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const withDefaultInvalidation = (
|
|
858
|
+
mut: any,
|
|
859
|
+
defaultInvalidation?: MutationOptionsBase["queryInvalidation"]
|
|
860
|
+
) => {
|
|
861
|
+
if (!defaultInvalidation) return mut
|
|
862
|
+
const apply = (callerOpts?: MutationOptionsBase) => ({
|
|
863
|
+
...callerOpts,
|
|
864
|
+
queryInvalidation: callerOpts?.queryInvalidation
|
|
865
|
+
? mergeInvalidation(defaultInvalidation, callerOpts.queryInvalidation)
|
|
866
|
+
: defaultInvalidation
|
|
867
|
+
})
|
|
868
|
+
return (input: any, callerOpts?: MutationOptionsBase) => mut(input, apply(callerOpts))
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const makeQueryResources = <Resources extends InvalidationResources>(resources: Resources | undefined) => {
|
|
872
|
+
if (!resources) {
|
|
873
|
+
return {} as Record<string, Record<string, unknown>>
|
|
874
|
+
}
|
|
875
|
+
return resources as Record<string, Record<string, unknown>>
|
|
876
|
+
}
|
|
382
877
|
|
|
383
878
|
const mapQuery = <M extends RequestsAny>(
|
|
384
879
|
client: ClientFrom<M>
|
|
385
880
|
) => {
|
|
386
881
|
const queries = Struct.keys(client).reduce(
|
|
387
882
|
(acc, key) => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
883
|
+
const handler = client[key]
|
|
884
|
+
if (isQueryHandler(handler)) {
|
|
885
|
+
Object.assign(acc, {
|
|
886
|
+
[camelCase(key) + "QueryFamily"]: Object.assign(useQueryFamily(handler), {
|
|
887
|
+
id: client[key].id
|
|
888
|
+
})
|
|
889
|
+
})
|
|
890
|
+
;(acc as any)[camelCase(key) + "Query"] = Object.assign(useQuery(handler), {
|
|
891
|
+
id: client[key].id
|
|
892
|
+
})
|
|
893
|
+
;(acc as any)[camelCase(key) + "QueryNew"] = Object.assign(useQueryNew(handler), {
|
|
894
|
+
id: client[key].id
|
|
895
|
+
})
|
|
896
|
+
;(acc as any)[camelCase(key) + "SuspenseQuery"] = Object.assign(useSuspenseQuery(handler), {
|
|
897
|
+
id: client[key].id
|
|
898
|
+
})
|
|
899
|
+
;(acc as any)[camelCase(key) + "SuspenseQueryNew"] = Object.assign(
|
|
900
|
+
useSuspenseQueryNew(handler),
|
|
901
|
+
{
|
|
902
|
+
id: client[key].id
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
} else if (isStreamQueryHandler(handler)) {
|
|
906
|
+
const streamHelpers = streamQueryHelpersFor(handler)
|
|
907
|
+
Object.assign(acc, {
|
|
908
|
+
[camelCase(key) + "QueryFamily"]: Object.assign(streamHelpers.family, {
|
|
909
|
+
id: client[key].id
|
|
910
|
+
}),
|
|
911
|
+
[camelCase(key) + "Query"]: Object.assign(streamHelpers.query, {
|
|
912
|
+
id: client[key].id
|
|
913
|
+
}),
|
|
914
|
+
[camelCase(key) + "QueryNew"]: Object.assign(streamHelpers.queryNew, {
|
|
915
|
+
id: client[key].id
|
|
916
|
+
})
|
|
917
|
+
})
|
|
918
|
+
}
|
|
394
919
|
return acc
|
|
395
920
|
},
|
|
396
921
|
{} as
|
|
922
|
+
& {
|
|
923
|
+
[
|
|
924
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
925
|
+
: `${ToCamel<string & Key>}QueryFamily`
|
|
926
|
+
]: Queries<RT, QueryHandler<typeof client[Key]>>["family"]
|
|
927
|
+
}
|
|
397
928
|
& {
|
|
398
929
|
// apparently can't get JSDoc in here..
|
|
399
|
-
[
|
|
930
|
+
[
|
|
931
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
932
|
+
: `${ToCamel<string & Key>}Query`
|
|
933
|
+
]: Queries<RT, QueryHandler<typeof client[Key]>>["query"]
|
|
934
|
+
}
|
|
935
|
+
& {
|
|
936
|
+
[
|
|
937
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
938
|
+
: `${ToCamel<string & Key>}QueryNew`
|
|
939
|
+
]: Queries<RT, QueryHandler<typeof client[Key]>>["queryNew"]
|
|
400
940
|
}
|
|
401
941
|
// todo: or suspense as an Option?
|
|
402
942
|
& {
|
|
403
943
|
// apparently can't get JSDoc in here..
|
|
404
|
-
[
|
|
944
|
+
[
|
|
945
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
946
|
+
: `${ToCamel<string & Key>}SuspenseQuery`
|
|
947
|
+
]: Queries<
|
|
405
948
|
RT,
|
|
406
|
-
typeof client[Key]
|
|
949
|
+
QueryHandler<typeof client[Key]>
|
|
407
950
|
>["suspense"]
|
|
408
951
|
}
|
|
952
|
+
& {
|
|
953
|
+
[
|
|
954
|
+
Key in keyof typeof client as QueryHandler<typeof client[Key]> extends never ? never
|
|
955
|
+
: `${ToCamel<string & Key>}SuspenseQueryNew`
|
|
956
|
+
]: Queries<
|
|
957
|
+
RT,
|
|
958
|
+
QueryHandler<typeof client[Key]>
|
|
959
|
+
>["suspenseNew"]
|
|
960
|
+
}
|
|
961
|
+
& {
|
|
962
|
+
[
|
|
963
|
+
Key in keyof typeof client as QueryStreamHandler<typeof client[Key]> extends never ? never
|
|
964
|
+
: `${ToCamel<string & Key>}QueryFamily`
|
|
965
|
+
]: StreamQueries<RT, QueryStreamHandler<typeof client[Key]>>["family"]
|
|
966
|
+
}
|
|
967
|
+
& {
|
|
968
|
+
[
|
|
969
|
+
Key in keyof typeof client as QueryStreamHandler<typeof client[Key]> extends never ? never
|
|
970
|
+
: `${ToCamel<string & Key>}Query`
|
|
971
|
+
]: StreamQueries<RT, QueryStreamHandler<typeof client[Key]>>["query"]
|
|
972
|
+
}
|
|
973
|
+
& {
|
|
974
|
+
[
|
|
975
|
+
Key in keyof typeof client as QueryStreamHandler<typeof client[Key]> extends never ? never
|
|
976
|
+
: `${ToCamel<string & Key>}QueryNew`
|
|
977
|
+
]: StreamQueries<RT, QueryStreamHandler<typeof client[Key]>>["queryNew"]
|
|
978
|
+
}
|
|
409
979
|
)
|
|
410
980
|
return queries
|
|
411
981
|
}
|
|
@@ -416,20 +986,27 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
416
986
|
const Command = useCommand()
|
|
417
987
|
const mutations = Struct.keys(client).reduce(
|
|
418
988
|
(acc, key) => {
|
|
989
|
+
if (!(client[key].Request.type === "command" && !client[key].Request.stream)) {
|
|
990
|
+
return acc
|
|
991
|
+
}
|
|
419
992
|
const mut = client[key].handler
|
|
420
|
-
const
|
|
421
|
-
const
|
|
993
|
+
const request = mut
|
|
994
|
+
const fn = (options?: any) => Command.fn(client[key].id, options)
|
|
995
|
+
const wrap = (options?: any) => Command.wrap({ mutate: request, id: client[key].id }, options)
|
|
422
996
|
;(acc as any)[camelCase(key) + "Request"] = Object.assign(
|
|
423
997
|
mut,
|
|
424
|
-
fn, // to get the i18n key etc.
|
|
425
|
-
{ wrap, fn }
|
|
998
|
+
Command.fn(client[key].id), // to get the i18n key etc.
|
|
999
|
+
{ wrap, fn, request }
|
|
426
1000
|
)
|
|
427
1001
|
return acc
|
|
428
1002
|
},
|
|
429
1003
|
{} as {
|
|
430
|
-
[
|
|
1004
|
+
[
|
|
1005
|
+
Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
|
|
1006
|
+
: `${ToCamel<string & Key>}Request`
|
|
1007
|
+
]: CommandRequestWithExtensions<
|
|
431
1008
|
RT | RTHooks,
|
|
432
|
-
typeof client[Key]
|
|
1009
|
+
CommandHandler<typeof client[Key]>
|
|
433
1010
|
>
|
|
434
1011
|
}
|
|
435
1012
|
)
|
|
@@ -437,26 +1014,56 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
437
1014
|
}
|
|
438
1015
|
|
|
439
1016
|
const mapMutation = <M extends RequestsAny>(
|
|
440
|
-
client: ClientFrom<M
|
|
1017
|
+
client: ClientFrom<M>,
|
|
1018
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
1019
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
441
1020
|
) => {
|
|
442
1021
|
const Command = useCommand()
|
|
443
1022
|
const mutation = useMutation()
|
|
1023
|
+
const invalidation = queryInvalidation?.(client)
|
|
1024
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
444
1025
|
const mutations = Struct.keys(client).reduce(
|
|
445
1026
|
(acc, key) => {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1027
|
+
if (!(client[key].Request.type === "command" && !client[key].Request.stream)) {
|
|
1028
|
+
return acc
|
|
1029
|
+
}
|
|
1030
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
1031
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
1032
|
+
| undefined
|
|
1033
|
+
const fromRequest = fromRequestConfig
|
|
1034
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
1035
|
+
fromRequestConfig(
|
|
1036
|
+
defaultKey,
|
|
1037
|
+
queryResources as never,
|
|
1038
|
+
input,
|
|
1039
|
+
output as never
|
|
1040
|
+
) as InvalidationEntry[])
|
|
1041
|
+
: undefined
|
|
1042
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
1043
|
+
const makeProjectedMutation = (handler: any): any => {
|
|
1044
|
+
const mut: any = withDefaultInvalidation(mutation(handler), mergedInvalidation)
|
|
1045
|
+
return Object.assign(mut, {
|
|
1046
|
+
wrap: (options?: any) => Command.wrap({ mutate: mut, id: client[key].id }, options),
|
|
1047
|
+
fn: (options?: any) => Command.fn(client[key].id, options),
|
|
1048
|
+
project: (projectionSchema: any) => {
|
|
1049
|
+
const projected = {
|
|
1050
|
+
...handler,
|
|
1051
|
+
handler: projectHandler(handler.handler, client[key].Request.success, projectionSchema)
|
|
1052
|
+
}
|
|
1053
|
+
return makeProjectedMutation(projected)
|
|
1054
|
+
}
|
|
1055
|
+
})
|
|
1056
|
+
}
|
|
1057
|
+
;(acc as any)[camelCase(key) + "Mutation"] = makeProjectedMutation(client[key] as any)
|
|
454
1058
|
return acc
|
|
455
1059
|
},
|
|
456
1060
|
{} as {
|
|
457
|
-
[
|
|
1061
|
+
[
|
|
1062
|
+
Key in keyof typeof client as CommandHandler<typeof client[Key]> extends never ? never
|
|
1063
|
+
: `${ToCamel<string & Key>}Mutation`
|
|
1064
|
+
]: MutationWithExtensions<
|
|
458
1065
|
RT | RTHooks,
|
|
459
|
-
typeof client[Key]
|
|
1066
|
+
CommandHandler<typeof client[Key]>
|
|
460
1067
|
>
|
|
461
1068
|
}
|
|
462
1069
|
)
|
|
@@ -466,7 +1073,8 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
466
1073
|
// make available .query, .suspense and .mutate for each operation
|
|
467
1074
|
// and a .helpers with all mutations and queries
|
|
468
1075
|
const mapClient = <M extends RequestsAny>(
|
|
469
|
-
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M
|
|
1076
|
+
queryInvalidation?: (client: ClientFrom<M>) => QueryInvalidation<M>,
|
|
1077
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
470
1078
|
) =>
|
|
471
1079
|
(
|
|
472
1080
|
client: ClientFrom<M>
|
|
@@ -474,74 +1082,167 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
474
1082
|
const Command = useCommand()
|
|
475
1083
|
const mutation = useMutation()
|
|
476
1084
|
const invalidation = queryInvalidation?.(client)
|
|
1085
|
+
const queryResources = makeQueryResources(invalidationResources)
|
|
477
1086
|
const extended = Struct.keys(client).reduce(
|
|
478
1087
|
(acc, key) => {
|
|
1088
|
+
const handler = client[key]
|
|
1089
|
+
const requestType = handler.Request.type
|
|
1090
|
+
const isStream = handler.Request.stream
|
|
479
1091
|
const fn = Command.fn(client[key].id)
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
client[key] as any,
|
|
483
|
-
invalidation?.[key] ? { queryInvalidation: invalidation[key] } : undefined
|
|
484
|
-
),
|
|
485
|
-
(mutate) =>
|
|
486
|
-
Object.assign(
|
|
487
|
-
mutate,
|
|
488
|
-
fn, // to get the i18n key etc.
|
|
489
|
-
{
|
|
490
|
-
wrap: Command.wrap({ mutate: Effect.isEffect(mutate) ? () => mutate : mutate, id: client[key].id }),
|
|
491
|
-
fn
|
|
492
|
-
}
|
|
493
|
-
)
|
|
494
|
-
)
|
|
495
|
-
|
|
496
|
-
const h_ = client[key].handler
|
|
497
|
-
const h = Effect.isEffect(h_)
|
|
498
|
-
? () => h_
|
|
499
|
-
: (...args: [any]) => h_(...args)
|
|
1092
|
+
const h_ = handler.handler
|
|
1093
|
+
const request = h_
|
|
500
1094
|
;(acc as any)[key] = Object.assign(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
1095
|
+
isQueryHandler(handler)
|
|
1096
|
+
? {
|
|
1097
|
+
...handler,
|
|
1098
|
+
request,
|
|
1099
|
+
...queryHelpersFor(handler),
|
|
1100
|
+
project: projectQueryFor(handler)
|
|
1101
|
+
}
|
|
1102
|
+
: isStreamQueryHandler(handler)
|
|
1103
|
+
? {
|
|
1104
|
+
...client[key],
|
|
1105
|
+
request,
|
|
1106
|
+
...streamQueryHelpersFor(handler)
|
|
1107
|
+
}
|
|
1108
|
+
: requestType === "command" && isStream
|
|
1109
|
+
? (() => {
|
|
1110
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
1111
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
1112
|
+
| undefined
|
|
1113
|
+
const fromRequest = fromRequestConfig
|
|
1114
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
1115
|
+
fromRequestConfig(
|
|
1116
|
+
defaultKey,
|
|
1117
|
+
queryResources as never,
|
|
1118
|
+
input,
|
|
1119
|
+
output as never
|
|
1120
|
+
) as InvalidationEntry[])
|
|
1121
|
+
: undefined
|
|
1122
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
1123
|
+
const streamCmd = useCommand()
|
|
1124
|
+
return {
|
|
1125
|
+
...client[key],
|
|
1126
|
+
request,
|
|
1127
|
+
query: useStreamQuery(client[key] as any),
|
|
1128
|
+
fn: (options?: any) => streamCmd.streamFn(client[key].id as any, options),
|
|
1129
|
+
mutate: (() => {
|
|
1130
|
+
const sm2Act = useStreamMutation2()(client[key] as any, mergedInvalidation)
|
|
1131
|
+
const sm2Handler = (input: any, _ctx: any) => (sm2Act as (i: any) => any)(input)
|
|
1132
|
+
return Object.assign(sm2Act, {
|
|
1133
|
+
id: client[key].id,
|
|
1134
|
+
wrap: (options?: any) => streamCmd.streamWrap(sm2Handler, client[key].id as any, options)
|
|
1135
|
+
})
|
|
1136
|
+
})()
|
|
1137
|
+
}
|
|
1138
|
+
})()
|
|
1139
|
+
: {
|
|
1140
|
+
mutate: ((handler: any) => {
|
|
1141
|
+
const fromRequestConfig = client[key].Request.config?.["invalidatesQueries"] as
|
|
1142
|
+
| InvalidationCallback<InvalidationResourcesFor<M>>
|
|
1143
|
+
| undefined
|
|
1144
|
+
const fromRequest = fromRequestConfig
|
|
1145
|
+
? ((defaultKey: string[], _name: string, input?: unknown, output?: unknown) =>
|
|
1146
|
+
fromRequestConfig(
|
|
1147
|
+
defaultKey,
|
|
1148
|
+
queryResources as never,
|
|
1149
|
+
input,
|
|
1150
|
+
output as never
|
|
1151
|
+
) as InvalidationEntry[])
|
|
1152
|
+
: undefined
|
|
1153
|
+
const mergedInvalidation = mergeInvalidation(fromRequest, invalidation?.[key])
|
|
1154
|
+
const makeProjectedMutation = (h: any): any => {
|
|
1155
|
+
const mutate = withDefaultInvalidation(mutation(h), mergedInvalidation)
|
|
1156
|
+
return Object.assign(
|
|
1157
|
+
mutate,
|
|
1158
|
+
{
|
|
1159
|
+
wrap: (options?: any) =>
|
|
1160
|
+
Command.wrap({
|
|
1161
|
+
mutate,
|
|
1162
|
+
id: client[key].id
|
|
1163
|
+
}, options),
|
|
1164
|
+
fn: (options?: any) => Command.fn(client[key].id, options),
|
|
1165
|
+
project: (projectionSchema: any) => {
|
|
1166
|
+
const projected = {
|
|
1167
|
+
...h,
|
|
1168
|
+
handler: projectHandler(h.handler, client[key].Request.success, projectionSchema)
|
|
1169
|
+
}
|
|
1170
|
+
return makeProjectedMutation(projected)
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
}
|
|
1175
|
+
return makeProjectedMutation(handler)
|
|
1176
|
+
})(client[key] as any),
|
|
1177
|
+
...client[key],
|
|
1178
|
+
...fn, // to get the i18n key etc.
|
|
1179
|
+
request,
|
|
1180
|
+
fn: (options?: any) => Command.fn(client[key].id, options),
|
|
1181
|
+
wrap: (options?: any) => Command.wrap({ mutate: h_, id: client[key].id }, options)
|
|
1182
|
+
}
|
|
511
1183
|
)
|
|
512
1184
|
return acc
|
|
513
1185
|
},
|
|
514
1186
|
{} as {
|
|
515
1187
|
[Key in keyof typeof client]:
|
|
516
1188
|
& typeof client[Key]
|
|
517
|
-
&
|
|
1189
|
+
& (QueryHandler<typeof client[Key]> extends never ? {}
|
|
1190
|
+
:
|
|
1191
|
+
& QueryRequestWithExtensions<QueryHandler<typeof client[Key]>>
|
|
1192
|
+
& Queries<RT, QueryHandler<typeof client[Key]>>
|
|
1193
|
+
& QueryProjection<RT, QueryHandler<typeof client[Key]>>)
|
|
1194
|
+
& (QueryStreamHandler<typeof client[Key]> extends never ? {}
|
|
1195
|
+
: StreamQueries<RT, QueryStreamHandler<typeof client[Key]>>)
|
|
1196
|
+
& (CommandHandler<typeof client[Key]> extends never ? {}
|
|
1197
|
+
: CommandRequestWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>>)
|
|
1198
|
+
& (CommandHandler<typeof client[Key]> extends never ? {}
|
|
1199
|
+
: { mutate: MutationWithExtensions<RT | RTHooks, CommandHandler<typeof client[Key]>> })
|
|
1200
|
+
& (CommandStreamHandler<typeof client[Key]> extends never ? {}
|
|
1201
|
+
: {
|
|
1202
|
+
fn: StreamFnStreamExtension<RT | RTHooks, CommandStreamHandler<typeof client[Key]>>
|
|
1203
|
+
mutate: StreamMutation2WithExtensions<RT | RTHooks, CommandStreamHandler<typeof client[Key]>>
|
|
1204
|
+
})
|
|
518
1205
|
& {
|
|
519
|
-
|
|
520
|
-
|
|
1206
|
+
Input: typeof client[Key] extends RequestStreamHandlerWithInput<infer I, any, any, any, any, any, any> ? I
|
|
1207
|
+
: typeof client[Key] extends RequestHandlerWithInput<infer I, any, any, any, any, any> ? I
|
|
1208
|
+
: never
|
|
521
1209
|
}
|
|
522
|
-
& Queries<RT, typeof client[Key]>
|
|
523
1210
|
}
|
|
524
1211
|
)
|
|
525
|
-
return Object.assign(extended, {
|
|
1212
|
+
return Object.assign(extended, {
|
|
1213
|
+
helpers: {
|
|
1214
|
+
...mapRequest(client),
|
|
1215
|
+
...mapMutation(client, queryInvalidation, invalidationResources),
|
|
1216
|
+
...mapQuery(client)
|
|
1217
|
+
}
|
|
1218
|
+
})
|
|
526
1219
|
}
|
|
527
1220
|
|
|
528
1221
|
// TODO: Clean up this delay initialisation messs
|
|
529
1222
|
// TODO; invalidateQueries should perhaps be configured in the Request impl themselves?
|
|
530
1223
|
const clientFor__ = <M extends RequestsAny>(
|
|
531
1224
|
m: M,
|
|
532
|
-
queryInvalidation?:
|
|
533
|
-
|
|
1225
|
+
queryInvalidation?: QueryInvalidationFactory<M>,
|
|
1226
|
+
invalidationResources?: InvalidationResourcesFor<M>
|
|
1227
|
+
) => getBaseMrt().runSync(clientFor_(m).pipe(Effect.map(mapClient(queryInvalidation, invalidationResources))))
|
|
534
1228
|
|
|
535
1229
|
// delay client creation until first access
|
|
536
1230
|
// the idea is that we don't need the useNuxtApp().$runtime (only available at later initialisation stage)
|
|
537
1231
|
// until we are at a place where it is available..
|
|
538
|
-
const clientFor = <
|
|
1232
|
+
const clientFor = <
|
|
1233
|
+
M extends RequestsAny,
|
|
1234
|
+
Resources extends InvalidationResourcesFor<M> = InvalidationResourcesFor<M>
|
|
1235
|
+
>(
|
|
539
1236
|
m: M,
|
|
540
|
-
|
|
1237
|
+
...args: ClientForArgs<M, Resources>
|
|
541
1238
|
) => {
|
|
1239
|
+
const [queryInvalidation, invalidationResources] = args as [
|
|
1240
|
+
QueryInvalidationFactory<M> | undefined,
|
|
1241
|
+
InvalidationResourcesFor<M> | undefined
|
|
1242
|
+
]
|
|
542
1243
|
type Client = ReturnType<typeof clientFor__<M>>
|
|
543
1244
|
let client: Client | undefined = undefined
|
|
544
|
-
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation))
|
|
1245
|
+
const getOrMakeClient = () => (client ??= clientFor__(m, queryInvalidation, invalidationResources))
|
|
545
1246
|
|
|
546
1247
|
// initialize on first use..
|
|
547
1248
|
const proxy = Struct.keys(m).concat(["helpers"]).reduce((acc, key) => {
|
|
@@ -564,6 +1265,7 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
564
1265
|
// delay initialisation until first use...
|
|
565
1266
|
fn: (...args: [any]) => useCommand().fn(...args),
|
|
566
1267
|
wrap: (...args: [any]) => useCommand().wrap(...args),
|
|
1268
|
+
streamFn: (...args: [any]) => useCommand().streamFn(...args),
|
|
567
1269
|
alt: (...args: [any]) => useCommand().alt(...args),
|
|
568
1270
|
alt2: (...args: [any]) => useCommand().alt2(...args)
|
|
569
1271
|
} as ReturnType<typeof useCommand>,
|
|
@@ -573,15 +1275,18 @@ export const makeClient = <RT_, RTHooks>(
|
|
|
573
1275
|
return {
|
|
574
1276
|
Command,
|
|
575
1277
|
useCommand,
|
|
576
|
-
clientFor
|
|
1278
|
+
clientFor,
|
|
1279
|
+
tanstackQueryClient: getTanstackQueryClient()
|
|
577
1280
|
}
|
|
578
1281
|
}
|
|
579
1282
|
|
|
580
1283
|
export type QueryInvalidation<M> = {
|
|
581
|
-
[K in keyof M]?: (
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
1284
|
+
[K in keyof M]?: (
|
|
1285
|
+
defaultKey: string[],
|
|
1286
|
+
name: string,
|
|
1287
|
+
input?: unknown,
|
|
1288
|
+
output?: Exit.Exit<unknown, unknown>
|
|
1289
|
+
) => InvalidationEntry[]
|
|
585
1290
|
}
|
|
586
1291
|
|
|
587
1292
|
export type ToCamel<S extends string | number | symbol> = S extends string
|
|
@@ -589,17 +1294,21 @@ export type ToCamel<S extends string | number | symbol> = S extends string
|
|
|
589
1294
|
: Uncapitalize<S>
|
|
590
1295
|
: never
|
|
591
1296
|
|
|
592
|
-
export interface CommandBase<I = void, A = void> {
|
|
1297
|
+
export interface CommandBase<I = void, A = void, RA = unknown, RE = unknown> {
|
|
593
1298
|
handle: (input: I) => A
|
|
594
1299
|
waiting: boolean
|
|
595
1300
|
blocked: boolean
|
|
596
1301
|
allowed: boolean
|
|
597
1302
|
action: string
|
|
598
1303
|
label: string
|
|
1304
|
+
/** formatted progress info for current `running` state, when `progress` was supplied */
|
|
1305
|
+
progress?: Progress | undefined
|
|
1306
|
+
/** reactive result state, available on stream-backed commands */
|
|
1307
|
+
result?: AsyncResult.AsyncResult<RA, RE>
|
|
599
1308
|
}
|
|
600
1309
|
|
|
601
|
-
export interface EffectCommand<I = void, A = unknown, E = unknown> extends CommandBase<I, Fiber<A, E
|
|
1310
|
+
export interface EffectCommand<I = void, A = unknown, E = unknown> extends CommandBase<I, Fiber<A, E>, A, E> {}
|
|
602
1311
|
|
|
603
|
-
export interface CommandFromRequest<I extends
|
|
604
|
-
extends EffectCommand<
|
|
1312
|
+
export interface CommandFromRequest<I extends { readonly make: (...args: any[]) => any }, A = unknown, E = unknown>
|
|
1313
|
+
extends EffectCommand<HandlerInput<I>, A, E>
|
|
605
1314
|
{}
|