@effect-app/vue 4.0.0-beta.8 → 4.0.0-beta.82

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +535 -0
  2. package/dist/{experimental/commander.d.ts → commander.d.ts} +53 -53
  3. package/dist/commander.d.ts.map +1 -0
  4. package/dist/commander.js +556 -0
  5. package/dist/{experimental/confirm.d.ts → confirm.d.ts} +2 -2
  6. package/dist/confirm.d.ts.map +1 -0
  7. package/dist/confirm.js +28 -0
  8. package/dist/form.d.ts +9 -0
  9. package/dist/form.d.ts.map +1 -1
  10. package/dist/form.js +38 -9
  11. package/dist/intl.d.ts +15 -0
  12. package/dist/intl.d.ts.map +1 -0
  13. package/dist/intl.js +9 -0
  14. package/dist/makeClient.d.ts +18 -241
  15. package/dist/makeClient.d.ts.map +1 -1
  16. package/dist/makeClient.js +12 -335
  17. package/dist/{experimental/makeUseCommand.d.ts → makeUseCommand.d.ts} +1 -1
  18. package/dist/makeUseCommand.d.ts.map +1 -0
  19. package/dist/makeUseCommand.js +13 -0
  20. package/dist/mutate.d.ts +1 -1
  21. package/dist/mutate.d.ts.map +1 -1
  22. package/dist/mutate.js +2 -2
  23. package/dist/query.d.ts +10 -14
  24. package/dist/query.d.ts.map +1 -1
  25. package/dist/query.js +23 -23
  26. package/dist/runtime.d.ts +3 -0
  27. package/dist/runtime.d.ts.map +1 -1
  28. package/dist/runtime.js +15 -3
  29. package/dist/{experimental/toast.d.ts → toast.d.ts} +9 -10
  30. package/dist/toast.d.ts.map +1 -0
  31. package/dist/toast.js +32 -0
  32. package/dist/{experimental/withToast.d.ts → withToast.d.ts} +3 -3
  33. package/dist/withToast.d.ts.map +1 -0
  34. package/dist/withToast.js +45 -0
  35. package/package.json +43 -43
  36. package/src/{experimental/commander.ts → commander.ts} +826 -202
  37. package/src/{experimental/confirm.ts → confirm.ts} +2 -2
  38. package/src/form.ts +46 -8
  39. package/src/intl.ts +12 -0
  40. package/src/makeClient.ts +27 -924
  41. package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +1 -1
  42. package/src/mutate.ts +1 -1
  43. package/src/query.ts +44 -45
  44. package/src/runtime.ts +25 -2
  45. package/src/{experimental/toast.ts → toast.ts} +11 -25
  46. package/src/{experimental/withToast.ts → withToast.ts} +3 -3
  47. package/test/Mutation.test.ts +77 -7
  48. package/test/dist/form.test.d.ts.map +1 -1
  49. package/test/dist/stubs.d.ts +273 -67
  50. package/test/dist/stubs.d.ts.map +1 -1
  51. package/test/dist/stubs.js +34 -15
  52. package/test/form-validation-errors.test.ts +23 -19
  53. package/test/form.test.ts +20 -2
  54. package/test/makeClient.test.ts +38 -23
  55. package/test/stubs.ts +45 -18
  56. package/tsconfig.json +0 -1
  57. package/dist/experimental/commander.d.ts.map +0 -1
  58. package/dist/experimental/commander.js +0 -558
  59. package/dist/experimental/confirm.d.ts.map +0 -1
  60. package/dist/experimental/confirm.js +0 -28
  61. package/dist/experimental/intl.d.ts +0 -16
  62. package/dist/experimental/intl.d.ts.map +0 -1
  63. package/dist/experimental/intl.js +0 -5
  64. package/dist/experimental/makeUseCommand.d.ts.map +0 -1
  65. package/dist/experimental/makeUseCommand.js +0 -13
  66. package/dist/experimental/toast.d.ts.map +0 -1
  67. package/dist/experimental/toast.js +0 -41
  68. package/dist/experimental/withToast.d.ts.map +0 -1
  69. package/dist/experimental/withToast.js +0 -45
  70. package/src/experimental/intl.ts +0 -9
@@ -12,7 +12,7 @@ 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.services<R>()
15
+ const runtime = yield* Effect.context<R>()
16
16
 
17
17
  const comm = cmndr(runtime, rtHooks)
18
18
 
package/src/mutate.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
3
2
  import { type InvalidateOptions, type InvalidateQueryFilters, type QueryClient, useQueryClient } from "@tanstack/vue-query"
4
3
  import { type Cause, Effect, type Exit, Option } from "effect-app"
5
4
  import { type Req } from "effect-app/client"
6
5
  import type { ClientForOptions, RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
7
6
  import { tuple } from "effect-app/Function"
7
+ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
8
8
  import { computed, type ComputedRef, shallowRef } from "vue"
9
9
  import { makeQueryKey } from "./lib.js"
10
10
 
package/src/query.ts CHANGED
@@ -2,16 +2,17 @@
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
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
6
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"
7
- import { Array, Cause, Effect, Exit, flow, Option, S, type ServiceMap } from "effect-app"
6
+ import { Array, Cause, type Context, Effect, Option, S } from "effect-app"
8
7
  import { type Req } from "effect-app/client"
9
8
  import type { RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
10
- import { ServiceUnavailableError } from "effect-app/client/errors"
9
+ import { CauseException, ServiceUnavailableError } from "effect-app/client/errors"
11
10
  import { type Span } from "effect/Tracer"
12
11
  import { isHttpClientError } from "effect/unstable/http/HttpClientError"
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
14
  import { makeQueryKey, 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 class KnownFiberFailure<E> extends Error {
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, KnownFiberFailure<E>>, never, never>,
99
- UseQueryDefinedReturnType<TData, KnownFiberFailure<E>>
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, KnownFiberFailure<E>>, never, never>,
109
- UseQueryDefinedReturnType<TData, KnownFiberFailure<E>>
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, KnownFiberFailure<E>>, never, never>,
119
- UseQueryDefinedReturnType<TData, KnownFiberFailure<E>>
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,14 +123,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
130
123
  options?: any
131
124
  // TODO
132
125
  ) => {
133
- // we wrap into KnownFiberFailure because we want to keep the full cause of the failure.
134
- const runPromise = flow(Effect.runPromiseExitWith(getRuntime()), (_) =>
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
130
  ? undefined
@@ -151,13 +138,24 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
151
138
  const queryKey = makeQueryKey(q)
152
139
  const handler = q.handler
153
140
 
154
- const r = useTanstackQuery<A, KnownFiberFailure<E>, TData>(
141
+ const defaultOptions = {
142
+ // we do not want to throw errors, because we turn the success and error responses into a Result type
143
+ // why don't we turn the error/success response into a Result type before returning to tanstack query? because we want to leverage tanstack query's retry and caching mechanism, which relies on throwing errors to trigger retries, and we don't want to interfere with that by catching the errors too early.
144
+ // but if we allow tanstack query to throw, it will trigger the error boundary in Vue - via a "watcher callback" error - which we currently report and log, which is not what we want.
145
+ // TODO: we might want to rethink the strategy of how to handle errors that happen after the initial load.
146
+ // For suspense, the initial load is captured by the suspense boundary.
147
+ // For subsequent loads (or non suspense use) we currently are required to use the QueryResult component to conditionally render error/loading/etc.
148
+ throwOnError: false
149
+ }
150
+
151
+ const r = useTanstackQuery<A, CauseException<E>, TData>(
155
152
  Effect.isEffect(handler)
156
153
  ? {
154
+ ...defaultOptions,
157
155
  ...options,
158
156
  retry: (retryCount, error) => {
159
- if (error instanceof KnownFiberFailure) {
160
- if (!isHttpClientError(error.error) && !S.is(ServiceUnavailableError)(error.error)) {
157
+ if (error instanceof CauseException) {
158
+ if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
161
159
  return false
162
160
  }
163
161
  }
@@ -177,10 +175,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
177
175
  )
178
176
  }
179
177
  : {
178
+ ...defaultOptions,
180
179
  ...options,
181
180
  retry: (retryCount, error) => {
182
- if (error instanceof KnownFiberFailure) {
183
- if (!isHttpClientError(error.error) && !S.is(ServiceUnavailableError)(error.error)) {
181
+ if (error instanceof CauseException) {
182
+ if (!isHttpClientError(error.cause) && !S.is(ServiceUnavailableError)(error.cause)) {
184
183
  return false
185
184
  }
186
185
  }
@@ -228,13 +227,13 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
228
227
  }
229
228
 
230
229
  function swrToQuery<E, A>(r: {
231
- error: KnownFiberFailure<E> | undefined
230
+ error: CauseException<E> | undefined
232
231
  data: A | undefined
233
232
  isValidating: boolean
234
233
  }): AsyncResult.AsyncResult<A, E> {
235
234
  if (r.error !== undefined) {
236
235
  return AsyncResult.failureWithPrevious(
237
- r.error.effectCause,
236
+ r.error.originalCause,
238
237
  {
239
238
  previous: r.data === undefined ? Option.none() : Option.some(AsyncResult.success(r.data)),
240
239
  waiting: r.isValidating
@@ -261,11 +260,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
261
260
  * Effect results are passed to the caller, including errors.
262
261
  */
263
262
  <TData = A>(
264
- options: CustomDefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>
263
+ options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
265
264
  ): readonly [
266
265
  ComputedRef<AsyncResult.AsyncResult<TData, E>>,
267
266
  ComputedRef<TData>,
268
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
267
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
269
268
  UseQueryReturnType<any, any>
270
269
  ]
271
270
  <TData = A>(
@@ -273,17 +272,17 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
273
272
  ): readonly [
274
273
  ComputedRef<AsyncResult.AsyncResult<TData, E>>,
275
274
  ComputedRef<TData>,
276
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>, never, never>,
277
- UseQueryDefinedReturnType<TData, KnownFiberFailure<E>>
275
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>, never, never>,
276
+ UseQueryDefinedReturnType<TData, CauseException<E>>
278
277
  ]
279
278
  // optional options, optional A
280
279
  /**
281
280
  * Effect results are passed to the caller, including errors.
282
281
  */
283
- <TData = A>(options?: CustomUndefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>): readonly [
282
+ <TData = A>(options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>): readonly [
284
283
  ComputedRef<AsyncResult.AsyncResult<A, E>>,
285
284
  ComputedRef<A | undefined>,
286
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
285
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
287
286
  UseQueryReturnType<any, any>
288
287
  ]
289
288
  }
@@ -300,11 +299,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
300
299
  */
301
300
  <TData = A>(
302
301
  arg: Arg | WatchSource<Arg>,
303
- options: CustomDefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>
302
+ options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
304
303
  ): readonly [
305
304
  ComputedRef<AsyncResult.AsyncResult<TData, E>>,
306
305
  ComputedRef<TData>,
307
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
306
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
308
307
  UseQueryReturnType<any, any>
309
308
  ]
310
309
  // required options, with placeholderData
@@ -313,11 +312,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
313
312
  */
314
313
  <TData = A>(
315
314
  arg: Arg | WatchSource<Arg>,
316
- options: CustomDefinedPlaceholderQueryOptions<A, KnownFiberFailure<E>, TData>
315
+ options: CustomDefinedPlaceholderQueryOptions<A, CauseException<E>, TData>
317
316
  ): readonly [
318
317
  ComputedRef<AsyncResult.AsyncResult<TData, E>>,
319
318
  ComputedRef<TData>,
320
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
319
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
321
320
  UseQueryReturnType<any, any>
322
321
  ]
323
322
  // optional options, optional A
@@ -326,11 +325,11 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
326
325
  */
327
326
  <TData = A>(
328
327
  arg: Arg | WatchSource<Arg>,
329
- options?: CustomUndefinedInitialQueryOptions<A, KnownFiberFailure<E>, TData>
328
+ options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
330
329
  ): readonly [
331
330
  ComputedRef<AsyncResult.AsyncResult<TData, E>>,
332
331
  ComputedRef<TData | undefined>,
333
- (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, KnownFiberFailure<E>>>,
332
+ (options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
334
333
  UseQueryReturnType<any, any>
335
334
  ]
336
335
  }
package/src/runtime.ts CHANGED
@@ -1,5 +1,7 @@
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
6
  export function makeAppRuntime<A, E>(layer: Layer.Layer<A, E>) {
5
7
  return Effect.gen(function*() {
@@ -7,7 +9,7 @@ export function makeAppRuntime<A, E>(layer: Layer.Layer<A, E>) {
7
9
  Layer.provide(Logger.layer([Logger.consolePretty()]))
8
10
  ) as Layer.Layer<A, never>
9
11
  const mrt = ManagedRuntime.make(l)
10
- yield* mrt.servicesEffect
12
+ yield* mrt.contextEffect
11
13
  return Object.assign(mrt, {
12
14
  [Symbol.dispose]() {
13
15
  return Effect.runSync(mrt.disposeEffect)
@@ -29,3 +31,24 @@ export function initializeAsync<A, E>(layer: Layer.Layer<A, E, never>) {
29
31
  return Effect
30
32
  .runPromise(makeAppRuntime(layer))
31
33
  }
34
+
35
+ // we wrap into CauseException because we want to keep the full cause of the failure.
36
+ export const makeRunPromise = <T>(services: Context<T>) =>
37
+ flow(Effect.runPromiseExitWith(services), (_) =>
38
+ _.then(
39
+ Exit.match({
40
+ onFailure: (cause) => Promise.reject(new CauseException(cause, "runPromise")),
41
+ onSuccess: (value) => Promise.resolve(value)
42
+ })
43
+ ))
44
+
45
+ export const makeRunSync = <T>(services: Context<T>) =>
46
+ flow(
47
+ Effect.runSyncExitWith(services),
48
+ Exit.match({
49
+ onFailure: (cause) => {
50
+ throw new CauseException(cause, "runSync")
51
+ },
52
+ onSuccess: (value) => value
53
+ })
54
+ )
@@ -1,5 +1,5 @@
1
- import { Effect, Option, ServiceMap } from "effect-app"
2
- import { proxify } from "effect-app/ServiceMap"
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 ServiceMap.Opaque<CurrentToastId, { toastId: ToastId }>()("CurrentToastId") {}
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
- export class Toast
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
- // const b = Toast.info("test")
60
-
61
- // const a2 = Toast.use((_) => _.error("test"))
62
-
63
- // const b2 = Effect.gen(function*() {
64
- // const toast = yield* Toast
65
- // toast.error("test")
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,4 +1,4 @@
1
- import { Cause, Effect, Layer, type Option, ServiceMap } from "effect-app"
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
 
@@ -33,10 +33,10 @@ export interface ToastOptions<A, E, Args extends ReadonlyArray<unknown>, WaiR, S
33
33
  }
34
34
 
35
35
  // @effect-diagnostics-next-line missingEffectServiceDependency:off
36
- export class WithToast extends ServiceMap.Service<WithToast>()("WithToast", {
36
+ export class WithToast extends Context.Service<WithToast>()("WithToast", {
37
37
  make: Effect.gen(function*() {
38
38
  const toast = yield* Toast
39
- return <A, E, Args extends Array<unknown>, R, WaiR = never, SucR = never, ErrR = never>(
39
+ return <A, E, Args extends readonly unknown[], R, WaiR = never, SucR = never, ErrR = never>(
40
40
  options: ToastOptions<A, E, Args, WaiR, SucR, ErrR>
41
41
  ) =>
42
42
  Effect.fnUntraced(function*(self: Effect.Effect<A, E, R>, ...args: Args) {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { it } from "@effect/vitest"
3
3
  import { Cause, Effect, Exit, Fiber, Option } from "effect-app"
4
- import { CommandContext, DefaultIntl } from "../src/experimental/commander.js"
4
+ import { CommandContext, DefaultIntl } from "../src/commander.js"
5
5
  import { AsyncResult } from "../src/lib.js"
6
6
  import { useExperimental } from "./stubs.js"
7
7
 
@@ -71,7 +71,10 @@ describe("alt2", () => {
71
71
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
72
72
  })),
73
73
  Effect.tap(() =>
74
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
74
+ Effect.currentSpan.pipe(
75
+ Effect.map((_) => _.name),
76
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
77
+ )
75
78
  ),
76
79
  Effect.tap(() => Effect.sync(() => executed = true))
77
80
  )
@@ -125,7 +128,10 @@ it.live("works", () =>
125
128
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
126
129
  })),
127
130
  Effect.tap(() =>
128
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
131
+ Effect.currentSpan.pipe(
132
+ Effect.map((_) => _.name),
133
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
134
+ )
129
135
  ),
130
136
  Effect.tap(() => Effect.sync(() => executed = true))
131
137
  )
@@ -175,7 +181,10 @@ it.live("works non-gen", () =>
175
181
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
176
182
  })),
177
183
  Effect.tap(() =>
178
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
184
+ Effect.currentSpan.pipe(
185
+ Effect.map((_) => _.name),
186
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
187
+ )
179
188
  ),
180
189
  Effect.tap(() => Effect.sync(() => executed = true))
181
190
  )
@@ -317,7 +326,10 @@ it.live("with toasts", () =>
317
326
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
318
327
  })),
319
328
  Effect.tap(() =>
320
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
329
+ Effect.currentSpan.pipe(
330
+ Effect.map((_) => _.name),
331
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
332
+ )
321
333
  ),
322
334
  // WithToast.handle({
323
335
  // onFailure: "failed",
@@ -471,7 +483,10 @@ it.live("works with alt", () =>
471
483
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
472
484
  })),
473
485
  Effect.tap(() =>
474
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
486
+ Effect.currentSpan.pipe(
487
+ Effect.map((_) => _.name),
488
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
489
+ )
475
490
  ),
476
491
  Effect.tap(() => Effect.sync(() => executed = true))
477
492
  )
@@ -624,7 +639,10 @@ it.live("with toasts with alt", () =>
624
639
  expect(yield* Effect.currentSpan.pipe(Effect.map((_) => _.name))).toBe("Test Action")
625
640
  })),
626
641
  Effect.tap(() =>
627
- Effect.currentSpan.pipe(Effect.map((_) => _.name), Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action"))))
642
+ Effect.currentSpan.pipe(
643
+ Effect.map((_) => _.name),
644
+ Effect.tap((_) => Effect.sync(() => expect(_).toBe("Test Action")))
645
+ )
628
646
  ),
629
647
  Command.withDefaultToast(),
630
648
  Effect.tap(() => Effect.sync(() => executed = true))
@@ -758,3 +776,55 @@ it.live("defect with alt", () =>
758
776
  expect(toasts.length).toBe(1) // toast should show error
759
777
  expect(toasts[0].message).toBe("Test Action unexpected error, please try again shortly.")
760
778
  }))
779
+
780
+ describe("state-in-toast", () => {
781
+ it("works", () => {
782
+ const toasts: any[] = []
783
+ const removeMutation = Object.assign(
784
+ Effect.fn(function*(_item: string) {
785
+ yield* Effect.sleep(1000)
786
+ }),
787
+ { id: "remove_thing" }
788
+ )
789
+
790
+ const item = "x"
791
+
792
+ const Command = useExperimental({ toasts, messages: DefaultIntl.en })
793
+
794
+ Command.fn(removeMutation, {
795
+ state: () => ({ item }),
796
+ waitKey: (id) => `${id}.${item}`,
797
+ blockKey: () => `modify_thing.${item}`
798
+ // allowed: () => role.value === "admin"
799
+ })(
800
+ function*() {
801
+ // yield* Command.confirmOrInterrupt(yield* I18n.formatMessage({ id: "confirm.remove_item" }, { item }))
802
+ yield* removeMutation(item)
803
+ },
804
+ Command.withDefaultToast({
805
+ onSuccess: (a, b, c, d) => {
806
+ console.log("Success", { a, b, c, d })
807
+ expectTypeOf(d.state).toEqualTypeOf<{ readonly item: "x" }>()
808
+ }
809
+ })
810
+ )
811
+
812
+ Command.fn(removeMutation, {
813
+ state: () => ({ item }),
814
+ waitKey: (id) => `${id}.${item}`,
815
+ blockKey: () => `modify_thing.${item}`
816
+ // allowed: () => role.value === "admin"
817
+ })(
818
+ function*() {
819
+ // yield* Command.confirmOrInterrupt(yield* I18n.formatMessage({ id: "confirm.remove_item" }, { item }))
820
+ yield* removeMutation(item)
821
+ },
822
+ Command.withDefaultToast({
823
+ onSuccess: (a, b, c) => {
824
+ console.log("Success", { a, b, c })
825
+ expectTypeOf(c).toEqualTypeOf<undefined>()
826
+ }
827
+ })
828
+ )
829
+ })
830
+ })
@@ -1 +1 @@
1
- {"version":3,"file":"form.test.d.ts","sourceRoot":"","sources":["../form.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,CAAC,EAAE,MAAM,YAAY,CAAA;;;;;;;;;;;;;;;;;AAGtC,qBAAa,YAAa,SAAQ,iBAShC;CAAG;;;;AAEL,qBAAa,mBAAoB,SAAQ,wBAEvC;CAAG;;;;;;;;;;;AAEL,qBAAa,WAAY,SAAQ,gBAK/B;CAAG;;;;;;;;AAEL,cAAM,MAAO,SAAQ,WAEnB;CAAG;;;;;;;;AAEL,cAAM,MAAO,SAAQ,WAEnB;CAAG;;;;;;;;;;;AAEL,cAAM,QAAS,SAAQ,aAGrB;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBL,qBAAa,cAAe,SAAQ,mBAGlC;CAAG"}
1
+ {"version":3,"file":"form.test.d.ts","sourceRoot":"","sources":["../form.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,CAAC,EAAE,MAAM,YAAY,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGtC,qBAAa,YAAa,SAAQ,iBAahC;CAAG;;;;AAEL,qBAAa,mBAAoB,SAAQ,wBAEvC;CAAG;;;;;;;;;;;;AAEL,qBAAa,WAAY,SAAQ,gBAK/B;CAAG;;;;;;;;AAEL,cAAM,MAAO,SAAQ,WAEnB;CAAG;;;;;;;;AAEL,cAAM,MAAO,SAAQ,WAEnB;CAAG;;;;;;;;;;;;AAEL,cAAM,QAAS,SAAQ,aAGrB;CAAG;;;;;;;;;;;;;;;;;;;;;;;;AAqBL,qBAAa,cAAe,SAAQ,mBAGlC;CAAG"}