@effect-app/vue 4.0.0-beta.22 → 4.0.0-beta.220
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 +1605 -0
- package/dist/commander.d.ts +628 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +1060 -0
- package/dist/confirm.d.ts +19 -0
- package/dist/confirm.d.ts.map +1 -0
- package/dist/confirm.js +24 -0
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -18
- package/dist/form.d.ts +14 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +41 -12
- package/dist/index.d.ts +1 -1
- package/dist/intl.d.ts +15 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +9 -0
- package/dist/lib.d.ts +6 -9
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +35 -10
- package/dist/makeClient.d.ts +152 -339
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +221 -376
- package/dist/makeContext.d.ts +1 -1
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/makeUseCommand.d.ts +8 -0
- package/dist/makeUseCommand.d.ts.map +1 -0
- package/dist/makeUseCommand.js +13 -0
- package/dist/mutate.d.ts +52 -34
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +137 -46
- package/dist/query.d.ts +19 -39
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +128 -72
- package/dist/routeParams.d.ts +1 -1
- package/dist/runtime.d.ts +7 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +27 -17
- package/dist/toast.d.ts +50 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +32 -0
- package/dist/withToast.d.ts +27 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +59 -0
- package/examples/streamMutation.ts +70 -0
- package/package.json +48 -50
- package/src/commander.ts +3393 -0
- package/src/{experimental/confirm.ts → confirm.ts} +10 -14
- package/src/errorReporter.ts +62 -74
- package/src/form.ts +56 -17
- package/src/intl.ts +12 -0
- package/src/lib.ts +47 -20
- package/src/makeClient.ts +568 -1134
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +6 -4
- package/src/mutate.ts +265 -127
- package/src/query.ts +197 -183
- package/src/runtime.ts +41 -20
- package/src/{experimental/toast.ts → toast.ts} +13 -27
- package/src/{experimental/withToast.ts → withToast.ts} +40 -12
- package/test/Mutation.test.ts +176 -23
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/lib.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 +3529 -122
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +182 -31
- package/test/form-validation-errors.test.ts +23 -19
- package/test/form.test.ts +20 -2
- package/test/lib.test.ts +240 -0
- package/test/makeClient.test.ts +292 -38
- package/test/streamFinal.test.ts +63 -0
- package/test/streamFn.test.ts +455 -0
- package/test/stubs.ts +218 -42
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +5 -2
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/dist/experimental/commander.d.ts +0 -359
- package/dist/experimental/commander.d.ts.map +0 -1
- package/dist/experimental/commander.js +0 -557
- package/dist/experimental/confirm.d.ts +0 -19
- package/dist/experimental/confirm.d.ts.map +0 -1
- package/dist/experimental/confirm.js +0 -28
- package/dist/experimental/intl.d.ts +0 -16
- package/dist/experimental/intl.d.ts.map +0 -1
- package/dist/experimental/intl.js +0 -5
- package/dist/experimental/makeUseCommand.d.ts +0 -8
- package/dist/experimental/makeUseCommand.d.ts.map +0 -1
- package/dist/experimental/makeUseCommand.js +0 -13
- package/dist/experimental/toast.d.ts +0 -47
- package/dist/experimental/toast.d.ts.map +0 -1
- package/dist/experimental/toast.js +0 -41
- package/dist/experimental/withToast.d.ts +0 -25
- package/dist/experimental/withToast.d.ts.map +0 -1
- package/dist/experimental/withToast.js +0 -45
- package/eslint.config.mjs +0 -24
- package/src/experimental/commander.ts +0 -1835
- package/src/experimental/intl.ts +0 -9
package/src/query.ts
CHANGED
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import { type
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
6
|
+
import { type DefaultError, type Enabled, experimental_streamedQuery as streamedQuery, type InitialDataFunction, type NonUndefinedGuard, type PlaceholderDataFunction, type QueryKey, type QueryObserverOptions, type QueryObserverResult, type RefetchOptions, useQuery as useTanstackQuery, useQueryClient, type UseQueryDefinedReturnType, type UseQueryReturnType } from "@tanstack/vue-query"
|
|
7
|
+
import { Array, Cause, type Context, Effect, Exit, Option, S } from "effect-app"
|
|
8
|
+
import { makeQueryKey, type Req } from "effect-app/client"
|
|
9
|
+
import type { RequestHandlerWithInput, RequestStreamHandlerWithInput } from "effect-app/client/clientFor"
|
|
10
|
+
import { CauseException, ServiceUnavailableError } from "effect-app/client/errors"
|
|
11
|
+
import * as Channel from "effect/Channel"
|
|
12
|
+
import * as Pull from "effect/Pull"
|
|
13
|
+
import * as Scope from "effect/Scope"
|
|
14
|
+
import type * as Stream from "effect/Stream"
|
|
10
15
|
import { type Span } from "effect/Tracer"
|
|
11
16
|
import { isHttpClientError } from "effect/unstable/http/HttpClientError"
|
|
12
17
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
13
18
|
import { computed, type ComputedRef, type MaybeRefOrGetter, ref, shallowRef, watch, type WatchSource } from "vue"
|
|
14
|
-
import {
|
|
19
|
+
import { reportRuntimeError } from "./lib.js"
|
|
20
|
+
import { makeRunPromise } from "./runtime.js"
|
|
15
21
|
|
|
16
22
|
// we must use interface extends, or we get the dreaded typescript error of isn't portable blabla @tanstack/vue-query/build/modern/types.js
|
|
17
23
|
// but because how they are dealing with some extends clause, we loose all properties except initialData
|
|
@@ -74,20 +80,68 @@ export interface CustomDefinedPlaceholderQueryOptions<
|
|
|
74
80
|
| PlaceholderDataFunction<NonFunctionGuard<TQueryData>, TError, NonFunctionGuard<TQueryData>, TQueryKey>
|
|
75
81
|
}
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
function swrToQuery<E, A>(r: {
|
|
84
|
+
error: CauseException<E> | undefined
|
|
85
|
+
data: A | undefined
|
|
86
|
+
isValidating: boolean
|
|
87
|
+
}): AsyncResult.AsyncResult<A, E> {
|
|
88
|
+
if (r.error !== undefined) {
|
|
89
|
+
return AsyncResult.failureWithPrevious(
|
|
90
|
+
r.error.originalCause,
|
|
91
|
+
{
|
|
92
|
+
previous: r.data === undefined ? Option.none() : Option.some(AsyncResult.success(r.data)),
|
|
93
|
+
waiting: r.isValidating
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
if (r.data !== undefined) {
|
|
98
|
+
return AsyncResult.success<A, E>(r.data, { waiting: r.isValidating })
|
|
82
99
|
}
|
|
100
|
+
|
|
101
|
+
return AsyncResult.initial(r.isValidating)
|
|
83
102
|
}
|
|
84
103
|
|
|
85
|
-
|
|
104
|
+
function streamToAsyncIterableWithCauseException<A, E, R>(
|
|
105
|
+
self: Stream.Stream<A, E, R>,
|
|
106
|
+
context: Context.Context<R>,
|
|
107
|
+
id: string
|
|
108
|
+
): AsyncIterable<A> {
|
|
109
|
+
return {
|
|
110
|
+
[Symbol.asyncIterator]() {
|
|
111
|
+
const runPromise = Effect.runPromiseWith(context)
|
|
112
|
+
const runPromiseExit = Effect.runPromiseExitWith(context)
|
|
113
|
+
const scope = Scope.makeUnsafe()
|
|
114
|
+
let pull: any
|
|
115
|
+
let currentIter: Iterator<A> | undefined
|
|
116
|
+
return {
|
|
117
|
+
async next(): Promise<IteratorResult<A>> {
|
|
118
|
+
if (currentIter) {
|
|
119
|
+
const next = currentIter.next()
|
|
120
|
+
if (!next.done) return next
|
|
121
|
+
currentIter = undefined
|
|
122
|
+
}
|
|
123
|
+
pull ??= await runPromise(Channel.toPullScoped((self as any).channel, scope))
|
|
124
|
+
const exit = await runPromiseExit(pull)
|
|
125
|
+
if (Exit.isSuccess(exit)) {
|
|
126
|
+
currentIter = (exit.value as any)[Symbol.iterator]()
|
|
127
|
+
return currentIter!.next()
|
|
128
|
+
} else if (Pull.isDoneCause((exit as any).cause)) {
|
|
129
|
+
return { done: true, value: undefined }
|
|
130
|
+
}
|
|
131
|
+
throw new CauseException((exit as any).cause, id)
|
|
132
|
+
},
|
|
133
|
+
return(_) {
|
|
134
|
+
return runPromise(Effect.as(Scope.close(scope, Exit.void), { done: true, value: undefined }) as any)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const makeQuery = <R>(getRuntime: () => Context.Context<R>) => {
|
|
86
142
|
const useQuery_: {
|
|
87
143
|
<I, A, E, Request extends Req, Name extends string>(
|
|
88
|
-
q:
|
|
89
|
-
| RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
90
|
-
| RequestHandler<A, E, R, Request, Name>
|
|
144
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
91
145
|
): {
|
|
92
146
|
<TData = A>(
|
|
93
147
|
arg: I | WatchSource<I> | undefined,
|
|
@@ -95,8 +149,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
95
149
|
): readonly [
|
|
96
150
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
97
151
|
ComputedRef<TData | undefined>,
|
|
98
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
99
|
-
UseQueryDefinedReturnType<TData,
|
|
152
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
153
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
100
154
|
]
|
|
101
155
|
|
|
102
156
|
<TData = A>(
|
|
@@ -105,8 +159,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
105
159
|
): readonly [
|
|
106
160
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
107
161
|
ComputedRef<TData>,
|
|
108
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
109
|
-
UseQueryDefinedReturnType<TData,
|
|
162
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
163
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
110
164
|
]
|
|
111
165
|
|
|
112
166
|
<TData = A>(
|
|
@@ -115,14 +169,12 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
115
169
|
): readonly [
|
|
116
170
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
117
171
|
ComputedRef<TData>,
|
|
118
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
119
|
-
UseQueryDefinedReturnType<TData,
|
|
172
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
173
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
120
174
|
]
|
|
121
175
|
}
|
|
122
176
|
} = <I, A, E, Request extends Req, Name extends string>(
|
|
123
|
-
q:
|
|
124
|
-
| RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
125
|
-
| RequestHandler<A, E, R, Request, Name>
|
|
177
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
126
178
|
) =>
|
|
127
179
|
<TData = A>(
|
|
128
180
|
arg: I | WatchSource<I> | undefined,
|
|
@@ -130,26 +182,20 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
130
182
|
options?: any
|
|
131
183
|
// TODO
|
|
132
184
|
) => {
|
|
133
|
-
// we wrap into
|
|
134
|
-
const runPromise =
|
|
135
|
-
_.then(
|
|
136
|
-
Exit.match({
|
|
137
|
-
onFailure: (cause) => Promise.reject(new KnownFiberFailure(cause)),
|
|
138
|
-
onSuccess: (value) => Promise.resolve(value)
|
|
139
|
-
})
|
|
140
|
-
))
|
|
185
|
+
// we wrap into CauseException because we want to keep the full cause of the failure.
|
|
186
|
+
const runPromise = makeRunPromise(getRuntime())
|
|
141
187
|
const arr = arg
|
|
142
|
-
const req: { value: I } = !arg
|
|
188
|
+
const req: { value: I } | undefined = !arg
|
|
143
189
|
? undefined
|
|
144
190
|
: typeof arr === "function"
|
|
145
191
|
? ({
|
|
146
192
|
get value() {
|
|
147
193
|
return (arr as any)()
|
|
148
194
|
}
|
|
149
|
-
}
|
|
150
|
-
: ref(arg)
|
|
195
|
+
})
|
|
196
|
+
: ref(arg) as any
|
|
151
197
|
const queryKey = makeQueryKey(q)
|
|
152
|
-
const
|
|
198
|
+
const projectionHash = (q as { queryKeyProjectionHash?: string }).queryKeyProjectionHash
|
|
153
199
|
|
|
154
200
|
const defaultOptions = {
|
|
155
201
|
// we do not want to throw errors, because we turn the success and error responses into a Result type
|
|
@@ -161,57 +207,31 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
161
207
|
throwOnError: false
|
|
162
208
|
}
|
|
163
209
|
|
|
164
|
-
const r = useTanstackQuery<A,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return false
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return retryCount < 5
|
|
177
|
-
},
|
|
178
|
-
queryKey,
|
|
179
|
-
queryFn: ({ meta, signal }) =>
|
|
180
|
-
runPromise(
|
|
181
|
-
handler
|
|
182
|
-
.pipe(
|
|
183
|
-
Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause)),
|
|
184
|
-
Effect.withSpan(`query ${q.id}`, {}, { captureStackTrace: false }),
|
|
185
|
-
meta?.["span"] ? Effect.withParentSpan(meta["span"] as Span) : (_) => _
|
|
186
|
-
),
|
|
187
|
-
{ signal }
|
|
188
|
-
)
|
|
210
|
+
const r = useTanstackQuery<A, CauseException<E>, TData>({
|
|
211
|
+
...defaultOptions,
|
|
212
|
+
...options,
|
|
213
|
+
retry: (retryCount, error) => {
|
|
214
|
+
if (error instanceof CauseException) {
|
|
215
|
+
if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
|
|
216
|
+
return false
|
|
217
|
+
}
|
|
189
218
|
}
|
|
190
|
-
: {
|
|
191
|
-
...defaultOptions,
|
|
192
|
-
...options,
|
|
193
|
-
retry: (retryCount, error) => {
|
|
194
|
-
if (error instanceof KnownFiberFailure) {
|
|
195
|
-
if (!isHttpClientError(error.error) && !S.is(ServiceUnavailableError)(error.error)) {
|
|
196
|
-
return false
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
219
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
220
|
+
return retryCount < 5
|
|
221
|
+
},
|
|
222
|
+
queryKey: projectionHash === undefined ? [...queryKey, req] : [...queryKey, req, projectionHash],
|
|
223
|
+
queryFn: ({ meta, signal }) =>
|
|
224
|
+
runPromise(
|
|
225
|
+
q
|
|
226
|
+
.handler(req?.value as I)
|
|
227
|
+
.pipe(
|
|
228
|
+
Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause)),
|
|
229
|
+
Effect.withSpan(`query ${q.id}`, {}, { captureStackTrace: false }),
|
|
230
|
+
meta?.["span"] ? Effect.withParentSpan(meta["span"] as Span) : (_) => _
|
|
231
|
+
),
|
|
232
|
+
{ signal }
|
|
233
|
+
)
|
|
234
|
+
})
|
|
215
235
|
|
|
216
236
|
const latestSuccess = shallowRef<TData>()
|
|
217
237
|
const result = computed((): AsyncResult.AsyncResult<TData, E> =>
|
|
@@ -239,129 +259,130 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
239
259
|
] as any
|
|
240
260
|
}
|
|
241
261
|
|
|
242
|
-
function swrToQuery<E, A>(r: {
|
|
243
|
-
error: KnownFiberFailure<E> | undefined
|
|
244
|
-
data: A | undefined
|
|
245
|
-
isValidating: boolean
|
|
246
|
-
}): AsyncResult.AsyncResult<A, E> {
|
|
247
|
-
if (r.error !== undefined) {
|
|
248
|
-
return AsyncResult.failureWithPrevious(
|
|
249
|
-
r.error.effectCause,
|
|
250
|
-
{
|
|
251
|
-
previous: r.data === undefined ? Option.none() : Option.some(AsyncResult.success(r.data)),
|
|
252
|
-
waiting: r.isValidating
|
|
253
|
-
}
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
if (r.data !== undefined) {
|
|
257
|
-
return AsyncResult.success<A, E>(r.data, { waiting: r.isValidating })
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return AsyncResult.initial(r.isValidating)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
262
|
const useQuery: {
|
|
264
263
|
/**
|
|
265
264
|
* Effect results are passed to the caller, including errors.
|
|
265
|
+
* When `I = void` the input argument may be omitted.
|
|
266
266
|
* @deprecated use client helpers instead (.query())
|
|
267
267
|
*/
|
|
268
|
-
<E, A, Request extends Req, Name extends string>(
|
|
269
|
-
self:
|
|
270
|
-
): {
|
|
271
|
-
// required options, with initialData
|
|
272
|
-
/**
|
|
273
|
-
* Effect results are passed to the caller, including errors.
|
|
274
|
-
*/
|
|
275
|
-
<TData = A>(
|
|
276
|
-
options: CustomDefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>
|
|
277
|
-
): readonly [
|
|
278
|
-
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
279
|
-
ComputedRef<TData>,
|
|
280
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
|
|
281
|
-
UseQueryReturnType<any, any>
|
|
282
|
-
]
|
|
283
|
-
<TData = A>(
|
|
284
|
-
options: CustomDefinedPlaceholderQueryOptions<A, E, TData>
|
|
285
|
-
): readonly [
|
|
286
|
-
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
287
|
-
ComputedRef<TData>,
|
|
288
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>, never, never>,
|
|
289
|
-
UseQueryDefinedReturnType<TData, KnownFiberFailure<E>>
|
|
290
|
-
]
|
|
291
|
-
// optional options, optional A
|
|
292
|
-
/**
|
|
293
|
-
* Effect results are passed to the caller, including errors.
|
|
294
|
-
*/
|
|
295
|
-
<TData = A>(options?: CustomUndefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>): readonly [
|
|
296
|
-
ComputedRef<AsyncResult.AsyncResult<A, E>>,
|
|
297
|
-
ComputedRef<A | undefined>,
|
|
298
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
|
|
299
|
-
UseQueryReturnType<any, any>
|
|
300
|
-
]
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Effect results are passed to the caller, including errors.
|
|
304
|
-
* @deprecated use client helpers instead (.query())
|
|
305
|
-
*/
|
|
306
|
-
<Arg, E, A, Request extends Req, Name extends string>(
|
|
307
|
-
self: RequestHandlerWithInput<Arg, A, E, R, Request, Name>
|
|
268
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
269
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
308
270
|
): {
|
|
309
|
-
// required options, with initialData
|
|
310
|
-
/**
|
|
311
|
-
* Effect results are passed to the caller, including errors.
|
|
312
|
-
*/
|
|
313
271
|
<TData = A>(
|
|
314
|
-
arg:
|
|
315
|
-
options: CustomDefinedInitialQueryOptions<A,
|
|
272
|
+
arg: I | WatchSource<I>,
|
|
273
|
+
options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
316
274
|
): readonly [
|
|
317
275
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
318
276
|
ComputedRef<TData>,
|
|
319
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
277
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
320
278
|
UseQueryReturnType<any, any>
|
|
321
279
|
]
|
|
322
|
-
// required options, with placeholderData
|
|
323
|
-
/**
|
|
324
|
-
* Effect results are passed to the caller, including errors.
|
|
325
|
-
*/
|
|
326
280
|
<TData = A>(
|
|
327
|
-
arg:
|
|
328
|
-
options: CustomDefinedPlaceholderQueryOptions<A,
|
|
281
|
+
arg: I | WatchSource<I>,
|
|
282
|
+
options: CustomDefinedPlaceholderQueryOptions<A, CauseException<E>, TData>
|
|
329
283
|
): readonly [
|
|
330
284
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
331
285
|
ComputedRef<TData>,
|
|
332
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
286
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
333
287
|
UseQueryReturnType<any, any>
|
|
334
288
|
]
|
|
335
|
-
// optional options, optional A
|
|
336
|
-
/**
|
|
337
|
-
* Effect results are passed to the caller, including errors.
|
|
338
|
-
*/
|
|
339
289
|
<TData = A>(
|
|
340
|
-
arg:
|
|
341
|
-
options?: CustomUndefinedInitialQueryOptions<A,
|
|
290
|
+
arg: I | WatchSource<I>,
|
|
291
|
+
options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
342
292
|
): readonly [
|
|
343
293
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
344
294
|
ComputedRef<TData | undefined>,
|
|
345
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
295
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
346
296
|
UseQueryReturnType<any, any>
|
|
347
297
|
]
|
|
348
298
|
}
|
|
349
|
-
} = (
|
|
299
|
+
} = ((
|
|
350
300
|
self: any
|
|
351
301
|
) => {
|
|
352
302
|
const q = useQuery_(self)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
Effect.isEffect(self.handler)
|
|
356
|
-
? q(undefined, argOrOptions)
|
|
357
|
-
: q(argOrOptions, options)
|
|
358
|
-
}
|
|
303
|
+
return (arg?: any, options?: any) => q(arg, options)
|
|
304
|
+
}) as any
|
|
359
305
|
return useQuery
|
|
360
306
|
}
|
|
361
307
|
|
|
362
308
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
363
309
|
export interface MakeQuery2<R> extends ReturnType<typeof makeQuery<R>> {}
|
|
364
310
|
|
|
311
|
+
type StreamQueryResult<A, E> = readonly [
|
|
312
|
+
ComputedRef<AsyncResult.AsyncResult<A[], E>>,
|
|
313
|
+
ComputedRef<A[] | undefined>,
|
|
314
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<A[], CauseException<E>>>,
|
|
315
|
+
UseQueryReturnType<any, any>
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
export const makeStreamQuery = <R>(getRuntime: () => Context.Context<R>) => {
|
|
319
|
+
const streamQuery_: {
|
|
320
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
321
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
322
|
+
): (arg: I | WatchSource<I>) => StreamQueryResult<A, E>
|
|
323
|
+
} = (q: any) => (arg?: any) => {
|
|
324
|
+
const context = getRuntime()
|
|
325
|
+
const arr = arg
|
|
326
|
+
const req: { value: any } | undefined = !arg
|
|
327
|
+
? undefined
|
|
328
|
+
: typeof arr === "function"
|
|
329
|
+
? ({
|
|
330
|
+
get value() {
|
|
331
|
+
return arr()
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
: ref(arg)
|
|
335
|
+
const queryKey = makeQueryKey(q)
|
|
336
|
+
|
|
337
|
+
const r = useTanstackQuery<any[], CauseException<any>, any[]>(
|
|
338
|
+
{
|
|
339
|
+
throwOnError: false,
|
|
340
|
+
retry: (retryCount: number, error: unknown) => {
|
|
341
|
+
if (error instanceof CauseException) {
|
|
342
|
+
if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return retryCount < 5
|
|
347
|
+
},
|
|
348
|
+
queryKey: [...queryKey, req],
|
|
349
|
+
queryFn: streamedQuery({
|
|
350
|
+
streamFn: () => {
|
|
351
|
+
const stream = q.handler(req?.value)
|
|
352
|
+
return streamToAsyncIterableWithCauseException(stream, context, q.id)
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
const latestSuccess = shallowRef<any[]>()
|
|
359
|
+
const result = computed((): AsyncResult.AsyncResult<any[], any> =>
|
|
360
|
+
swrToQuery({
|
|
361
|
+
error: r.error.value ?? undefined,
|
|
362
|
+
data: r.data.value === undefined ? latestSuccess.value : r.data.value,
|
|
363
|
+
isValidating: r.isFetching.value
|
|
364
|
+
})
|
|
365
|
+
)
|
|
366
|
+
watch(result, (value) => latestSuccess.value = Option.getOrUndefined(AsyncResult.value(value)), { immediate: true })
|
|
367
|
+
|
|
368
|
+
return [
|
|
369
|
+
result,
|
|
370
|
+
computed(() => latestSuccess.value),
|
|
371
|
+
(options?: RefetchOptions) =>
|
|
372
|
+
Effect.currentSpan.pipe(
|
|
373
|
+
Effect.orElseSucceed(() => null),
|
|
374
|
+
Effect.flatMap((span) => Effect.promise(() => r.refetch({ ...options, updateMeta: { span } })))
|
|
375
|
+
),
|
|
376
|
+
r
|
|
377
|
+
] as any
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return streamQuery_
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
384
|
+
export interface MakeStreamQuery2<R> extends ReturnType<typeof makeStreamQuery<R>> {}
|
|
385
|
+
|
|
365
386
|
function orPrevious<E, A>(result: AsyncResult.AsyncResult<A, E>) {
|
|
366
387
|
return AsyncResult.isFailure(result) && Option.isSome(result.previousSuccess)
|
|
367
388
|
? AsyncResult.success(result.previousSuccess.value, { waiting: result.waiting })
|
|
@@ -412,20 +433,13 @@ export const useUpdateQuery = () => {
|
|
|
412
433
|
const queryClient = useQueryClient()
|
|
413
434
|
|
|
414
435
|
const f: {
|
|
415
|
-
<A>(
|
|
416
|
-
query: RequestHandler<A, any, any, any, any>,
|
|
417
|
-
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
418
|
-
): void
|
|
419
436
|
<I, A>(
|
|
420
437
|
query: RequestHandlerWithInput<I, A, any, any, any, any>,
|
|
421
438
|
input: I,
|
|
422
439
|
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
423
440
|
): void
|
|
424
|
-
} = (query: any,
|
|
425
|
-
const
|
|
426
|
-
const key = updaterMaybe !== undefined
|
|
427
|
-
? [...makeQueryKey(query), updateOrInput]
|
|
428
|
-
: makeQueryKey(query)
|
|
441
|
+
} = (query: any, input: any, updater: any) => {
|
|
442
|
+
const key = [...makeQueryKey(query), input]
|
|
429
443
|
const data = queryClient.getQueryData(key)
|
|
430
444
|
if (data) {
|
|
431
445
|
queryClient.setQueryData(key, updater)
|
package/src/runtime.ts
CHANGED
|
@@ -1,31 +1,52 @@
|
|
|
1
|
-
import { ManagedRuntime } from "effect"
|
|
1
|
+
import { Exit, flow, ManagedRuntime } from "effect"
|
|
2
2
|
import { Effect, Layer, Logger } from "effect-app"
|
|
3
|
+
import { CauseException } from "effect-app/client/errors"
|
|
4
|
+
import { type Context } from "effect-app/Context"
|
|
3
5
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
},
|
|
6
|
+
export const makeAppRuntime = Effect.fnUntraced(function*<A, E>(layer: Layer.Layer<A, E>) {
|
|
7
|
+
const l = layer.pipe(
|
|
8
|
+
Layer.provide(Logger.layer([Logger.consolePretty()]))
|
|
9
|
+
) as Layer.Layer<A>
|
|
10
|
+
const mrt = ManagedRuntime.make(l)
|
|
11
|
+
yield* mrt.contextEffect
|
|
12
|
+
return Object.assign(mrt, {
|
|
13
|
+
[Symbol.dispose]() {
|
|
14
|
+
return Effect.runSync(mrt.disposeEffect)
|
|
15
|
+
},
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
[Symbol.asyncDispose]() {
|
|
18
|
+
return mrt.dispose()
|
|
19
|
+
}
|
|
20
|
+
}) // as we initialise here, there is no more error left.
|
|
21
|
+
})
|
|
22
22
|
|
|
23
|
-
export function initializeSync<A, E>(layer: Layer.Layer<A, E
|
|
23
|
+
export function initializeSync<A, E>(layer: Layer.Layer<A, E>) {
|
|
24
24
|
const runtime = Effect.runSync(makeAppRuntime(layer))
|
|
25
25
|
return runtime
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export function initializeAsync<A, E>(layer: Layer.Layer<A, E
|
|
28
|
+
export function initializeAsync<A, E>(layer: Layer.Layer<A, E>) {
|
|
29
29
|
return Effect
|
|
30
30
|
.runPromise(makeAppRuntime(layer))
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
// we wrap into CauseException because we want to keep the full cause of the failure.
|
|
34
|
+
export const makeRunPromise = <T>(services: Context<T>) =>
|
|
35
|
+
flow(Effect.runPromiseExitWith(services), (_) =>
|
|
36
|
+
_.then(
|
|
37
|
+
Exit.match({
|
|
38
|
+
onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")),
|
|
39
|
+
onSuccess: (value) => Promise.resolve(value)
|
|
40
|
+
})
|
|
41
|
+
))
|
|
42
|
+
|
|
43
|
+
export const makeRunSync = <T>(services: Context<T>) =>
|
|
44
|
+
flow(
|
|
45
|
+
Effect.runSyncExitWith(services),
|
|
46
|
+
Exit.match({
|
|
47
|
+
onFailure: (cause) => {
|
|
48
|
+
throw new CauseException(cause, "runSync")
|
|
49
|
+
},
|
|
50
|
+
onSuccess: (value) => value
|
|
51
|
+
})
|
|
52
|
+
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Effect, Option
|
|
2
|
-
import {
|
|
1
|
+
import { Context, Effect, Option } from "effect-app"
|
|
2
|
+
import { accessEffectFn } from "effect-app/Context"
|
|
3
3
|
|
|
4
4
|
export type ToastId = string | number
|
|
5
|
-
export type ToastOpts = { id?: ToastId; timeout?: number }
|
|
6
|
-
export type ToastOptsInternal = { id?: ToastId | null; timeout?: number }
|
|
5
|
+
export type ToastOpts = { id?: ToastId; timeout?: number; groupId?: string; requestId?: string }
|
|
6
|
+
export type ToastOptsInternal = { id?: ToastId | null; timeout?: number; groupId?: string; requestId?: string }
|
|
7
7
|
|
|
8
8
|
export type UseToast = () => {
|
|
9
9
|
error: (this: void, message: string, options?: ToastOpts) => ToastId
|
|
@@ -13,7 +13,7 @@ export type UseToast = () => {
|
|
|
13
13
|
dismiss: (this: void, id: ToastId) => void
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export class CurrentToastId extends
|
|
16
|
+
export class CurrentToastId extends Context.Opaque<CurrentToastId, { toastId: ToastId }>()("CurrentToastId") {}
|
|
17
17
|
|
|
18
18
|
/** fallback to CurrentToastId when available unless id is explicitly set to a value or null */
|
|
19
19
|
export const wrap = (toast: ReturnType<UseToast>) => {
|
|
@@ -41,26 +41,12 @@ export const wrap = (toast: ReturnType<UseToast>) => {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
extends proxify(ServiceMap.Opaque<Toast, ReturnType<typeof wrap>>()("Toast"))<Toast, ReturnType<typeof wrap>>()
|
|
46
|
-
{
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// const a = Layer.effect(Toast, Effect.sync(() => Toast.of(null as any)))
|
|
50
|
-
|
|
51
|
-
// const A = Toast.of({
|
|
52
|
-
// error: () => Effect.succeed(null as any),
|
|
53
|
-
// info: () => Effect.succeed(null as any),
|
|
54
|
-
// success: () => Effect.succeed(null as any),
|
|
55
|
-
// warning: () => Effect.succeed(null as any),
|
|
56
|
-
// dismiss: () => Effect.succeed(null as any)
|
|
57
|
-
// })
|
|
44
|
+
type ToastShape = ReturnType<typeof wrap>
|
|
58
45
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// })
|
|
46
|
+
export class Toast extends Context.Opaque<Toast, ToastShape>()("Toast") {
|
|
47
|
+
static readonly error = accessEffectFn(this, "error")
|
|
48
|
+
static readonly info = accessEffectFn(this, "info")
|
|
49
|
+
static readonly success = accessEffectFn(this, "success")
|
|
50
|
+
static readonly warning = accessEffectFn(this, "warning")
|
|
51
|
+
static readonly dismiss = accessEffectFn(this, "dismiss")
|
|
52
|
+
}
|