@effect-app/vue 4.0.0-beta.30 → 4.0.0-beta.301

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 (135) hide show
  1. package/CHANGELOG.md +2234 -0
  2. package/dist/atomQuery.d.ts +111 -0
  3. package/dist/atomQuery.d.ts.map +1 -0
  4. package/dist/atomQuery.js +386 -0
  5. package/dist/commander.d.ts +634 -0
  6. package/dist/commander.d.ts.map +1 -0
  7. package/dist/commander.js +1070 -0
  8. package/dist/{experimental/confirm.d.ts → confirm.d.ts} +6 -4
  9. package/dist/confirm.d.ts.map +1 -0
  10. package/dist/confirm.js +26 -0
  11. package/dist/dependencyMetadata.d.ts +10 -0
  12. package/dist/dependencyMetadata.d.ts.map +1 -0
  13. package/dist/dependencyMetadata.js +29 -0
  14. package/dist/errorReporter.d.ts +6 -4
  15. package/dist/errorReporter.d.ts.map +1 -1
  16. package/dist/errorReporter.js +14 -19
  17. package/dist/form.d.ts +14 -5
  18. package/dist/form.d.ts.map +1 -1
  19. package/dist/form.js +46 -13
  20. package/dist/internal/tanstackQuery.d.ts +9 -0
  21. package/dist/internal/tanstackQuery.d.ts.map +1 -0
  22. package/dist/internal/tanstackQuery.js +194 -0
  23. package/dist/intl.d.ts +15 -0
  24. package/dist/intl.d.ts.map +1 -0
  25. package/dist/intl.js +9 -0
  26. package/dist/lib.d.ts +7 -9
  27. package/dist/lib.d.ts.map +1 -1
  28. package/dist/lib.js +35 -10
  29. package/dist/makeClient.d.ts +236 -336
  30. package/dist/makeClient.d.ts.map +1 -1
  31. package/dist/makeClient.js +416 -408
  32. package/dist/makeContext.d.ts.map +1 -1
  33. package/dist/makeIntl.d.ts.map +1 -1
  34. package/dist/{experimental/makeUseCommand.d.ts → makeUseCommand.d.ts} +4 -3
  35. package/dist/makeUseCommand.d.ts.map +1 -0
  36. package/dist/makeUseCommand.js +13 -0
  37. package/dist/mutate.d.ts +108 -40
  38. package/dist/mutate.d.ts.map +1 -1
  39. package/dist/mutate.js +211 -58
  40. package/dist/query.d.ts +150 -46
  41. package/dist/query.d.ts.map +1 -1
  42. package/dist/query.js +370 -122
  43. package/dist/routeParams.d.ts +4 -4
  44. package/dist/routeParams.d.ts.map +1 -1
  45. package/dist/routeParams.js +4 -3
  46. package/dist/runtime.d.ts +1 -14
  47. package/dist/runtime.d.ts.map +1 -1
  48. package/dist/runtime.js +2 -26
  49. package/dist/suspense.d.ts +19 -0
  50. package/dist/suspense.d.ts.map +1 -0
  51. package/dist/suspense.js +38 -0
  52. package/dist/toast.d.ts +2 -0
  53. package/dist/toast.d.ts.map +1 -0
  54. package/dist/toast.js +2 -0
  55. package/dist/withToast.d.ts +2 -0
  56. package/dist/withToast.d.ts.map +1 -0
  57. package/dist/withToast.js +2 -0
  58. package/docs/atom-query-api-redesign.md +207 -0
  59. package/docs/query-key-invalidation.md +106 -0
  60. package/examples/streamMutation.ts +72 -0
  61. package/package.json +36 -95
  62. package/src/atomQuery.ts +540 -0
  63. package/src/commander.ts +3413 -0
  64. package/src/{experimental/confirm.ts → confirm.ts} +13 -15
  65. package/src/dependencyMetadata.ts +38 -0
  66. package/src/errorReporter.ts +65 -75
  67. package/src/form.ts +61 -18
  68. package/src/index.ts +7 -7
  69. package/src/internal/tanstackQuery.ts +289 -0
  70. package/src/intl.ts +12 -0
  71. package/src/lib.ts +49 -21
  72. package/src/makeClient.ts +967 -1159
  73. package/src/makeIntl.ts +2 -2
  74. package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +9 -6
  75. package/src/mutate.ts +370 -152
  76. package/src/query.ts +742 -260
  77. package/src/routeParams.ts +7 -7
  78. package/src/runtime.ts +1 -31
  79. package/src/suspense.ts +43 -0
  80. package/src/toast.ts +1 -0
  81. package/src/withToast.ts +1 -0
  82. package/test/Mutation.test.ts +181 -24
  83. package/test/dependencyInvalidation.test.ts +373 -0
  84. package/test/dist/dependencyInvalidation.test.d.ts.map +1 -0
  85. package/test/dist/form.test.d.ts.map +1 -1
  86. package/test/dist/interrupt-refetch-repro.test.d.ts.map +1 -0
  87. package/test/dist/lib.test.d.ts.map +1 -0
  88. package/test/dist/query-span.test.d.ts.map +1 -0
  89. package/test/dist/queryOptions.test.d.ts.map +1 -0
  90. package/test/dist/streamFinal.test.d.ts.map +1 -0
  91. package/test/dist/streamFn.test.d.ts.map +1 -0
  92. package/test/dist/stubs.d.ts +3756 -122
  93. package/test/dist/stubs.d.ts.map +1 -1
  94. package/test/dist/stubs.js +192 -32
  95. package/test/dist/suspense-regression.test.d.ts.map +1 -0
  96. package/test/dist/suspense.test.d.ts.map +1 -0
  97. package/test/form-validation-errors.test.ts +25 -20
  98. package/test/form.test.ts +22 -3
  99. package/test/interrupt-refetch-repro.test.ts +623 -0
  100. package/test/lib.test.ts +240 -0
  101. package/test/makeClient.test.ts +437 -38
  102. package/test/query-span.test.ts +92 -0
  103. package/test/queryOptions.test.ts +88 -0
  104. package/test/streamFinal.test.ts +64 -0
  105. package/test/streamFn.test.ts +457 -0
  106. package/test/stubs.ts +228 -43
  107. package/test/suspense-regression.test.ts +168 -0
  108. package/test/suspense.test.ts +160 -0
  109. package/tsconfig.examples.json +20 -0
  110. package/tsconfig.json +3 -1
  111. package/tsconfig.src.json +34 -34
  112. package/tsconfig.test.json +2 -2
  113. package/vitest.config.ts +5 -5
  114. package/dist/experimental/commander.d.ts +0 -359
  115. package/dist/experimental/commander.d.ts.map +0 -1
  116. package/dist/experimental/commander.js +0 -557
  117. package/dist/experimental/confirm.d.ts.map +0 -1
  118. package/dist/experimental/confirm.js +0 -28
  119. package/dist/experimental/intl.d.ts +0 -16
  120. package/dist/experimental/intl.d.ts.map +0 -1
  121. package/dist/experimental/intl.js +0 -5
  122. package/dist/experimental/makeUseCommand.d.ts.map +0 -1
  123. package/dist/experimental/makeUseCommand.js +0 -13
  124. package/dist/experimental/toast.d.ts +0 -47
  125. package/dist/experimental/toast.d.ts.map +0 -1
  126. package/dist/experimental/toast.js +0 -41
  127. package/dist/experimental/withToast.d.ts +0 -25
  128. package/dist/experimental/withToast.d.ts.map +0 -1
  129. package/dist/experimental/withToast.js +0 -45
  130. package/eslint.config.mjs +0 -24
  131. package/src/experimental/commander.ts +0 -1835
  132. package/src/experimental/intl.ts +0 -9
  133. package/src/experimental/toast.ts +0 -66
  134. package/src/experimental/withToast.ts +0 -99
  135. package/tsconfig.json.bak +0 -12
@@ -0,0 +1,289 @@
1
+ import { injectRegistry } from "@effect/atom-vue"
2
+ import { QueryClient, useQuery as useTanstackQuery } from "@tanstack/vue-query"
3
+ import { DataDependencies, makeQueryKey, type Req } from "effect-app/client"
4
+ import type { RequestHandlerWithInput } from "effect-app/client/clientFor"
5
+ import { CauseException, ServiceUnavailableError } from "effect-app/client/errors"
6
+ import type * as Context from "effect-app/Context"
7
+ import * as Effect from "effect-app/Effect"
8
+ import * as Option from "effect-app/Option"
9
+ import * as S from "effect-app/Schema"
10
+ import * as Cause from "effect/Cause"
11
+ import * as Ref from "effect/Ref"
12
+ import type * as Tracer from "effect/Tracer"
13
+ import { isHttpClientError } from "effect/unstable/http/HttpClientError"
14
+ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
15
+ import * as Atom from "effect/unstable/reactivity/Atom"
16
+ import { computed, type MaybeRefOrGetter, shallowRef, toValue, watch, type WatchSource } from "vue"
17
+ import { replaceEqualDeep } from "../atomQuery.ts"
18
+ import { clearQueryReadDependencies, setQueryReadDependencies } from "../dependencyMetadata.ts"
19
+ import { reportRuntimeError } from "../lib.ts"
20
+ import type { QueryInvalidator } from "../mutate.ts"
21
+ import type { CustomDefinedInitialQueryOptions, CustomDefinedPlaceholderQueryOptions, CustomUndefinedInitialQueryOptions, CustomUseQueryOptions, MakeQuery2, QueryCacheUpdater, QueryHandle, QueryObserverResult, RefetchOptions } from "../query.ts"
22
+ import { makeRunPromise } from "../runtime.ts"
23
+
24
+ const swrToQuery = <E, A>(r: {
25
+ readonly error: CauseException<E> | null | undefined
26
+ readonly data: A | undefined
27
+ readonly isValidating: boolean
28
+ }): AsyncResult.AsyncResult<A, E> => {
29
+ if (r.error !== undefined && r.error !== null) {
30
+ return AsyncResult.failureWithPrevious(
31
+ r.error.originalCause,
32
+ {
33
+ previous: r.data === undefined ? Option.none() : Option.some(AsyncResult.success(r.data)),
34
+ waiting: r.isValidating
35
+ }
36
+ )
37
+ }
38
+ if (r.data !== undefined) {
39
+ return AsyncResult.success<A, E>(r.data, { waiting: r.isValidating })
40
+ }
41
+
42
+ return AsyncResult.initial(r.isValidating)
43
+ }
44
+
45
+ const recoverCauseException = <A, E>(error: unknown): Effect.Effect<A, E> =>
46
+ error instanceof CauseException
47
+ ? Effect.failCause(error.originalCause)
48
+ : Effect.die(error)
49
+
50
+ const isRetryable = (error: unknown) => {
51
+ if (error instanceof CauseException) {
52
+ return isHttpClientError(error.cause) || S.is(ServiceUnavailableError)(error.cause)
53
+ }
54
+ return false
55
+ }
56
+
57
+ const isInputOption = <I>(value: I | Option.Option<I> | undefined): value is Option.Option<I> => Option.isOption(value)
58
+
59
+ const resolveInput = <I>(
60
+ arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
61
+ mode: "optional" | undefined
62
+ ): I | undefined => {
63
+ if (mode === "optional") {
64
+ const option = toValue(arg)
65
+ return isInputOption(option) && Option.isSome(option) ? option.value : undefined
66
+ }
67
+ const value = toValue(arg)
68
+ return isInputOption(value) ? undefined : value
69
+ }
70
+
71
+ const resolveEnabled = <I>(
72
+ arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
73
+ options: {
74
+ readonly mode?: "optional" | undefined
75
+ readonly enabled?: MaybeRefOrGetter<boolean | undefined> | undefined
76
+ } | undefined
77
+ ) => {
78
+ if (options?.mode === "optional") {
79
+ return computed(() => {
80
+ const option = toValue(arg)
81
+ return Option.isSome(option)
82
+ })
83
+ }
84
+ return computed(() => {
85
+ const enabled = options?.enabled
86
+ if (enabled === undefined) return true
87
+ return !!toValue(enabled)
88
+ })
89
+ }
90
+
91
+ type LegacyTanstackOptions<A, E, TData> =
92
+ & (
93
+ | CustomUndefinedInitialQueryOptions<A, E, TData>
94
+ | CustomDefinedInitialQueryOptions<A, E, TData>
95
+ | CustomDefinedPlaceholderQueryOptions<A, E, TData>
96
+ | CustomUseQueryOptions<A, E, TData>
97
+ )
98
+ & { readonly mode?: "optional" | undefined }
99
+
100
+ export const makeTanstackQueryClient = () => new QueryClient()
101
+
102
+ export const makeTanstackQueryInvalidator = (queryClient: QueryClient): QueryInvalidator => ({
103
+ invalidateAndAwait: (keys) =>
104
+ Effect.gen(function*() {
105
+ const span = yield* Effect.currentParentSpan.pipe(Effect.orElseSucceed(() => undefined))
106
+ yield* Effect.forEach(
107
+ keys,
108
+ (queryKey) => {
109
+ const options = { updateMeta: { span } }
110
+ return Effect.promise(() => queryClient.invalidateQueries({ queryKey }, options))
111
+ },
112
+ { discard: true, concurrency: "inherit" }
113
+ )
114
+ })
115
+ })
116
+
117
+ const fullQueryKey = (
118
+ q: { readonly queryKeyProjectionHash?: string },
119
+ queryKey: ReadonlyArray<unknown>,
120
+ input: unknown
121
+ ) => q.queryKeyProjectionHash === undefined ? [...queryKey, input] : [...queryKey, q.queryKeyProjectionHash, input]
122
+
123
+ export const makeTanstackQueryCacheUpdater = (queryClient: QueryClient): QueryCacheUpdater => ({
124
+ update: <I, A, E, R, Request extends Req, Name extends string>(
125
+ _registry: ReturnType<typeof injectRegistry>,
126
+ query: RequestHandlerWithInput<I, A, E, R, Request, Name>,
127
+ input: I,
128
+ updater: (data: NoInfer<A>) => NoInfer<A>
129
+ ) => {
130
+ const queryKey = fullQueryKey(query, makeQueryKey(query), input)
131
+ if (queryClient.getQueryData(queryKey) === undefined) {
132
+ console.warn(`Query ${query.id} has not been used yet; nothing to update`)
133
+ return
134
+ }
135
+ queryClient.setQueryData(queryKey, (data: A | undefined) => data === undefined ? data : updater(data))
136
+ }
137
+ })
138
+
139
+ export const makeTanstackQuery = <R>(
140
+ getRuntime: () => Context.Context<R>,
141
+ queryClient: QueryClient
142
+ ): MakeQuery2<R> => {
143
+ // Drop a query's recorded read-dependencies when tanstack evicts it from the cache, so the
144
+ // registry mirrors the live queries (mirrors the atom engine's `trackReadDependencies` finalizer).
145
+ queryClient.getQueryCache().subscribe((event) => {
146
+ if (event.type === "removed") clearQueryReadDependencies(event.query.queryKey)
147
+ })
148
+
149
+ const useQuery: MakeQuery2<R> = <I, A, E, Request extends Req, Name extends string>(
150
+ q: RequestHandlerWithInput<I, A, E, R, Request, Name>
151
+ ) =>
152
+ <TData = A>(
153
+ arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
154
+ options?: LegacyTanstackOptions<A, CauseException<E>, TData>
155
+ ) => {
156
+ const runPromise = makeRunPromise(getRuntime())
157
+ const queryKey = makeQueryKey(q)
158
+ const enabled = resolveEnabled(arg, options)
159
+ const structuralSharing = options?.structuralSharing === false ? false : replaceEqualDeep
160
+ const tanstackOptions = {
161
+ ...(options?.staleTime !== undefined ? { staleTime: options.staleTime } : {}),
162
+ ...(typeof options?.gcTime === "number" ? { gcTime: options.gcTime } : {}),
163
+ ...(options?.refetchOnWindowFocus !== undefined ? { refetchOnWindowFocus: options.refetchOnWindowFocus } : {}),
164
+ structuralSharing,
165
+ ...(options?.refetchInterval !== undefined ? { refetchInterval: options.refetchInterval } : {}),
166
+ ...(options?.select !== undefined ? { select: options.select } : {})
167
+ }
168
+ const tanstack = useTanstackQuery<A, CauseException<E>, TData>({
169
+ ...tanstackOptions,
170
+ enabled,
171
+ throwOnError: false,
172
+ retry: (retryCount: number, error: unknown) => isRetryable(error) && retryCount < 5,
173
+ queryKey: computed(() => {
174
+ const input = resolveInput(arg, options?.mode)
175
+ return fullQueryKey(q, queryKey, input)
176
+ }),
177
+ queryFn: (
178
+ { meta, signal }: {
179
+ readonly meta?: { readonly span?: Tracer.AnySpan | undefined } | undefined
180
+ readonly signal: AbortSignal
181
+ }
182
+ ) =>
183
+ runPromise(
184
+ Effect.gen(function*() {
185
+ const input = resolveInput(arg, options?.mode)!
186
+ // Record the repository/server read-dependencies seen while fetching, keyed by the
187
+ // tanstack queryKey, so a later mutation whose writes intersect them derives this query
188
+ // as an invalidation target (the tanstack invalidator invalidates by this same key).
189
+ const readsRef = yield* Ref.make(DataDependencies.empty())
190
+ const writesRef = yield* Ref.make(DataDependencies.empty())
191
+ const recorder = DataDependencies.makeDataDependencyRecorder(readsRef, writesRef)
192
+ const result = yield* q
193
+ .handler(input)
194
+ .pipe(
195
+ Effect.provideService(DataDependencies.DataDependencyRecorder, recorder),
196
+ Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause)),
197
+ Effect.withSpan(`query ${q.id}`, {}, { captureStackTrace: false }),
198
+ meta?.span === undefined
199
+ ? (effect) => effect
200
+ : Effect.withParentSpan(meta.span, { captureStackTrace: false })
201
+ )
202
+ setQueryReadDependencies(fullQueryKey(q, queryKey, input), yield* Ref.get(readsRef))
203
+ return result
204
+ }),
205
+ { signal }
206
+ )
207
+ }, queryClient)
208
+
209
+ const latestSuccess = shallowRef<TData>()
210
+ const result = computed((): AsyncResult.AsyncResult<TData, E> =>
211
+ swrToQuery({
212
+ error: tanstack.error.value,
213
+ data: tanstack.data.value === undefined ? latestSuccess.value : tanstack.data.value,
214
+ isValidating: tanstack.isFetching.value
215
+ })
216
+ )
217
+ watch(result, (value) => latestSuccess.value = Option.getOrUndefined(AsyncResult.value(value)), { immediate: true })
218
+ const seedLatestSuccess = (data: TData) => {
219
+ if (latestSuccess.value === undefined) {
220
+ latestSuccess.value = data
221
+ }
222
+ }
223
+
224
+ const registry = injectRegistry()
225
+ const latestSuccessRef = computed(() => latestSuccess.value)
226
+ const atom = computed<Atom.Atom<AsyncResult.AsyncResult<TData, E>>>(() => Atom.readable(() => result.value))
227
+
228
+ const awaitResult = (): Effect.Effect<TData, E> =>
229
+ Effect
230
+ .tryPromise({
231
+ try: async () => {
232
+ const queryResult = await tanstack.suspense()
233
+ const data = queryResult.data
234
+ if (data === undefined) {
235
+ throw new Error("TanStack query resolved without data")
236
+ }
237
+ seedLatestSuccess(data)
238
+ return data
239
+ },
240
+ catch: (error) => error
241
+ })
242
+ .pipe(
243
+ Effect.catch((error) => recoverCauseException<TData, E>(error))
244
+ )
245
+
246
+ const refetch = (): Effect.Effect<TData, E> =>
247
+ Effect.gen(function*() {
248
+ const span = yield* Effect.currentParentSpan.pipe(Effect.orElseSucceed(() => undefined))
249
+ return yield* Effect
250
+ .tryPromise({
251
+ try: async () => {
252
+ const options = { throwOnError: true, updateMeta: { span } }
253
+ const queryResult = await tanstack.refetch(options)
254
+ const data = queryResult.data
255
+ if (data === undefined) {
256
+ throw new Error("TanStack query refetched without data")
257
+ }
258
+ return data
259
+ },
260
+ catch: (error) => error
261
+ })
262
+ .pipe(
263
+ Effect.catch((error) => recoverCauseException<TData, E>(error))
264
+ )
265
+ })
266
+
267
+ const handle: QueryHandle<TData, E> = {
268
+ awaitResult,
269
+ refetch,
270
+ refresh: () => {
271
+ void tanstack.refetch()
272
+ },
273
+ registry,
274
+ atom
275
+ }
276
+
277
+ const fetch = (_options?: RefetchOptions): Effect.Effect<QueryObserverResult<TData, CauseException<E>>> =>
278
+ refetch().pipe(Effect.exit, Effect.map(AsyncResult.fromExit))
279
+
280
+ return [
281
+ result,
282
+ latestSuccessRef,
283
+ fetch,
284
+ handle
285
+ ] as const
286
+ }
287
+
288
+ return useQuery
289
+ }
package/src/intl.ts ADDED
@@ -0,0 +1,12 @@
1
+ import * as Context from "effect-app/Context"
2
+ import { accessCn, accessFn } from "effect-app/Context"
3
+ import { type MakeIntlReturn } from "./makeIntl.ts"
4
+
5
+ type I18nShape = ReturnType<MakeIntlReturn<string>["useIntl"]>
6
+
7
+ export class I18n extends Context.Opaque<I18n, I18nShape>()("I18n") {
8
+ static readonly locale = accessCn(this, "locale")
9
+ static readonly trans = accessFn(this, "trans")
10
+ static readonly formatMessage = accessFn(this, "formatMessage")
11
+ static readonly intl = accessCn(this, "intl")
12
+ }
package/src/lib.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { type Pausable, useIntervalFn, type UseIntervalFnOptions } from "@vueuse/core"
2
- import { Cause, type Effect, pipe } from "effect-app"
3
- import { type Req } from "effect-app/client"
4
- import type { ClientForOptions, RequestHandler, RequestHandlerWithInput } from "effect-app/client/clientFor"
2
+ import type { Req } from "effect-app/client"
3
+ import type { RequestHandlerWithInput } from "effect-app/client/clientFor"
4
+ import type * as Effect from "effect-app/Effect"
5
+ import * as Cause from "effect/Cause"
5
6
  import { isHttpClientError } from "effect/unstable/http/HttpClientError"
6
- import type { MaybeRefOrGetter } from "vue"
7
- import { reportError } from "./errorReporter.js"
7
+ import { isProxy, isReactive, isRef, type MaybeRefOrGetter, toRaw } from "vue"
8
+ import { reportError } from "./errorReporter.ts"
8
9
 
9
10
  export * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
10
11
 
@@ -31,15 +32,7 @@ const determineLevel = (cause: Cause.Cause<unknown>) => {
31
32
  export const reportRuntimeError = (cause: Cause.Cause<unknown>, extras?: Record<string, unknown>) =>
32
33
  reportRuntimeError_(cause, extras, determineLevel(cause))
33
34
 
34
- // $Project/$Configuration.Index
35
- // -> "$Project", "$Configuration", "Index"
36
- export const makeQueryKey = ({ id, options }: { id: string; options?: ClientForOptions }) =>
37
- pipe(
38
- id.split("/"),
39
- (split) => split.filter((_) => !options || !options?.skipQueryKey?.includes(_)).map((_) => "$" + _)
40
- )
41
- .join(".")
42
- .split(".")
35
+ export { makeQueryKey } from "effect-app/client"
43
36
 
44
37
  export function pauseWhileProcessing(
45
38
  iv: Pausable,
@@ -75,13 +68,48 @@ export const mapHandler: {
75
68
  self: RequestHandlerWithInput<I, A, E, R, Request, Name>,
76
69
  map: (handler: (i: I) => Effect.Effect<A, E, R>) => (i: I) => Effect.Effect<A2, E2, R2>
77
70
  ): RequestHandlerWithInput<I, A2, E2, R2, Request, Name>
78
- <E, A, R, E2, A2, R2, Request extends Req, Name extends string>(
79
- self: RequestHandler<A, E, R, Request, Name>,
80
- map: (handler: Effect.Effect<A, E, R>) => Effect.Effect<A2, E2, R2>
81
- ): RequestHandler<A2, E2, R2, Request, Name>
82
71
  } = (self: any, map: any): any => ({
83
72
  ...self,
84
- handler: typeof self.handler === "function"
85
- ? (i: any) => map(self.handler as (i: any) => Effect.Effect<any, any, any>)(i)
86
- : map(self.handler)
73
+ handler: (i: any) => map(self.handler as (i: any) => Effect.Effect<any, any, any>)(i)
87
74
  })
75
+
76
+ export function deepToRaw<T>(sourceObj: T): T {
77
+ const objectIterator = (input: any): any => {
78
+ if (isRef(input)) {
79
+ return objectIterator(input.value)
80
+ }
81
+
82
+ const rawInput = isReactive(input) || isProxy(input)
83
+ ? toRaw(input)
84
+ : input
85
+
86
+ if (Array.isArray(rawInput)) {
87
+ return rawInput.map((item) => objectIterator(item))
88
+ }
89
+
90
+ if (rawInput instanceof Map) {
91
+ return new Map(
92
+ Array.from(rawInput.entries(), ([key, value]) => [objectIterator(key), objectIterator(value)])
93
+ )
94
+ }
95
+
96
+ if (rawInput instanceof Set) {
97
+ return new Set(Array.from(rawInput.values(), (value) => objectIterator(value)))
98
+ }
99
+
100
+ if (rawInput instanceof Date) {
101
+ return new Date(rawInput)
102
+ }
103
+
104
+ if (rawInput && typeof rawInput === "object") {
105
+ return Object.keys(rawInput).reduce((acc, key) => {
106
+ acc[key] = objectIterator(rawInput[key])
107
+ return acc
108
+ }, {} as Record<string, unknown>)
109
+ }
110
+
111
+ return rawInput
112
+ }
113
+
114
+ return objectIterator(sourceObj)
115
+ }