@effect-app/vue 4.0.0-beta.17 → 4.0.0-beta.171
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 +1098 -0
- package/dist/commander.d.ts +370 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +591 -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 +13 -4
- 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 +8 -9
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +34 -7
- package/dist/makeClient.d.ts +120 -277
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +155 -359
- 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 +4 -4
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +7 -7
- package/dist/query.d.ts +11 -15
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +19 -27
- package/dist/routeParams.d.ts +1 -1
- package/dist/runtime.d.ts +5 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +27 -17
- package/dist/toast.d.ts +46 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +32 -0
- package/dist/withToast.d.ts +26 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +49 -0
- package/eslint.config.mjs +2 -2
- package/package.json +48 -48
- package/src/{experimental/commander.ts → commander.ts} +930 -255
- package/src/{experimental/confirm.ts → confirm.ts} +10 -14
- package/src/errorReporter.ts +60 -72
- package/src/form.ts +55 -16
- package/src/intl.ts +12 -0
- package/src/lib.ts +50 -13
- package/src/makeClient.ts +454 -1023
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +3 -3
- package/src/mutate.ts +40 -32
- package/src/query.ts +39 -50
- package/src/runtime.ts +39 -18
- package/src/{experimental/toast.ts → toast.ts} +11 -25
- package/src/{experimental/withToast.ts → withToast.ts} +15 -6
- package/test/Mutation.test.ts +130 -10
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +2162 -118
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +80 -23
- 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 +217 -38
- package/test/stubs.ts +117 -40
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -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/src/experimental/intl.ts +0 -9
|
@@ -12,15 +12,15 @@ export interface CommanderResolved<RT, RTHooks>
|
|
|
12
12
|
export const makeUseCommand = Effect.fnUntraced(
|
|
13
13
|
function*<R = never, RTHooks = never>(rtHooks: Layer.Layer<RTHooks, never, R>) {
|
|
14
14
|
const cmndr = yield* Commander
|
|
15
|
-
const runtime = yield* Effect.
|
|
15
|
+
const runtime = yield* Effect.context<R>()
|
|
16
16
|
|
|
17
17
|
const comm = cmndr(runtime, rtHooks)
|
|
18
18
|
|
|
19
|
-
const command = {
|
|
19
|
+
const command: CommanderResolved<R, RTHooks> = {
|
|
20
20
|
...comm,
|
|
21
21
|
...CommanderStatic
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
return command
|
|
24
|
+
return command
|
|
25
25
|
}
|
|
26
26
|
)
|
package/src/mutate.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { type InvalidateOptions, type InvalidateQueryFilters, type QueryClient, useQueryClient } from "@tanstack/vue-query"
|
|
3
3
|
import { type Cause, Effect, type Exit, Option } from "effect-app"
|
|
4
|
-
import { type Req } from "effect-app/client"
|
|
4
|
+
import { makeQueryKey, type Req } from "effect-app/client"
|
|
5
5
|
import type { ClientForOptions, RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
|
|
6
6
|
import { tuple } from "effect-app/Function"
|
|
7
7
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
8
8
|
import { computed, type ComputedRef, shallowRef } from "vue"
|
|
9
|
-
import { makeQueryKey } from "./lib.js"
|
|
10
9
|
|
|
11
10
|
export const getQueryKey = (h: { id: string; options?: ClientForOptions }) => {
|
|
12
11
|
const key = makeQueryKey(h)
|
|
@@ -72,7 +71,7 @@ export interface MutationOptionsBase {
|
|
|
72
71
|
* By default we invalidate one level of the query key, e.g $project/$configuration.get, we invalidate $project.
|
|
73
72
|
* This can be overridden by providing a function that returns an array of filters and options.
|
|
74
73
|
*/
|
|
75
|
-
queryInvalidation?: (defaultKey: string[], name: string) => {
|
|
74
|
+
queryInvalidation?: (defaultKey: string[], name: string, input?: unknown, output?: Exit.Exit<unknown, unknown>) => {
|
|
76
75
|
filters?: InvalidateQueryFilters | undefined
|
|
77
76
|
options?: InvalidateOptions | undefined
|
|
78
77
|
}[]
|
|
@@ -158,40 +157,49 @@ export const invalidateQueries = (
|
|
|
158
157
|
)
|
|
159
158
|
)
|
|
160
159
|
|
|
161
|
-
const invalidateCache =
|
|
162
|
-
|
|
160
|
+
const invalidateCache = (input: unknown, output: Exit.Exit<unknown, unknown>) =>
|
|
161
|
+
Effect.suspend(() => {
|
|
162
|
+
const queryKey = getQueryKey(self)
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
if (options) {
|
|
165
|
+
const opts = options(queryKey, self.id, input, output)
|
|
166
|
+
if (!opts.length) {
|
|
167
|
+
return Effect.void
|
|
168
|
+
}
|
|
169
|
+
return Effect
|
|
170
|
+
.andThen(
|
|
171
|
+
Effect.annotateCurrentSpan({ queryKey, opts }),
|
|
172
|
+
Effect.forEach(opts, (_) => invalidateQueries(_.filters, _.options), { concurrency: "inherit" })
|
|
173
|
+
)
|
|
174
|
+
.pipe(Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false }))
|
|
168
175
|
}
|
|
176
|
+
|
|
177
|
+
if (!queryKey) return Effect.void
|
|
178
|
+
|
|
169
179
|
return Effect
|
|
170
180
|
.andThen(
|
|
171
|
-
Effect.annotateCurrentSpan({ queryKey
|
|
172
|
-
|
|
181
|
+
Effect.annotateCurrentSpan({ queryKey }),
|
|
182
|
+
invalidateQueries({ queryKey })
|
|
173
183
|
)
|
|
174
|
-
.pipe(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
184
|
+
.pipe(
|
|
185
|
+
Effect.tap(
|
|
186
|
+
// hand over control back to the event loop so that state can be updated..
|
|
187
|
+
// TODO: should we do this in general on any mutation, regardless of invalidation?
|
|
188
|
+
Effect.sleep(0)
|
|
189
|
+
),
|
|
190
|
+
Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false })
|
|
191
|
+
)
|
|
192
|
+
})
|
|
178
193
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// hand over control back to the event loop so that state can be updated..
|
|
187
|
-
// TODO: should we do this in general on any mutation, regardless of invalidation?
|
|
188
|
-
Effect.sleep(0)
|
|
189
|
-
),
|
|
190
|
-
Effect.withSpan("client.query.invalidation", {}, { captureStackTrace: false })
|
|
194
|
+
const handle = <A, E, R>(self: Effect.Effect<A, E, R>, input?: unknown) =>
|
|
195
|
+
self.pipe(
|
|
196
|
+
Effect.onExit((exit) =>
|
|
197
|
+
invalidateCache(
|
|
198
|
+
input,
|
|
199
|
+
exit
|
|
200
|
+
)
|
|
191
201
|
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const handle = <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.ensuring(self, invalidateCache)
|
|
202
|
+
)
|
|
195
203
|
|
|
196
204
|
return handle
|
|
197
205
|
}
|
|
@@ -221,7 +229,7 @@ export const makeMutation = () => {
|
|
|
221
229
|
const queryClient = useQueryClient()
|
|
222
230
|
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
223
231
|
const handler = self.handler
|
|
224
|
-
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i))
|
|
232
|
+
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i), i)
|
|
225
233
|
|
|
226
234
|
return Object.assign(r, { id: self.id }) as any
|
|
227
235
|
}
|
|
@@ -255,7 +263,7 @@ export const useMakeMutation = () => {
|
|
|
255
263
|
) => {
|
|
256
264
|
const handle = invalidateQueries(queryClient, self, options?.queryInvalidation)
|
|
257
265
|
const handler = self.handler
|
|
258
|
-
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i))
|
|
266
|
+
const r = Effect.isEffect(handler) ? handle(handler) : (i: I) => handle(handler(i), i)
|
|
259
267
|
|
|
260
268
|
return Object.assign(r, { id: self.id }) as any
|
|
261
269
|
}
|
package/src/query.ts
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
5
5
|
import { type DefaultError, type Enabled, 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"
|
|
6
|
-
import { Array, Cause,
|
|
7
|
-
import { type Req } from "effect-app/client"
|
|
6
|
+
import { Array, Cause, type Context, Effect, Option, S } from "effect-app"
|
|
7
|
+
import { makeQueryKey, type Req } from "effect-app/client"
|
|
8
8
|
import type { RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
|
|
9
|
-
import { ServiceUnavailableError } from "effect-app/client/errors"
|
|
9
|
+
import { CauseException, ServiceUnavailableError } from "effect-app/client/errors"
|
|
10
10
|
import { type Span } from "effect/Tracer"
|
|
11
11
|
import { isHttpClientError } from "effect/unstable/http/HttpClientError"
|
|
12
12
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
13
13
|
import { computed, type ComputedRef, type MaybeRefOrGetter, ref, shallowRef, watch, type WatchSource } from "vue"
|
|
14
|
-
import {
|
|
14
|
+
import { reportRuntimeError } from "./lib.js"
|
|
15
|
+
import { makeRunPromise } from "./runtime.js"
|
|
15
16
|
|
|
16
17
|
// 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
18
|
// but because how they are dealing with some extends clause, we loose all properties except initialData
|
|
@@ -74,15 +75,7 @@ export interface CustomDefinedPlaceholderQueryOptions<
|
|
|
74
75
|
| PlaceholderDataFunction<NonFunctionGuard<TQueryData>, TError, NonFunctionGuard<TQueryData>, TQueryKey>
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
export
|
|
78
|
-
readonly error: unknown
|
|
79
|
-
constructor(public effectCause: Cause.Cause<E>) {
|
|
80
|
-
super("Query failed with cause: " + Cause.squash(effectCause))
|
|
81
|
-
this.error = Cause.squash(effectCause)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
78
|
+
export const makeQuery = <R>(getRuntime: () => Context.Context<R>) => {
|
|
86
79
|
const useQuery_: {
|
|
87
80
|
<I, A, E, Request extends Req, Name extends string>(
|
|
88
81
|
q:
|
|
@@ -95,8 +88,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
95
88
|
): readonly [
|
|
96
89
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
97
90
|
ComputedRef<TData | undefined>,
|
|
98
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
99
|
-
UseQueryDefinedReturnType<TData,
|
|
91
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>, never, never>,
|
|
92
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
100
93
|
]
|
|
101
94
|
|
|
102
95
|
<TData = A>(
|
|
@@ -105,8 +98,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
105
98
|
): readonly [
|
|
106
99
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
107
100
|
ComputedRef<TData>,
|
|
108
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
109
|
-
UseQueryDefinedReturnType<TData,
|
|
101
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>, never, never>,
|
|
102
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
110
103
|
]
|
|
111
104
|
|
|
112
105
|
<TData = A>(
|
|
@@ -115,8 +108,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
115
108
|
): readonly [
|
|
116
109
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
117
110
|
ComputedRef<TData>,
|
|
118
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
119
|
-
UseQueryDefinedReturnType<TData,
|
|
111
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>, never, never>,
|
|
112
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
120
113
|
]
|
|
121
114
|
}
|
|
122
115
|
} = <I, A, E, Request extends Req, Name extends string>(
|
|
@@ -130,25 +123,21 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
130
123
|
options?: any
|
|
131
124
|
// TODO
|
|
132
125
|
) => {
|
|
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
|
-
))
|
|
126
|
+
// we wrap into CauseException because we want to keep the full cause of the failure.
|
|
127
|
+
const runPromise = makeRunPromise(getRuntime())
|
|
141
128
|
const arr = arg
|
|
142
129
|
const req: { value: I } = !arg
|
|
143
|
-
? undefined
|
|
130
|
+
? undefined as any
|
|
144
131
|
: typeof arr === "function"
|
|
145
132
|
? ({
|
|
146
133
|
get value() {
|
|
147
134
|
return (arr as any)()
|
|
148
135
|
}
|
|
149
|
-
}
|
|
136
|
+
})
|
|
150
137
|
: ref(arg)
|
|
151
138
|
const queryKey = makeQueryKey(q)
|
|
139
|
+
const projectionHash = (q as { queryKeyProjectionHash?: string }).queryKeyProjectionHash
|
|
140
|
+
const baseQueryKey = projectionHash === undefined ? queryKey : [...queryKey, projectionHash]
|
|
152
141
|
const handler = q.handler
|
|
153
142
|
|
|
154
143
|
const defaultOptions = {
|
|
@@ -161,21 +150,21 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
161
150
|
throwOnError: false
|
|
162
151
|
}
|
|
163
152
|
|
|
164
|
-
const r = useTanstackQuery<A,
|
|
153
|
+
const r = useTanstackQuery<A, CauseException<E>, TData>(
|
|
165
154
|
Effect.isEffect(handler)
|
|
166
155
|
? {
|
|
167
156
|
...defaultOptions,
|
|
168
157
|
...options,
|
|
169
158
|
retry: (retryCount, error) => {
|
|
170
|
-
if (error instanceof
|
|
171
|
-
if (!isHttpClientError(error.
|
|
159
|
+
if (error instanceof CauseException) {
|
|
160
|
+
if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
|
|
172
161
|
return false
|
|
173
162
|
}
|
|
174
163
|
}
|
|
175
164
|
|
|
176
165
|
return retryCount < 5
|
|
177
166
|
},
|
|
178
|
-
queryKey,
|
|
167
|
+
queryKey: baseQueryKey,
|
|
179
168
|
queryFn: ({ meta, signal }) =>
|
|
180
169
|
runPromise(
|
|
181
170
|
handler
|
|
@@ -191,15 +180,15 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
191
180
|
...defaultOptions,
|
|
192
181
|
...options,
|
|
193
182
|
retry: (retryCount, error) => {
|
|
194
|
-
if (error instanceof
|
|
195
|
-
if (!isHttpClientError(error.
|
|
183
|
+
if (error instanceof CauseException) {
|
|
184
|
+
if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
|
|
196
185
|
return false
|
|
197
186
|
}
|
|
198
187
|
}
|
|
199
188
|
|
|
200
189
|
return retryCount < 5
|
|
201
190
|
},
|
|
202
|
-
queryKey: [...queryKey, req],
|
|
191
|
+
queryKey: projectionHash === undefined ? [...queryKey, req] : [...queryKey, req, projectionHash],
|
|
203
192
|
queryFn: ({ meta, signal }) =>
|
|
204
193
|
runPromise(
|
|
205
194
|
handler(req.value)
|
|
@@ -240,13 +229,13 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
240
229
|
}
|
|
241
230
|
|
|
242
231
|
function swrToQuery<E, A>(r: {
|
|
243
|
-
error:
|
|
232
|
+
error: CauseException<E> | undefined
|
|
244
233
|
data: A | undefined
|
|
245
234
|
isValidating: boolean
|
|
246
235
|
}): AsyncResult.AsyncResult<A, E> {
|
|
247
236
|
if (r.error !== undefined) {
|
|
248
237
|
return AsyncResult.failureWithPrevious(
|
|
249
|
-
r.error.
|
|
238
|
+
r.error.originalCause,
|
|
250
239
|
{
|
|
251
240
|
previous: r.data === undefined ? Option.none() : Option.some(AsyncResult.success(r.data)),
|
|
252
241
|
waiting: r.isValidating
|
|
@@ -273,11 +262,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
273
262
|
* Effect results are passed to the caller, including errors.
|
|
274
263
|
*/
|
|
275
264
|
<TData = A>(
|
|
276
|
-
options: CustomDefinedInitialQueryOptions<A,
|
|
265
|
+
options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
277
266
|
): readonly [
|
|
278
267
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
279
268
|
ComputedRef<TData>,
|
|
280
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
269
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
281
270
|
UseQueryReturnType<any, any>
|
|
282
271
|
]
|
|
283
272
|
<TData = A>(
|
|
@@ -285,17 +274,17 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
285
274
|
): readonly [
|
|
286
275
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
287
276
|
ComputedRef<TData>,
|
|
288
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
289
|
-
UseQueryDefinedReturnType<TData,
|
|
277
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>, never, never>,
|
|
278
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
290
279
|
]
|
|
291
280
|
// optional options, optional A
|
|
292
281
|
/**
|
|
293
282
|
* Effect results are passed to the caller, including errors.
|
|
294
283
|
*/
|
|
295
|
-
<TData = A>(options?: CustomUndefinedInitialQueryOptions<A,
|
|
284
|
+
<TData = A>(options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>): readonly [
|
|
296
285
|
ComputedRef<AsyncResult.AsyncResult<A, E>>,
|
|
297
286
|
ComputedRef<A | undefined>,
|
|
298
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
287
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
299
288
|
UseQueryReturnType<any, any>
|
|
300
289
|
]
|
|
301
290
|
}
|
|
@@ -312,11 +301,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
312
301
|
*/
|
|
313
302
|
<TData = A>(
|
|
314
303
|
arg: Arg | WatchSource<Arg>,
|
|
315
|
-
options: CustomDefinedInitialQueryOptions<A,
|
|
304
|
+
options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
316
305
|
): readonly [
|
|
317
306
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
318
307
|
ComputedRef<TData>,
|
|
319
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
308
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
320
309
|
UseQueryReturnType<any, any>
|
|
321
310
|
]
|
|
322
311
|
// required options, with placeholderData
|
|
@@ -325,11 +314,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
325
314
|
*/
|
|
326
315
|
<TData = A>(
|
|
327
316
|
arg: Arg | WatchSource<Arg>,
|
|
328
|
-
options: CustomDefinedPlaceholderQueryOptions<A,
|
|
317
|
+
options: CustomDefinedPlaceholderQueryOptions<A, CauseException<E>, TData>
|
|
329
318
|
): readonly [
|
|
330
319
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
331
320
|
ComputedRef<TData>,
|
|
332
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
321
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
333
322
|
UseQueryReturnType<any, any>
|
|
334
323
|
]
|
|
335
324
|
// optional options, optional A
|
|
@@ -338,11 +327,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
338
327
|
*/
|
|
339
328
|
<TData = A>(
|
|
340
329
|
arg: Arg | WatchSource<Arg>,
|
|
341
|
-
options?: CustomUndefinedInitialQueryOptions<A,
|
|
330
|
+
options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
342
331
|
): readonly [
|
|
343
332
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
344
333
|
ComputedRef<TData | undefined>,
|
|
345
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
334
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
346
335
|
UseQueryReturnType<any, any>
|
|
347
336
|
]
|
|
348
337
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
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, never>
|
|
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
23
|
export function initializeSync<A, E>(layer: Layer.Layer<A, E, never>) {
|
|
24
24
|
const runtime = Effect.runSync(makeAppRuntime(layer))
|
|
@@ -29,3 +29,24 @@ export function initializeAsync<A, E>(layer: Layer.Layer<A, E, never>) {
|
|
|
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,5 +1,5 @@
|
|
|
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
5
|
export type ToastOpts = { id?: ToastId; timeout?: number }
|
|
@@ -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
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Cause, Effect, Layer, type Option
|
|
1
|
+
import { Cause, Context, Effect, Layer, type Option } from "effect-app"
|
|
2
2
|
import { wrapEffect } from "effect-app/utils"
|
|
3
3
|
import { CurrentToastId, Toast } from "./toast.js"
|
|
4
4
|
|
|
5
5
|
export interface ToastOptions<A, E, Args extends ReadonlyArray<unknown>, WaiR, SucR, ErrR> {
|
|
6
6
|
stableToastId?: undefined | string | ((...args: Args) => string | undefined)
|
|
7
7
|
timeout?: number
|
|
8
|
+
showSpanInfo?: false
|
|
8
9
|
onWaiting:
|
|
9
10
|
| string
|
|
10
11
|
| ((...args: Args) => string | null)
|
|
@@ -33,10 +34,10 @@ export interface ToastOptions<A, E, Args extends ReadonlyArray<unknown>, WaiR, S
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
36
|
-
export class WithToast extends
|
|
37
|
+
export class WithToast extends Context.Service<WithToast>()("WithToast", {
|
|
37
38
|
make: Effect.gen(function*() {
|
|
38
39
|
const toast = yield* Toast
|
|
39
|
-
return <A, E, Args extends
|
|
40
|
+
return <A, E, Args extends readonly unknown[], R, WaiR = never, SucR = never, ErrR = never>(
|
|
40
41
|
options: ToastOptions<A, E, Args, WaiR, SucR, ErrR>
|
|
41
42
|
) =>
|
|
42
43
|
Effect.fnUntraced(function*(self: Effect.Effect<A, E, R>, ...args: Args) {
|
|
@@ -74,15 +75,23 @@ export class WithToast extends ServiceMap.Service<WithToast>()("WithToast", {
|
|
|
74
75
|
return
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
const spanInfo = options.showSpanInfo !== false
|
|
79
|
+
? yield* Effect.currentSpan.pipe(
|
|
80
|
+
Effect.map((span) => `\nTrace: ${span.traceId}\nSpan: ${span.spanId}`),
|
|
81
|
+
Effect.orElseSucceed(() => "")
|
|
82
|
+
)
|
|
83
|
+
: ""
|
|
84
|
+
|
|
77
85
|
const t = yield* wrapEffect(options.onFailure)(Cause.findErrorOption(cause), ...args)
|
|
78
86
|
const opts = { timeout: baseTimeout * 2 }
|
|
79
87
|
|
|
80
88
|
if (typeof t === "object") {
|
|
89
|
+
const message = t.message + spanInfo
|
|
81
90
|
return t.level === "warn"
|
|
82
|
-
? yield* toast.warning(
|
|
83
|
-
: yield* toast.error(
|
|
91
|
+
? yield* toast.warning(message, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
92
|
+
: yield* toast.error(message, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
84
93
|
}
|
|
85
|
-
yield* toast.error(t, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
94
|
+
yield* toast.error(t + spanInfo, toastId !== undefined ? { ...opts, id: toastId } : opts)
|
|
86
95
|
}, Effect.uninterruptible)),
|
|
87
96
|
toastId !== undefined ? Effect.provideService(CurrentToastId, CurrentToastId.of({ toastId })) : (_) => _
|
|
88
97
|
)
|