@effect-app/vue 4.0.0-beta.33 → 4.0.0-beta.331
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 +2463 -0
- package/dist/atomQuery.d.ts +120 -0
- package/dist/atomQuery.d.ts.map +1 -0
- package/dist/atomQuery.js +434 -0
- package/dist/commander.d.ts +634 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +1068 -0
- package/dist/{experimental/confirm.d.ts → confirm.d.ts} +6 -4
- package/dist/confirm.d.ts.map +1 -0
- package/dist/confirm.js +26 -0
- package/dist/dependencyMetadata.d.ts +18 -0
- package/dist/dependencyMetadata.d.ts.map +1 -0
- package/dist/dependencyMetadata.js +67 -0
- package/dist/errorReporter.d.ts +6 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +14 -19
- package/dist/form.d.ts +5 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +42 -16
- package/dist/internal/tanstackQuery.d.ts +9 -0
- package/dist/internal/tanstackQuery.d.ts.map +1 -0
- package/dist/internal/tanstackQuery.js +204 -0
- package/dist/intl.d.ts +15 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +9 -0
- package/dist/lib.d.ts +7 -9
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +35 -10
- package/dist/liveQueryInvalidation.d.ts +16 -0
- package/dist/liveQueryInvalidation.d.ts.map +1 -0
- package/dist/liveQueryInvalidation.js +115 -0
- package/dist/makeClient.d.ts +236 -336
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +417 -408
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/{experimental/makeUseCommand.d.ts → makeUseCommand.d.ts} +4 -3
- package/dist/makeUseCommand.d.ts.map +1 -0
- package/dist/makeUseCommand.js +13 -0
- package/dist/mutate.d.ts +109 -40
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +235 -58
- package/dist/query.d.ts +158 -45
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +380 -122
- package/dist/queryLifetime.d.ts +25 -0
- package/dist/queryLifetime.d.ts.map +1 -0
- package/dist/queryLifetime.js +54 -0
- package/dist/routeParams.d.ts +4 -4
- package/dist/routeParams.d.ts.map +1 -1
- package/dist/routeParams.js +4 -3
- package/dist/runtime.d.ts +1 -14
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -26
- package/dist/suspense.d.ts +19 -0
- package/dist/suspense.d.ts.map +1 -0
- package/dist/suspense.js +38 -0
- package/dist/toast.d.ts +2 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +2 -0
- package/dist/withToast.d.ts +2 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +2 -0
- package/docs/atom-query-api-redesign.md +207 -0
- package/docs/mutation-command-atoms.md +40 -0
- package/docs/query-key-invalidation.md +107 -0
- package/examples/streamMutation.ts +72 -0
- package/package.json +36 -95
- package/src/atomQuery.ts +600 -0
- package/src/commander.ts +3408 -0
- package/src/{experimental/confirm.ts → confirm.ts} +13 -15
- package/src/dependencyMetadata.ts +84 -0
- package/src/errorReporter.ts +65 -75
- package/src/form.ts +57 -21
- package/src/index.ts +7 -7
- package/src/internal/tanstackQuery.ts +308 -0
- package/src/intl.ts +12 -0
- package/src/lib.ts +49 -21
- package/src/liveQueryInvalidation.ts +138 -0
- package/src/makeClient.ts +971 -1159
- package/src/makeIntl.ts +2 -2
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +9 -6
- package/src/mutate.ts +406 -153
- package/src/query.ts +763 -260
- package/src/queryLifetime.ts +74 -0
- package/src/routeParams.ts +7 -7
- package/src/runtime.ts +1 -31
- package/src/suspense.ts +43 -0
- package/src/toast.ts +1 -0
- package/src/withToast.ts +1 -0
- package/test/Mutation.test.ts +181 -24
- package/test/dependencyInvalidation.test.ts +444 -0
- package/test/dist/dependencyInvalidation.test.d.ts.map +1 -0
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/inactiveInvalidation.test.d.ts.map +1 -0
- package/test/dist/interrupt-refetch-repro.test.d.ts.map +1 -0
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/liveQueryInvalidation.test.d.ts.map +1 -0
- package/test/dist/query-span.test.d.ts.map +1 -0
- package/test/dist/queryOptions.test.d.ts.map +1 -0
- package/test/dist/streamFinal.test.d.ts.map +1 -0
- package/test/dist/streamFn.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +3751 -120
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +190 -30
- package/test/dist/suspense-regression.test.d.ts.map +1 -0
- package/test/dist/suspense.test.d.ts.map +1 -0
- package/test/form-validation-errors.test.ts +24 -19
- package/test/form.test.ts +25 -6
- package/test/inactiveInvalidation.test.ts +172 -0
- package/test/interrupt-refetch-repro.test.ts +625 -0
- package/test/lib.test.ts +240 -0
- package/test/liveQueryInvalidation.test.ts +108 -0
- package/test/makeClient.test.ts +437 -38
- package/test/query-span.test.ts +92 -0
- package/test/queryOptions.test.ts +88 -0
- package/test/streamFinal.test.ts +64 -0
- package/test/streamFn.test.ts +457 -0
- package/test/stubs.ts +227 -42
- package/test/suspense-regression.test.ts +168 -0
- package/test/suspense.test.ts +160 -0
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +9 -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.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.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/experimental/toast.ts +0 -66
- package/src/experimental/withToast.ts +0 -99
- package/tsconfig.json.bak +0 -12
package/src/query.ts
CHANGED
|
@@ -2,33 +2,214 @@
|
|
|
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
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { type
|
|
11
|
-
import
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
6
|
+
import { injectRegistry, useAtomValue } from "@effect/atom-vue"
|
|
7
|
+
import * as Array from "effect-app/Array"
|
|
8
|
+
import { makeQueryKey, type Req } from "effect-app/client"
|
|
9
|
+
import type { ClientForOptions, RequestHandlerWithInput, RequestStreamHandlerWithInput } from "effect-app/client/clientFor"
|
|
10
|
+
import { type CauseException } from "effect-app/client/errors"
|
|
11
|
+
import type * as Context from "effect-app/Context"
|
|
12
|
+
import * as Effect from "effect-app/Effect"
|
|
13
|
+
import * as Option from "effect-app/Option"
|
|
14
|
+
import type * as Cause from "effect/Cause"
|
|
15
|
+
import * as Exit from "effect/Exit"
|
|
16
|
+
import * as Stream from "effect/Stream"
|
|
12
17
|
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
18
|
+
import * as Atom from "effect/unstable/reactivity/Atom"
|
|
19
|
+
import { computed, type ComputedRef, effectScope, type MaybeRefOrGetter, onBeforeUnmount, onMounted, onScopeDispose, ref, toValue, type WatchSource } from "vue"
|
|
20
|
+
import { type AtomClientRuntime, type AtomQueryOptions, awaitAtomResult, buildQueryFamily, buildStreamQueryFamily, disabledQueryAtom, isStaleResult, queryKeyForAtom, refreshAtomWithCurrentSpan, staleTimeMsOf, withQueryOptions } from "./atomQuery.ts"
|
|
21
|
+
import type { QueryInvalidationMode } from "./dependencyMetadata.ts"
|
|
22
|
+
import type { LiveQueryOptions } from "./liveQueryInvalidation.ts"
|
|
23
|
+
import { latestDefined } from "./suspense.ts"
|
|
15
24
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
// --- minimal local types (replacing the former @tanstack/vue-query type imports) ---
|
|
26
|
+
type DefaultError = Error
|
|
27
|
+
type QueryKey = ReadonlyArray<unknown>
|
|
28
|
+
/** Options accepted by `refetch()` — kept for source compatibility; the atom path ignores them. */
|
|
29
|
+
export interface RefetchOptions {
|
|
30
|
+
readonly cancelRefetch?: boolean
|
|
31
|
+
readonly throwOnError?: boolean
|
|
32
|
+
}
|
|
33
|
+
/** The 4th tuple element: private atom/registry handle for client helpers. */
|
|
34
|
+
export interface QueryHandle<A = unknown, E = unknown> {
|
|
35
|
+
readonly awaitResult: () => Effect.Effect<A, E, never>
|
|
36
|
+
readonly refetch: () => Effect.Effect<A, E, never>
|
|
37
|
+
readonly refresh: () => void
|
|
38
|
+
readonly registry: ReturnType<typeof injectRegistry>
|
|
39
|
+
readonly atom: ComputedRef<Atom.Atom<AsyncResult.AsyncResult<A, E>>>
|
|
40
|
+
}
|
|
41
|
+
export interface QueryView<A, E> extends QueryHandle<A, E> {
|
|
42
|
+
readonly result: ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
43
|
+
readonly data: ComputedRef<A | undefined>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const useScopedSuspenseSetup = <A>(setup: () => A) => {
|
|
47
|
+
const scope = effectScope()
|
|
48
|
+
const controller = new AbortController()
|
|
49
|
+
const value = scope.run(setup)
|
|
50
|
+
if (value === undefined) {
|
|
51
|
+
throw new Error("Internal Error: atom suspense setup scope did not initialize")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isMounted = ref(true)
|
|
55
|
+
let stopped = false
|
|
56
|
+
const stop = () => {
|
|
57
|
+
if (stopped) return
|
|
58
|
+
stopped = true
|
|
59
|
+
isMounted.value = false
|
|
60
|
+
controller.abort()
|
|
61
|
+
scope.stop()
|
|
62
|
+
}
|
|
63
|
+
onBeforeUnmount(stop)
|
|
64
|
+
onScopeDispose(stop)
|
|
65
|
+
|
|
66
|
+
return [value, isMounted, controller.signal] as const
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface QueryCacheUpdater {
|
|
70
|
+
readonly update: <I, A, E, R, Request extends Req, Name extends string>(
|
|
71
|
+
registry: ReturnType<typeof injectRegistry>,
|
|
72
|
+
query: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
73
|
+
input: I,
|
|
74
|
+
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
75
|
+
) => void
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// retained generic aliases so the exported option-interface arity is unchanged for consumers
|
|
79
|
+
export type UseQueryReturnType<A = any, E = any> = QueryHandle<A, E>
|
|
80
|
+
export type UseQueryDefinedReturnType<A = any, E = any> = QueryHandle<A, E>
|
|
81
|
+
export type QueryObserverResult<A = any, _E = any> = AsyncResult.AsyncResult<A, any>
|
|
82
|
+
export type SuspenseQueryTuple<A, E> = readonly [
|
|
83
|
+
ComputedRef<AsyncResult.AsyncResult<A, E>>,
|
|
84
|
+
ComputedRef<A>,
|
|
85
|
+
(options?: RefetchOptions) => Effect.Effect<A, E, never>,
|
|
86
|
+
QueryHandle<A, E>
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
export type SuspenseQueryView<A, E> =
|
|
90
|
+
& Omit<QueryView<A, E>, "data">
|
|
91
|
+
& {
|
|
92
|
+
readonly data: ComputedRef<A>
|
|
93
|
+
}
|
|
94
|
+
& SuspenseQueryTuple<A, E>
|
|
95
|
+
|
|
96
|
+
export type QueryAtomFamily<I, A, E> = (
|
|
97
|
+
input: I
|
|
98
|
+
) => Atom.Writable<AsyncResult.AsyncResult<A, E>, AsyncResult.AsyncResult<A, E>>
|
|
99
|
+
export type StreamQueryAtomFamily<I, A, E> = (input: I) => Atom.Writable<Atom.PullResult<A, E>, void>
|
|
100
|
+
|
|
101
|
+
interface QueryFamilyDescriptor<I, A, E> {
|
|
102
|
+
readonly id: string
|
|
103
|
+
readonly handler: (i: I) => Effect.Effect<A, E, any>
|
|
104
|
+
readonly options?: ClientForOptions
|
|
105
|
+
readonly queryKeyProjectionHash?: string
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface StreamQueryFamilyDescriptor<I, A, E> {
|
|
109
|
+
readonly id: string
|
|
110
|
+
readonly handler: (i: I) => Stream.Stream<A, E, any>
|
|
111
|
+
readonly options?: ClientForOptions
|
|
112
|
+
readonly queryKeyProjectionHash?: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const queryFamilyCacheKey = (
|
|
116
|
+
q: { readonly id: string; readonly options?: ClientForOptions; readonly queryKeyProjectionHash?: string }
|
|
117
|
+
) => `${makeQueryKey(q).join("/")}:${q.queryKeyProjectionHash ?? ""}`
|
|
118
|
+
|
|
119
|
+
// One atom family per request shape, keyed by the stable query key + projection hash (not the
|
|
120
|
+
// handler object — `clientFor` returns a fresh proxy per call, so the object isn't shareable).
|
|
121
|
+
// Module-level + key-indexed => the family is process-global, so the same request+input read
|
|
122
|
+
// the same atom across components/pages => cross-page caching via the global registry.
|
|
123
|
+
const queryFamilyByKey = new Map<string, any>()
|
|
124
|
+
const getQueryFamily = <I, A, E>(
|
|
125
|
+
rt: AtomClientRuntime,
|
|
126
|
+
q: QueryFamilyDescriptor<I, A, E>
|
|
127
|
+
): QueryAtomFamily<I, A, E> => {
|
|
128
|
+
const key = queryFamilyCacheKey(q)
|
|
129
|
+
let f = queryFamilyByKey.get(key)
|
|
130
|
+
if (!f) {
|
|
131
|
+
f = buildQueryFamily(rt, q)
|
|
132
|
+
queryFamilyByKey.set(key, f)
|
|
133
|
+
}
|
|
134
|
+
return f
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const streamQueryFamilyByKey = new Map<string, any>()
|
|
138
|
+
const getStreamQueryFamily = <I, A, E>(
|
|
139
|
+
rt: AtomClientRuntime,
|
|
140
|
+
q: StreamQueryFamilyDescriptor<I, A, E>
|
|
141
|
+
): StreamQueryAtomFamily<I, A, E> => {
|
|
142
|
+
const key = queryFamilyCacheKey(q)
|
|
143
|
+
let f = streamQueryFamilyByKey.get(key)
|
|
144
|
+
if (!f) {
|
|
145
|
+
f = buildStreamQueryFamily(rt, q)
|
|
146
|
+
streamQueryFamilyByKey.set(key, f)
|
|
147
|
+
}
|
|
148
|
+
return f
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const makeAtomQueryCacheUpdater = (warnIfMissing: boolean): QueryCacheUpdater => ({
|
|
152
|
+
update: <I, A, E, R, Request extends Req, Name extends string>(
|
|
153
|
+
registry: ReturnType<typeof injectRegistry>,
|
|
154
|
+
query: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
155
|
+
input: I,
|
|
156
|
+
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
157
|
+
) => {
|
|
158
|
+
const family = queryFamilyByKey.get(queryFamilyCacheKey(query))
|
|
159
|
+
if (!family) {
|
|
160
|
+
if (warnIfMissing) {
|
|
161
|
+
console.warn(`Query ${query.id} has not been used yet; nothing to update`)
|
|
162
|
+
}
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
const atom: Atom.Writable<AsyncResult.AsyncResult<A, E>, AsyncResult.AsyncResult<A, E>> = family(input)
|
|
166
|
+
const current = registry.get(atom)
|
|
167
|
+
const next = AsyncResult.map(current, updater)
|
|
168
|
+
registry.set(atom, next)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
export const atomQueryCacheUpdater = makeAtomQueryCacheUpdater(true)
|
|
173
|
+
export const optionalAtomQueryCacheUpdater = makeAtomQueryCacheUpdater(false)
|
|
174
|
+
|
|
175
|
+
export const combineQueryCacheUpdaters = (first: QueryCacheUpdater, second: QueryCacheUpdater): QueryCacheUpdater => ({
|
|
176
|
+
update: (registry, query, input, updater) => {
|
|
177
|
+
first.update(registry, query, input, updater)
|
|
178
|
+
second.update(registry, query, input, updater)
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
let activeQueryCacheUpdater = atomQueryCacheUpdater
|
|
183
|
+
|
|
184
|
+
export const setQueryCacheUpdater = (updater: QueryCacheUpdater) => {
|
|
185
|
+
activeQueryCacheUpdater = updater
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Atom-engine query options (formerly reconstructed from @tanstack/vue-query types).
|
|
189
|
+
// The generic arity is kept so the exported interface signatures are unchanged for consumers.
|
|
19
190
|
export interface CustomUseQueryOptions<
|
|
20
191
|
TQueryFnData = unknown,
|
|
21
192
|
TError = DefaultError,
|
|
22
193
|
TData = TQueryFnData,
|
|
23
194
|
TQueryData = TQueryFnData,
|
|
24
195
|
TQueryKey extends QueryKey = QueryKey
|
|
25
|
-
>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
196
|
+
> {
|
|
197
|
+
readonly enabled?: MaybeRefOrGetter<boolean | undefined>
|
|
198
|
+
/** stale threshold in ms (or a Duration input) */
|
|
199
|
+
readonly staleTime?: number
|
|
200
|
+
/** garbage-collect after idle, ms (or "infinity") */
|
|
201
|
+
readonly gcTime?: number | "infinity"
|
|
202
|
+
readonly refetchOnWindowFocus?: boolean
|
|
203
|
+
readonly structuralSharing?: boolean
|
|
204
|
+
/** poll: re-fetch every N ms (tanstack refetchInterval) */
|
|
205
|
+
readonly refetchInterval?: number
|
|
206
|
+
readonly live?: boolean | LiveQueryOptions
|
|
207
|
+
readonly invalidation?: QueryInvalidationMode
|
|
208
|
+
readonly select?: (data: TQueryFnData) => TData
|
|
209
|
+
/** accepted for source compatibility; not used by the atom engine */
|
|
210
|
+
readonly retry?: boolean | number
|
|
211
|
+
readonly meta?: Record<string, unknown>
|
|
212
|
+
readonly _phantom?: [TQueryData, TQueryKey, TError]
|
|
32
213
|
}
|
|
33
214
|
|
|
34
215
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
@@ -41,11 +222,8 @@ export interface CustomUndefinedInitialQueryOptions<
|
|
|
41
222
|
TQueryData = TQueryFnData,
|
|
42
223
|
TQueryKey extends QueryKey = QueryKey
|
|
43
224
|
> extends CustomUseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> {
|
|
44
|
-
initialData?:
|
|
45
|
-
placeholderData?:
|
|
46
|
-
| undefined
|
|
47
|
-
| NonFunctionGuard<TQueryData>
|
|
48
|
-
| PlaceholderDataFunction<NonFunctionGuard<TQueryData>, TError, NonFunctionGuard<TQueryData>, TQueryKey>
|
|
225
|
+
readonly initialData?: TQueryFnData | (() => TQueryFnData) | undefined
|
|
226
|
+
readonly placeholderData?: NonFunctionGuard<TQueryData> | ((prev: TQueryData | undefined) => TQueryData) | undefined
|
|
49
227
|
}
|
|
50
228
|
export interface CustomDefinedInitialQueryOptions<
|
|
51
229
|
TQueryFnData = unknown,
|
|
@@ -54,11 +232,8 @@ export interface CustomDefinedInitialQueryOptions<
|
|
|
54
232
|
TQueryData = TQueryFnData,
|
|
55
233
|
TQueryKey extends QueryKey = QueryKey
|
|
56
234
|
> extends CustomUseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> {
|
|
57
|
-
initialData:
|
|
58
|
-
placeholderData?:
|
|
59
|
-
| undefined
|
|
60
|
-
| NonFunctionGuard<TQueryData>
|
|
61
|
-
| PlaceholderDataFunction<NonFunctionGuard<TQueryData>, TError, NonFunctionGuard<TQueryData>, TQueryKey>
|
|
235
|
+
readonly initialData: TQueryFnData | (() => TQueryFnData)
|
|
236
|
+
readonly placeholderData?: NonFunctionGuard<TQueryData> | ((prev: TQueryData | undefined) => TQueryData) | undefined
|
|
62
237
|
}
|
|
63
238
|
|
|
64
239
|
export interface CustomDefinedPlaceholderQueryOptions<
|
|
@@ -68,35 +243,453 @@ export interface CustomDefinedPlaceholderQueryOptions<
|
|
|
68
243
|
TQueryData = TQueryFnData,
|
|
69
244
|
TQueryKey extends QueryKey = QueryKey
|
|
70
245
|
> extends CustomUseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> {
|
|
71
|
-
initialData?:
|
|
72
|
-
placeholderData:
|
|
73
|
-
|
|
74
|
-
|
|
246
|
+
readonly initialData?: TQueryFnData | (() => TQueryFnData) | undefined
|
|
247
|
+
readonly placeholderData: NonFunctionGuard<TQueryData> | ((prev: TQueryData | undefined) => TQueryData)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface AtomQueryNewOptions<TQueryFnData = unknown, TData = TQueryFnData> {
|
|
251
|
+
readonly enabled?: MaybeRefOrGetter<boolean | undefined>
|
|
252
|
+
readonly staleTime?: number
|
|
253
|
+
readonly idleTTL?: number | "infinity"
|
|
254
|
+
readonly gcTime?: number | "infinity"
|
|
255
|
+
readonly revalidateOnFocus?: boolean
|
|
256
|
+
readonly refetchOnWindowFocus?: boolean
|
|
257
|
+
readonly structuralSharing?: boolean
|
|
258
|
+
readonly refreshEvery?: number
|
|
259
|
+
readonly refetchInterval?: number
|
|
260
|
+
readonly live?: boolean | LiveQueryOptions
|
|
261
|
+
readonly invalidation?: QueryInvalidationMode
|
|
262
|
+
readonly select?: (data: TQueryFnData) => TData
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface AtomStreamQueryOptions {
|
|
266
|
+
readonly staleTime?: number
|
|
267
|
+
readonly idleTTL?: number | "infinity"
|
|
268
|
+
readonly gcTime?: number | "infinity"
|
|
269
|
+
readonly revalidateOnFocus?: boolean
|
|
270
|
+
readonly refetchOnWindowFocus?: boolean
|
|
271
|
+
readonly refreshEvery?: number
|
|
272
|
+
readonly live?: boolean | LiveQueryOptions
|
|
273
|
+
readonly refetchInterval?: number
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Layer TanStack `initialData` / `placeholderData` over a raw query result as a DISPLAY fallback.
|
|
278
|
+
* Only while the result is still `Initial` (pending, never settled): show `initialData` as resolved
|
|
279
|
+
* data, or `placeholderData` as provisional (`waiting: true`). Once real data exists the seed is
|
|
280
|
+
* dropped. Failures are never masked — including failures without `previousSuccess` (where
|
|
281
|
+
* `AsyncResult.value` is empty). `placeholderData` in function form receives the last seen concrete
|
|
282
|
+
* value for basic keep-previous across input changes. Returns the raw ref untouched when neither
|
|
283
|
+
* option is set. Neither value is written to the atom cache.
|
|
284
|
+
*/
|
|
285
|
+
export const withDataFallback = <A, E>(
|
|
286
|
+
// Structural read so both Ref and Readonly<Ref> from useAtomValue are accepted.
|
|
287
|
+
rawResult: { readonly value: AsyncResult.AsyncResult<A, E> },
|
|
288
|
+
options?: unknown
|
|
289
|
+
): ComputedRef<AsyncResult.AsyncResult<A, E>> => {
|
|
290
|
+
const opts = options as
|
|
291
|
+
| { readonly initialData?: A | (() => A); readonly placeholderData?: A | ((prev: A | undefined) => A) }
|
|
292
|
+
| undefined
|
|
293
|
+
const initialData = opts?.initialData
|
|
294
|
+
const placeholderData = opts?.placeholderData
|
|
295
|
+
if (initialData === undefined && placeholderData === undefined) {
|
|
296
|
+
// Identity when no fallback options. Callers that need a real ComputedRef
|
|
297
|
+
// (e.g. useAtomValue's Readonly<Ref>) wrap before calling this helper.
|
|
298
|
+
return rawResult as ComputedRef<AsyncResult.AsyncResult<A, E>>
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let lastData: A | undefined
|
|
302
|
+
return computed(() => {
|
|
303
|
+
const r = rawResult.value
|
|
304
|
+
const concrete = AsyncResult.value(r)
|
|
305
|
+
if (Option.isSome(concrete)) {
|
|
306
|
+
lastData = concrete.value
|
|
307
|
+
}
|
|
308
|
+
// Fallback is pending-only. Failure (even without previousSuccess) and Success pass through.
|
|
309
|
+
if (!AsyncResult.isInitial(r)) {
|
|
310
|
+
return r
|
|
311
|
+
}
|
|
312
|
+
if (initialData !== undefined) {
|
|
313
|
+
const v = typeof initialData === "function" ? (initialData as () => A)() : initialData
|
|
314
|
+
return AsyncResult.success(v, { waiting: r.waiting })
|
|
315
|
+
}
|
|
316
|
+
const v = typeof placeholderData === "function"
|
|
317
|
+
? (placeholderData as (prev: A | undefined) => A)(lastData)
|
|
318
|
+
: placeholderData
|
|
319
|
+
return v === undefined ? r : AsyncResult.success(v, { waiting: true })
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const normalizeQueryOptions = (options?: {
|
|
324
|
+
readonly staleTime?: number
|
|
325
|
+
readonly gcTime?: number | "infinity"
|
|
326
|
+
readonly idleTTL?: number | "infinity"
|
|
327
|
+
readonly refetchOnWindowFocus?: boolean
|
|
328
|
+
readonly revalidateOnFocus?: boolean
|
|
329
|
+
readonly structuralSharing?: boolean
|
|
330
|
+
readonly refetchInterval?: number
|
|
331
|
+
readonly refreshEvery?: number
|
|
332
|
+
readonly live?: boolean | LiveQueryOptions
|
|
333
|
+
readonly invalidation?: QueryInvalidationMode
|
|
334
|
+
}): AtomQueryOptions => {
|
|
335
|
+
const out: {
|
|
336
|
+
staleTime?: number
|
|
337
|
+
gcTime?: number | "infinity"
|
|
338
|
+
revalidateOnFocus?: boolean
|
|
339
|
+
structuralSharing?: boolean
|
|
340
|
+
refetchInterval?: number
|
|
341
|
+
live?: boolean | LiveQueryOptions
|
|
342
|
+
invalidation?: QueryInvalidationMode
|
|
343
|
+
} = {}
|
|
344
|
+
if (options?.staleTime !== undefined) out.staleTime = options.staleTime
|
|
345
|
+
const gcTime = options?.idleTTL ?? options?.gcTime
|
|
346
|
+
if (gcTime !== undefined) out.gcTime = gcTime
|
|
347
|
+
const revalidateOnFocus = options?.revalidateOnFocus ?? options?.refetchOnWindowFocus
|
|
348
|
+
if (revalidateOnFocus !== undefined) out.revalidateOnFocus = revalidateOnFocus
|
|
349
|
+
if (options?.structuralSharing !== undefined) out.structuralSharing = options.structuralSharing
|
|
350
|
+
const refetchInterval = options?.refreshEvery ?? options?.refetchInterval
|
|
351
|
+
if (refetchInterval !== undefined) out.refetchInterval = refetchInterval
|
|
352
|
+
if (options?.live !== undefined) out.live = options.live
|
|
353
|
+
if (options?.invalidation !== undefined) out.invalidation = options.invalidation
|
|
354
|
+
return out
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export const useAtomQuery = <A, E>(
|
|
358
|
+
atom: () => Atom.Atom<AsyncResult.AsyncResult<A, E>>
|
|
359
|
+
): QueryView<A, E> => {
|
|
360
|
+
const registry = injectRegistry()
|
|
361
|
+
const atomRef = computed(atom)
|
|
362
|
+
const atomResult = useAtomValue(() => atomRef.value)
|
|
363
|
+
const result = computed(() => atomResult.value)
|
|
364
|
+
const refresh = () => registry.refresh(atomRef.value)
|
|
365
|
+
const awaitResult = () => awaitAtomResult(registry, atomRef.value)
|
|
366
|
+
const refetch = () =>
|
|
367
|
+
Effect.gen(function*() {
|
|
368
|
+
yield* refreshAtomWithCurrentSpan(registry, atomRef.value)
|
|
369
|
+
return yield* awaitResult()
|
|
370
|
+
})
|
|
371
|
+
const data = computed(() => Option.getOrUndefined(AsyncResult.value(result.value)))
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
result,
|
|
375
|
+
data,
|
|
376
|
+
awaitResult,
|
|
377
|
+
refetch,
|
|
378
|
+
refresh,
|
|
379
|
+
registry,
|
|
380
|
+
atom: atomRef
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export const useAtomSuspense = <A, E>(
|
|
385
|
+
atom: () => Atom.Atom<AsyncResult.AsyncResult<A, E>>
|
|
386
|
+
): Promise<SuspenseQueryView<A, E>> => {
|
|
387
|
+
const [{ view, data }, isMounted, signal] = useScopedSuspenseSetup(() => {
|
|
388
|
+
const view = useAtomQuery(atom)
|
|
389
|
+
const data = latestDefined(() => view.data.value, "atom suspense")
|
|
390
|
+
return { view, data }
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
const eff = Effect.gen(function*() {
|
|
394
|
+
const exit = yield* view.awaitResult().pipe(Effect.exit)
|
|
395
|
+
if (!isMounted.value) {
|
|
396
|
+
return yield* Effect.interrupt
|
|
397
|
+
}
|
|
398
|
+
if (Exit.isFailure(exit)) {
|
|
399
|
+
return yield* Exit.failCause(exit.cause)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const fetch = (_options?: RefetchOptions) => view.refetch()
|
|
403
|
+
const handle = {
|
|
404
|
+
awaitResult: view.awaitResult,
|
|
405
|
+
refetch: view.refetch,
|
|
406
|
+
refresh: view.refresh,
|
|
407
|
+
registry: view.registry,
|
|
408
|
+
atom: view.atom
|
|
409
|
+
}
|
|
410
|
+
return Object.assign(
|
|
411
|
+
[
|
|
412
|
+
view.result,
|
|
413
|
+
data,
|
|
414
|
+
fetch,
|
|
415
|
+
handle
|
|
416
|
+
] as const,
|
|
417
|
+
{
|
|
418
|
+
...view,
|
|
419
|
+
data
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
return Effect.runPromise(eff, { signal })
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export type StreamQueryPullValue<A> = {
|
|
428
|
+
readonly done: boolean
|
|
429
|
+
readonly items: ReadonlyArray<A>
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export interface StreamQueryView<A, E> {
|
|
433
|
+
readonly result: ComputedRef<Atom.PullResult<A, E>>
|
|
434
|
+
readonly items: ComputedRef<ReadonlyArray<A>>
|
|
435
|
+
readonly latest: ComputedRef<A | undefined>
|
|
436
|
+
readonly done: ComputedRef<boolean>
|
|
437
|
+
readonly awaitResult: () => Effect.Effect<StreamQueryPullValue<A>, E | Cause.NoSuchElementError, never>
|
|
438
|
+
readonly pull: () => void
|
|
439
|
+
readonly pullAndAwait: () => Effect.Effect<StreamQueryPullValue<A>, E | Cause.NoSuchElementError, never>
|
|
440
|
+
readonly refresh: () => void
|
|
441
|
+
readonly registry: ReturnType<typeof injectRegistry>
|
|
442
|
+
readonly atom: ComputedRef<Atom.Writable<Atom.PullResult<A, E>, void>>
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export const useAtomStreamQuery = <A, E>(
|
|
446
|
+
atom: () => Atom.Writable<Atom.PullResult<A, E>, void>
|
|
447
|
+
): StreamQueryView<A, E> => {
|
|
448
|
+
const registry = injectRegistry()
|
|
449
|
+
const atomRef = computed(atom)
|
|
450
|
+
const atomResult = useAtomValue(() => atomRef.value)
|
|
451
|
+
const result = computed(() => atomResult.value)
|
|
452
|
+
const pull = () => registry.set(atomRef.value, void 0)
|
|
453
|
+
const refresh = () => registry.refresh(atomRef.value)
|
|
454
|
+
const awaitResult = () => awaitAtomResult(registry, atomRef.value)
|
|
455
|
+
const pullAndAwait = () =>
|
|
456
|
+
Effect.gen(function*() {
|
|
457
|
+
pull()
|
|
458
|
+
return yield* awaitResult()
|
|
459
|
+
})
|
|
460
|
+
const items = computed(() =>
|
|
461
|
+
Option.getOrElse(
|
|
462
|
+
Option.map(AsyncResult.value(result.value), (_) => _.items),
|
|
463
|
+
() => []
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
const latest = computed(() => items.value.at(-1))
|
|
467
|
+
const done = computed(() =>
|
|
468
|
+
Option.getOrElse(
|
|
469
|
+
Option.map(AsyncResult.value(result.value), (_) => _.done),
|
|
470
|
+
() => false
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
result,
|
|
476
|
+
items,
|
|
477
|
+
latest,
|
|
478
|
+
done,
|
|
479
|
+
awaitResult,
|
|
480
|
+
pull,
|
|
481
|
+
pullAndAwait,
|
|
482
|
+
refresh,
|
|
483
|
+
registry,
|
|
484
|
+
atom: atomRef
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const optionValue = <I>(
|
|
489
|
+
arr: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
|
|
490
|
+
options?: { readonly mode?: "optional"; readonly enabled?: MaybeRefOrGetter<boolean | undefined> }
|
|
491
|
+
): readonly [{ readonly value: I }, ComputedRef<boolean>] => {
|
|
492
|
+
if (options?.mode === "optional") {
|
|
493
|
+
const getOption: () => Option.Option<I> = typeof arr === "function"
|
|
494
|
+
? arr as () => Option.Option<I>
|
|
495
|
+
: () => (arr as { value: Option.Option<I> }).value
|
|
496
|
+
return [
|
|
497
|
+
{
|
|
498
|
+
get value() {
|
|
499
|
+
return Option.getOrUndefined(getOption()) as I
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
computed(() => Option.isSome(getOption()))
|
|
503
|
+
] as const
|
|
504
|
+
}
|
|
505
|
+
const req = !arr
|
|
506
|
+
? ({ value: undefined as I })
|
|
507
|
+
: typeof arr === "function"
|
|
508
|
+
? ({
|
|
509
|
+
get value() {
|
|
510
|
+
return (arr as any)()
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
: (ref(arr) as any)
|
|
514
|
+
const enabled = options?.enabled
|
|
515
|
+
return [req, computed(() => enabled === undefined ? true : !!toValue(enabled))] as const
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const observedAtom = <A, E>(
|
|
519
|
+
atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>,
|
|
520
|
+
options?: {
|
|
521
|
+
readonly staleTime?: number
|
|
522
|
+
readonly gcTime?: number | "infinity"
|
|
523
|
+
readonly idleTTL?: number | "infinity"
|
|
524
|
+
readonly refetchOnWindowFocus?: boolean
|
|
525
|
+
readonly revalidateOnFocus?: boolean
|
|
526
|
+
readonly structuralSharing?: boolean
|
|
527
|
+
readonly refetchInterval?: number
|
|
528
|
+
readonly refreshEvery?: number
|
|
529
|
+
readonly live?: boolean | LiveQueryOptions
|
|
530
|
+
readonly invalidation?: QueryInvalidationMode
|
|
531
|
+
}
|
|
532
|
+
): Atom.Atom<AsyncResult.AsyncResult<A, E>> =>
|
|
533
|
+
withQueryOptions(atom, normalizeQueryOptions(options), queryKeyForAtom(atom))
|
|
534
|
+
|
|
535
|
+
const observedStreamAtom = <A, E>(
|
|
536
|
+
atom: Atom.Writable<Atom.PullResult<A, E>, void>,
|
|
537
|
+
options?: AtomStreamQueryOptions
|
|
538
|
+
): Atom.Writable<Atom.PullResult<A, E>, void> => {
|
|
539
|
+
let next = atom
|
|
540
|
+
const refetchInterval = options?.refreshEvery ?? options?.refetchInterval
|
|
541
|
+
if (refetchInterval !== undefined) next = Atom.withRefresh(refetchInterval)(next)
|
|
542
|
+
const gcTime = options?.idleTTL ?? options?.gcTime
|
|
543
|
+
if (gcTime === "infinity") return Atom.keepAlive(next)
|
|
544
|
+
if (gcTime !== undefined) return Atom.setIdleTTL(next, gcTime)
|
|
545
|
+
return next
|
|
75
546
|
}
|
|
76
547
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
548
|
+
const queryAtomFor = <I, A, E, TData>(
|
|
549
|
+
rt: AtomClientRuntime,
|
|
550
|
+
q: QueryFamilyDescriptor<I, A, E>,
|
|
551
|
+
arg: I,
|
|
552
|
+
options?: Omit<AtomQueryNewOptions<A, TData>, "select"> | Omit<CustomUseQueryOptions<A, E, TData>, "select">
|
|
553
|
+
): Atom.Atom<AsyncResult.AsyncResult<A, E>> => {
|
|
554
|
+
const family = getQueryFamily(rt, q)
|
|
555
|
+
return observedAtom(family(arg), options)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const makeQueryView = <I, A, E, TData>(
|
|
559
|
+
getAtomRt: () => AtomClientRuntime,
|
|
560
|
+
q: QueryFamilyDescriptor<I, A, E>,
|
|
561
|
+
arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
|
|
562
|
+
options?: (AtomQueryNewOptions<A, TData> | CustomUseQueryOptions<A, E, TData>) & {
|
|
563
|
+
readonly mode?: "optional"
|
|
564
|
+
}
|
|
565
|
+
): QueryView<TData, E> => {
|
|
566
|
+
const atomRt = getAtomRt()
|
|
567
|
+
const registry = injectRegistry()
|
|
568
|
+
const [req, enabledRef] = optionValue<I>(arg, options)
|
|
569
|
+
const family = getQueryFamily(atomRt, q)
|
|
570
|
+
const atomRef = computed(() => enabledRef.value ? observedAtom(family(req.value), options) : disabledQueryAtom)
|
|
571
|
+
// useAtomValue returns Readonly<Ref>, not ComputedRef — re-wrap before withDataFallback / QueryView.
|
|
572
|
+
const rawAtomResult = useAtomValue(() => atomRef.value)
|
|
573
|
+
const rawResult = computed(() => rawAtomResult.value)
|
|
574
|
+
// `initialData` / `placeholderData` display fallback (old `.query()` API). Applied only while the
|
|
575
|
+
// result is still Initial; Failures/Success pass through unmasked. Neither is written to cache
|
|
576
|
+
// (the base atom stays Initial, so the mount-staleness check still triggers the real fetch). The
|
|
577
|
+
// fallback runs PRE-select, matching TanStack (`initialData`/`placeholderData` are TQueryData).
|
|
578
|
+
const fallbackResult = withDataFallback(rawResult, options)
|
|
579
|
+
const select = options?.select
|
|
580
|
+
const result = (select
|
|
581
|
+
? computed(() => AsyncResult.map(fallbackResult.value, select))
|
|
582
|
+
: fallbackResult) as ComputedRef<AsyncResult.AsyncResult<TData, E>>
|
|
583
|
+
const refresh = () => registry.refresh(atomRef.value)
|
|
584
|
+
const awaitResult = () =>
|
|
585
|
+
select
|
|
586
|
+
? awaitAtomResult(registry, atomRef.value).pipe(Effect.map(select))
|
|
587
|
+
: awaitAtomResult(registry, atomRef.value)
|
|
588
|
+
const refetch = () =>
|
|
589
|
+
Effect.gen(function*() {
|
|
590
|
+
yield* refreshAtomWithCurrentSpan(registry, atomRef.value)
|
|
591
|
+
return yield* awaitResult()
|
|
592
|
+
})
|
|
593
|
+
const staleMs = staleTimeMsOf(normalizeQueryOptions(options))
|
|
594
|
+
onMounted(() => {
|
|
595
|
+
if (!enabledRef.value) return
|
|
596
|
+
if (isStaleResult(registry.get(atomRef.value), staleMs)) refresh()
|
|
597
|
+
})
|
|
598
|
+
const data = computed(() => Option.getOrUndefined(AsyncResult.value(result.value)))
|
|
599
|
+
|
|
600
|
+
return {
|
|
601
|
+
result,
|
|
602
|
+
data,
|
|
603
|
+
awaitResult,
|
|
604
|
+
refetch,
|
|
605
|
+
refresh,
|
|
606
|
+
registry,
|
|
607
|
+
atom: atomRef
|
|
82
608
|
}
|
|
83
609
|
}
|
|
84
610
|
|
|
85
|
-
export const
|
|
611
|
+
export const makeQueryFamily = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
612
|
+
const useQueryFamily: {
|
|
613
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
614
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
615
|
+
): QueryAtomFamily<I, A, E>
|
|
616
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
617
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
618
|
+
) => getQueryFamily(getAtomRt(), q)
|
|
619
|
+
|
|
620
|
+
return useQueryFamily
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export const makeQueryAtom = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
624
|
+
const useQueryAtom: {
|
|
625
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
626
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
627
|
+
): {
|
|
628
|
+
<TData = A>(
|
|
629
|
+
arg: I,
|
|
630
|
+
options?: Omit<AtomQueryNewOptions<A, TData>, "select">
|
|
631
|
+
): Atom.Atom<AsyncResult.AsyncResult<A, E>>
|
|
632
|
+
}
|
|
633
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
634
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
635
|
+
) =>
|
|
636
|
+
<TData = A>(
|
|
637
|
+
arg: I,
|
|
638
|
+
options?: Omit<AtomQueryNewOptions<A, TData>, "select">
|
|
639
|
+
) => queryAtomFor(getAtomRt(), q, arg, options)
|
|
640
|
+
|
|
641
|
+
return useQueryAtom
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export const makeQueryNew = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
645
|
+
const useQueryNew: {
|
|
646
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
647
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
648
|
+
): {
|
|
649
|
+
<TData = A>(
|
|
650
|
+
arg: WatchSource<Option.Option<I>>,
|
|
651
|
+
options: Omit<AtomQueryNewOptions<A, TData>, "enabled"> & { mode: "optional" }
|
|
652
|
+
): QueryView<TData, E>
|
|
653
|
+
|
|
654
|
+
<TData = A>(
|
|
655
|
+
arg: I | WatchSource<I> | undefined,
|
|
656
|
+
options?: AtomQueryNewOptions<A, TData>
|
|
657
|
+
): QueryView<TData, E>
|
|
658
|
+
}
|
|
659
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
660
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
661
|
+
) =>
|
|
662
|
+
<TData = A>(
|
|
663
|
+
arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
|
|
664
|
+
options?: AtomQueryNewOptions<A, TData> & { readonly mode?: "optional" }
|
|
665
|
+
) => makeQueryView<I, A, E, TData>(getAtomRt, q, arg, options)
|
|
666
|
+
|
|
667
|
+
return useQueryNew
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export const makeQuery = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
86
671
|
const useQuery_: {
|
|
87
672
|
<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>
|
|
673
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
91
674
|
): {
|
|
675
|
+
<TData = A>(
|
|
676
|
+
arg: WatchSource<Option.Option<I>>,
|
|
677
|
+
options: Omit<CustomUndefinedInitialQueryOptions<A, E, TData>, "enabled"> & { mode: "optional" }
|
|
678
|
+
): readonly [
|
|
679
|
+
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
680
|
+
ComputedRef<TData | undefined>,
|
|
681
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
682
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
683
|
+
]
|
|
684
|
+
|
|
92
685
|
<TData = A>(
|
|
93
686
|
arg: I | WatchSource<I> | undefined,
|
|
94
687
|
options?: CustomUndefinedInitialQueryOptions<A, E, TData>
|
|
95
688
|
): readonly [
|
|
96
689
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
97
690
|
ComputedRef<TData | undefined>,
|
|
98
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
99
|
-
UseQueryDefinedReturnType<TData,
|
|
691
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
692
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
100
693
|
]
|
|
101
694
|
|
|
102
695
|
<TData = A>(
|
|
@@ -105,8 +698,8 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
105
698
|
): readonly [
|
|
106
699
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
107
700
|
ComputedRef<TData>,
|
|
108
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
109
|
-
UseQueryDefinedReturnType<TData,
|
|
701
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
702
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
110
703
|
]
|
|
111
704
|
|
|
112
705
|
<TData = A>(
|
|
@@ -115,253 +708,178 @@ export const makeQuery = <R>(getRuntime: () => ServiceMap.ServiceMap<R>) => {
|
|
|
115
708
|
): readonly [
|
|
116
709
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
117
710
|
ComputedRef<TData>,
|
|
118
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
119
|
-
UseQueryDefinedReturnType<TData,
|
|
711
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
712
|
+
UseQueryDefinedReturnType<TData, CauseException<E>>
|
|
120
713
|
]
|
|
121
714
|
}
|
|
122
715
|
} = <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>
|
|
716
|
+
q: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
126
717
|
) =>
|
|
127
718
|
<TData = A>(
|
|
128
|
-
arg: I | WatchSource<I> | undefined
|
|
129
|
-
// todo QueryKey type would be [string, ...string[]], but with I it would be [string, ...string[], I]
|
|
719
|
+
arg: I | WatchSource<I> | undefined | WatchSource<Option.Option<I>>,
|
|
130
720
|
options?: any
|
|
131
|
-
// TODO
|
|
132
721
|
) => {
|
|
133
|
-
|
|
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
|
-
))
|
|
141
|
-
const arr = arg
|
|
142
|
-
const req: { value: I } = !arg
|
|
143
|
-
? undefined
|
|
144
|
-
: typeof arr === "function"
|
|
145
|
-
? ({
|
|
146
|
-
get value() {
|
|
147
|
-
return (arr as any)()
|
|
148
|
-
}
|
|
149
|
-
} as any)
|
|
150
|
-
: ref(arg)
|
|
151
|
-
const queryKey = makeQueryKey(q)
|
|
152
|
-
const handler = q.handler
|
|
153
|
-
|
|
154
|
-
const defaultOptions = {
|
|
155
|
-
// we do not want to throw errors, because we turn the success and error responses into a Result type
|
|
156
|
-
// 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.
|
|
157
|
-
// 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.
|
|
158
|
-
// TODO: we might want to rethink the strategy of how to handle errors that happen after the initial load.
|
|
159
|
-
// For suspense, the initial load is captured by the suspense boundary.
|
|
160
|
-
// For subsequent loads (or non suspense use) we currently are required to use the QueryResult component to conditionally render error/loading/etc.
|
|
161
|
-
throwOnError: false
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const r = useTanstackQuery<A, KnownFiberFailure<E>, TData>(
|
|
165
|
-
Effect.isEffect(handler)
|
|
166
|
-
? {
|
|
167
|
-
...defaultOptions,
|
|
168
|
-
...options,
|
|
169
|
-
retry: (retryCount, error) => {
|
|
170
|
-
if (error instanceof KnownFiberFailure) {
|
|
171
|
-
if (!isHttpClientError(error.error) && !S.is(ServiceUnavailableError)(error.error)) {
|
|
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
|
-
)
|
|
189
|
-
}
|
|
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
|
-
|
|
200
|
-
return retryCount < 5
|
|
201
|
-
},
|
|
202
|
-
queryKey: [...queryKey, req],
|
|
203
|
-
queryFn: ({ meta, signal }) =>
|
|
204
|
-
runPromise(
|
|
205
|
-
handler(req.value)
|
|
206
|
-
.pipe(
|
|
207
|
-
Effect.tapCauseIf(Cause.hasDies, (cause) => reportRuntimeError(cause)),
|
|
208
|
-
Effect.withSpan(`query ${q.id}`, {}, { captureStackTrace: false }),
|
|
209
|
-
meta?.["span"] ? Effect.withParentSpan(meta["span"] as Span) : (_) => _
|
|
210
|
-
),
|
|
211
|
-
{ signal }
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
)
|
|
722
|
+
const view = makeQueryView<I, A, E, TData>(getAtomRt, q, arg, options)
|
|
215
723
|
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// not using `computed` here as we have a circular dependency
|
|
225
|
-
watch(result, (value) => latestSuccess.value = Option.getOrUndefined(AsyncResult.value(value)), { immediate: true })
|
|
724
|
+
// 4th element is internal-only; the public `.suspense()` Promise boundary lives in makeClient.
|
|
725
|
+
const handle = {
|
|
726
|
+
awaitResult: view.awaitResult,
|
|
727
|
+
refetch: view.refetch,
|
|
728
|
+
refresh: view.refresh,
|
|
729
|
+
registry: view.registry,
|
|
730
|
+
atom: view.atom
|
|
731
|
+
}
|
|
226
732
|
|
|
227
733
|
return [
|
|
228
|
-
result,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// and always ends up in the success channel, even when error..
|
|
233
|
-
(options?: RefetchOptions) =>
|
|
234
|
-
Effect.currentSpan.pipe(
|
|
235
|
-
Effect.orElseSucceed(() => null),
|
|
236
|
-
Effect.flatMap((span) => Effect.promise(() => r.refetch({ ...options, updateMeta: { span } })))
|
|
237
|
-
),
|
|
238
|
-
r
|
|
734
|
+
view.result,
|
|
735
|
+
view.data,
|
|
736
|
+
(_options?: RefetchOptions) => view.refetch(),
|
|
737
|
+
handle
|
|
239
738
|
] as any
|
|
240
739
|
}
|
|
241
740
|
|
|
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
741
|
const useQuery: {
|
|
264
742
|
/**
|
|
265
743
|
* Effect results are passed to the caller, including errors.
|
|
744
|
+
* When `I = void` the input argument may be omitted.
|
|
266
745
|
* @deprecated use client helpers instead (.query())
|
|
267
746
|
*/
|
|
268
|
-
<E, A, Request extends Req, Name extends string>(
|
|
269
|
-
self:
|
|
747
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
748
|
+
self: RequestHandlerWithInput<I, A, E, R, Request, Name>
|
|
270
749
|
): {
|
|
271
|
-
// required options, with initialData
|
|
272
|
-
/**
|
|
273
|
-
* Effect results are passed to the caller, including errors.
|
|
274
|
-
*/
|
|
275
750
|
<TData = A>(
|
|
276
|
-
|
|
751
|
+
arg: WatchSource<Option.Option<I>>,
|
|
752
|
+
options: Omit<CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>, "enabled"> & { mode: "optional" }
|
|
277
753
|
): readonly [
|
|
278
754
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
279
|
-
ComputedRef<TData>,
|
|
280
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
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>>>,
|
|
755
|
+
ComputedRef<TData | undefined>,
|
|
756
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
299
757
|
UseQueryReturnType<any, any>
|
|
300
758
|
]
|
|
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>
|
|
308
|
-
): {
|
|
309
|
-
// required options, with initialData
|
|
310
|
-
/**
|
|
311
|
-
* Effect results are passed to the caller, including errors.
|
|
312
|
-
*/
|
|
313
759
|
<TData = A>(
|
|
314
|
-
arg:
|
|
315
|
-
options: CustomDefinedInitialQueryOptions<A,
|
|
760
|
+
arg: I | WatchSource<I>,
|
|
761
|
+
options: CustomDefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
316
762
|
): readonly [
|
|
317
763
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
318
764
|
ComputedRef<TData>,
|
|
319
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
765
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
320
766
|
UseQueryReturnType<any, any>
|
|
321
767
|
]
|
|
322
|
-
// required options, with placeholderData
|
|
323
|
-
/**
|
|
324
|
-
* Effect results are passed to the caller, including errors.
|
|
325
|
-
*/
|
|
326
768
|
<TData = A>(
|
|
327
|
-
arg:
|
|
328
|
-
options: CustomDefinedPlaceholderQueryOptions<A,
|
|
769
|
+
arg: I | WatchSource<I>,
|
|
770
|
+
options: CustomDefinedPlaceholderQueryOptions<A, CauseException<E>, TData>
|
|
329
771
|
): readonly [
|
|
330
772
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
331
773
|
ComputedRef<TData>,
|
|
332
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
774
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
333
775
|
UseQueryReturnType<any, any>
|
|
334
776
|
]
|
|
335
|
-
// optional options, optional A
|
|
336
|
-
/**
|
|
337
|
-
* Effect results are passed to the caller, including errors.
|
|
338
|
-
*/
|
|
339
777
|
<TData = A>(
|
|
340
|
-
arg:
|
|
341
|
-
options?: CustomUndefinedInitialQueryOptions<A,
|
|
778
|
+
arg: I | WatchSource<I>,
|
|
779
|
+
options?: CustomUndefinedInitialQueryOptions<A, CauseException<E>, TData>
|
|
342
780
|
): readonly [
|
|
343
781
|
ComputedRef<AsyncResult.AsyncResult<TData, E>>,
|
|
344
782
|
ComputedRef<TData | undefined>,
|
|
345
|
-
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData,
|
|
783
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<TData, CauseException<E>>>,
|
|
346
784
|
UseQueryReturnType<any, any>
|
|
347
785
|
]
|
|
348
786
|
}
|
|
349
|
-
} = (
|
|
787
|
+
} = ((
|
|
350
788
|
self: any
|
|
351
789
|
) => {
|
|
352
790
|
const q = useQuery_(self)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
Effect.isEffect(self.handler)
|
|
356
|
-
? q(undefined, argOrOptions)
|
|
357
|
-
: q(argOrOptions, options)
|
|
358
|
-
}
|
|
791
|
+
return (arg?: any, options?: any) => q(arg, options)
|
|
792
|
+
}) as any
|
|
359
793
|
return useQuery
|
|
360
794
|
}
|
|
361
795
|
|
|
362
796
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
363
797
|
export interface MakeQuery2<R> extends ReturnType<typeof makeQuery<R>> {}
|
|
364
798
|
|
|
799
|
+
const streamQueryAtomFor = <I, A, E>(
|
|
800
|
+
rt: AtomClientRuntime,
|
|
801
|
+
q: StreamQueryFamilyDescriptor<I, A, E>,
|
|
802
|
+
arg: I,
|
|
803
|
+
options?: AtomStreamQueryOptions
|
|
804
|
+
): Atom.Writable<Atom.PullResult<A, E>, void> => {
|
|
805
|
+
const family = getStreamQueryFamily(rt, q)
|
|
806
|
+
return observedStreamAtom(family(arg), options)
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export const makeStreamQueryFamily = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
810
|
+
const useStreamQueryFamily: {
|
|
811
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
812
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
813
|
+
): StreamQueryAtomFamily<I, A, E>
|
|
814
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
815
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
816
|
+
) => getStreamQueryFamily(getAtomRt(), q)
|
|
817
|
+
|
|
818
|
+
return useStreamQueryFamily
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
export const makeStreamQueryAtom = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
822
|
+
const useStreamQueryAtom: {
|
|
823
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
824
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
825
|
+
): (arg: I, options?: AtomStreamQueryOptions) => Atom.Writable<Atom.PullResult<A, E>, void>
|
|
826
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
827
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
828
|
+
) =>
|
|
829
|
+
(arg: I, options?: AtomStreamQueryOptions) => streamQueryAtomFor(getAtomRt(), q, arg, options)
|
|
830
|
+
|
|
831
|
+
return useStreamQueryAtom
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export const makeStreamQueryNew = <R>(_getRuntime: () => Context.Context<R>, getAtomRt: () => AtomClientRuntime) => {
|
|
835
|
+
const useStreamQueryNew: {
|
|
836
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
837
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
838
|
+
): (arg: MaybeRefOrGetter<I>, options?: AtomStreamQueryOptions) => StreamQueryView<A, E>
|
|
839
|
+
} = <I, E, A, Request extends Req, Name extends string>(
|
|
840
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
841
|
+
) =>
|
|
842
|
+
(arg: MaybeRefOrGetter<I>, options?: AtomStreamQueryOptions) =>
|
|
843
|
+
useAtomStreamQuery(() => streamQueryAtomFor(getAtomRt(), q, toValue(arg), options))
|
|
844
|
+
|
|
845
|
+
return useStreamQueryNew
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
type StreamQueryResult<A, E> = readonly [
|
|
849
|
+
ComputedRef<AsyncResult.AsyncResult<A[], E>>,
|
|
850
|
+
ComputedRef<A[] | undefined>,
|
|
851
|
+
(options?: RefetchOptions) => Effect.Effect<QueryObserverResult<A[], CauseException<E>>>,
|
|
852
|
+
UseQueryReturnType<any, any>
|
|
853
|
+
]
|
|
854
|
+
|
|
855
|
+
export const makeStreamQuery = <R>(
|
|
856
|
+
getRuntime: () => Context.Context<R>,
|
|
857
|
+
getAtomRt: () => AtomClientRuntime
|
|
858
|
+
) => {
|
|
859
|
+
const query = makeQuery(getRuntime, getAtomRt)
|
|
860
|
+
// A stream query is an ordinary atom query over an effect that collects the whole stream
|
|
861
|
+
// into an array (`Stream.runCollect`). It reuses all the atom machinery (family cache, swr,
|
|
862
|
+
// invalidation, structural sharing). Note: unlike the old tanstack `streamedQuery`, the result
|
|
863
|
+
// appears once the stream completes, not incrementally (stream queries are not used today).
|
|
864
|
+
const streamQuery_: {
|
|
865
|
+
<I, E, A, Request extends Req, Name extends string>(
|
|
866
|
+
q: RequestStreamHandlerWithInput<I, A, E, R, Request, Name>
|
|
867
|
+
): (arg: I | WatchSource<I>) => StreamQueryResult<A, E>
|
|
868
|
+
} = (q: any) => {
|
|
869
|
+
const hook = query({
|
|
870
|
+
id: q.id,
|
|
871
|
+
options: q.options,
|
|
872
|
+
handler: (i: any) => Stream.runCollect(q.handler(i)).pipe(Effect.map((chunk) => [...chunk]))
|
|
873
|
+
} as any)
|
|
874
|
+
return (arg?: any) => hook(arg) as any
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
return streamQuery_
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
881
|
+
export interface MakeStreamQuery2<R> extends ReturnType<typeof makeStreamQuery<R>> {}
|
|
882
|
+
|
|
365
883
|
function orPrevious<E, A>(result: AsyncResult.AsyncResult<A, E>) {
|
|
366
884
|
return AsyncResult.isFailure(result) && Option.isSome(result.previousSuccess)
|
|
367
885
|
? AsyncResult.success(result.previousSuccess.value, { waiting: result.waiting })
|
|
@@ -409,29 +927,14 @@ export function composeQueries<
|
|
|
409
927
|
}
|
|
410
928
|
|
|
411
929
|
export const useUpdateQuery = () => {
|
|
412
|
-
const
|
|
930
|
+
const registry = injectRegistry()
|
|
413
931
|
|
|
414
932
|
const f: {
|
|
415
|
-
<A>(
|
|
416
|
-
query:
|
|
417
|
-
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
418
|
-
): void
|
|
419
|
-
<I, A>(
|
|
420
|
-
query: RequestHandlerWithInput<I, A, any, any, any, any>,
|
|
933
|
+
<I, A, E, R, Request extends Req, Name extends string>(
|
|
934
|
+
query: RequestHandlerWithInput<I, A, E, R, Request, Name>,
|
|
421
935
|
input: I,
|
|
422
936
|
updater: (data: NoInfer<A>) => NoInfer<A>
|
|
423
937
|
): void
|
|
424
|
-
} = (query
|
|
425
|
-
const updater = updaterMaybe !== undefined ? updaterMaybe : updateOrInput
|
|
426
|
-
const key = updaterMaybe !== undefined
|
|
427
|
-
? [...makeQueryKey(query), updateOrInput]
|
|
428
|
-
: makeQueryKey(query)
|
|
429
|
-
const data = queryClient.getQueryData(key)
|
|
430
|
-
if (data) {
|
|
431
|
-
queryClient.setQueryData(key, updater)
|
|
432
|
-
} else {
|
|
433
|
-
console.warn(`Query data for key ${key} not found`, key)
|
|
434
|
-
}
|
|
435
|
-
}
|
|
938
|
+
} = (query, input, updater) => activeQueryCacheUpdater.update(registry, query, input, updater)
|
|
436
939
|
return f
|
|
437
940
|
}
|