@trpc/tanstack-react-query 0.0.0-alpha.0
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/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.mjs +3 -0
- package/dist/internals/Context.d.ts +14 -0
- package/dist/internals/Context.d.ts.map +1 -0
- package/dist/internals/Context.js +52 -0
- package/dist/internals/Context.mjs +31 -0
- package/dist/internals/createOptionsProxy.d.ts +107 -0
- package/dist/internals/createOptionsProxy.d.ts.map +1 -0
- package/dist/internals/createOptionsProxy.js +99 -0
- package/dist/internals/createOptionsProxy.mjs +97 -0
- package/dist/internals/infiniteQueryOptions.d.ts +49 -0
- package/dist/internals/infiniteQueryOptions.d.ts.map +1 -0
- package/dist/internals/infiniteQueryOptions.js +39 -0
- package/dist/internals/infiniteQueryOptions.mjs +37 -0
- package/dist/internals/mutationOptions.d.ts +38 -0
- package/dist/internals/mutationOptions.d.ts.map +1 -0
- package/dist/internals/mutationOptions.js +38 -0
- package/dist/internals/mutationOptions.mjs +36 -0
- package/dist/internals/queryOptions.d.ts +61 -0
- package/dist/internals/queryOptions.d.ts.map +1 -0
- package/dist/internals/queryOptions.js +40 -0
- package/dist/internals/queryOptions.mjs +38 -0
- package/dist/internals/subscriptionOptions.d.ts +77 -0
- package/dist/internals/subscriptionOptions.d.ts.map +1 -0
- package/dist/internals/subscriptionOptions.js +173 -0
- package/dist/internals/subscriptionOptions.mjs +151 -0
- package/dist/internals/types.d.ts +41 -0
- package/dist/internals/types.d.ts.map +1 -0
- package/dist/internals/utils.d.ts +28 -0
- package/dist/internals/utils.d.ts.map +1 -0
- package/dist/internals/utils.js +112 -0
- package/dist/internals/utils.mjs +105 -0
- package/package.json +81 -0
- package/src/index.ts +25 -0
- package/src/internals/Context.tsx +46 -0
- package/src/internals/createOptionsProxy.ts +309 -0
- package/src/internals/infiniteQueryOptions.ts +233 -0
- package/src/internals/mutationOptions.ts +113 -0
- package/src/internals/queryOptions.ts +199 -0
- package/src/internals/subscriptionOptions.ts +286 -0
- package/src/internals/types.ts +47 -0
- package/src/internals/utils.ts +140 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DataTag,
|
|
3
|
+
DefinedInitialDataOptions,
|
|
4
|
+
QueryClient,
|
|
5
|
+
QueryFunction,
|
|
6
|
+
UndefinedInitialDataOptions,
|
|
7
|
+
UnusedSkipTokenOptions,
|
|
8
|
+
} from '@tanstack/react-query';
|
|
9
|
+
import { queryOptions, skipToken, type SkipToken } from '@tanstack/react-query';
|
|
10
|
+
import type { TRPCClientErrorLike, TRPCUntypedClient } from '@trpc/client';
|
|
11
|
+
import type {
|
|
12
|
+
coerceAsyncIterableToArray,
|
|
13
|
+
DistributiveOmit,
|
|
14
|
+
} from '@trpc/server/unstable-core-do-not-import';
|
|
15
|
+
import { isAsyncIterable } from '@trpc/server/unstable-core-do-not-import';
|
|
16
|
+
import type {
|
|
17
|
+
ResolverDef,
|
|
18
|
+
TRPCQueryBaseOptions,
|
|
19
|
+
TRPCQueryKey,
|
|
20
|
+
TRPCQueryOptionsResult,
|
|
21
|
+
} from './types';
|
|
22
|
+
import {
|
|
23
|
+
buildQueryFromAsyncIterable,
|
|
24
|
+
createTRPCOptionsResult,
|
|
25
|
+
getClientArgs,
|
|
26
|
+
unwrapLazyArg,
|
|
27
|
+
} from './utils';
|
|
28
|
+
|
|
29
|
+
type ReservedOptions = 'queryKey' | 'queryFn' | 'queryHashFn' | 'queryHash';
|
|
30
|
+
|
|
31
|
+
interface UndefinedTRPCQueryOptionsIn<TQueryFnData, TData, TError>
|
|
32
|
+
extends DistributiveOmit<
|
|
33
|
+
UndefinedInitialDataOptions<
|
|
34
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
35
|
+
TError,
|
|
36
|
+
coerceAsyncIterableToArray<TData>,
|
|
37
|
+
TRPCQueryKey
|
|
38
|
+
>,
|
|
39
|
+
ReservedOptions
|
|
40
|
+
>,
|
|
41
|
+
TRPCQueryBaseOptions {}
|
|
42
|
+
|
|
43
|
+
interface UndefinedTRPCQueryOptionsOut<TQueryFnData, TOutput, TError>
|
|
44
|
+
extends UndefinedInitialDataOptions<
|
|
45
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
46
|
+
TError,
|
|
47
|
+
coerceAsyncIterableToArray<TOutput>,
|
|
48
|
+
TRPCQueryKey
|
|
49
|
+
>,
|
|
50
|
+
TRPCQueryOptionsResult {
|
|
51
|
+
queryKey: DataTag<TRPCQueryKey, coerceAsyncIterableToArray<TOutput>>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface DefinedTRPCQueryOptionsIn<TQueryFnData, TData, TError>
|
|
55
|
+
extends DistributiveOmit<
|
|
56
|
+
DefinedInitialDataOptions<
|
|
57
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
58
|
+
TError,
|
|
59
|
+
coerceAsyncIterableToArray<TData>,
|
|
60
|
+
TRPCQueryKey
|
|
61
|
+
>,
|
|
62
|
+
ReservedOptions
|
|
63
|
+
>,
|
|
64
|
+
TRPCQueryBaseOptions {}
|
|
65
|
+
|
|
66
|
+
interface DefinedTRPCQueryOptionsOut<TQueryFnData, TData, TError>
|
|
67
|
+
extends DefinedInitialDataOptions<
|
|
68
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
69
|
+
TError,
|
|
70
|
+
coerceAsyncIterableToArray<TData>,
|
|
71
|
+
TRPCQueryKey
|
|
72
|
+
>,
|
|
73
|
+
TRPCQueryOptionsResult {
|
|
74
|
+
queryKey: DataTag<TRPCQueryKey, coerceAsyncIterableToArray<TData>>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface UnusedSkipTokenTRPCQueryOptionsIn<TQueryFnData, TData, TError>
|
|
78
|
+
extends DistributiveOmit<
|
|
79
|
+
UnusedSkipTokenOptions<
|
|
80
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
81
|
+
TError,
|
|
82
|
+
coerceAsyncIterableToArray<TData>,
|
|
83
|
+
TRPCQueryKey
|
|
84
|
+
>,
|
|
85
|
+
ReservedOptions
|
|
86
|
+
>,
|
|
87
|
+
TRPCQueryBaseOptions {}
|
|
88
|
+
|
|
89
|
+
interface UnusedSkipTokenTRPCQueryOptionsOut<TQueryFnData, TOutput, TError>
|
|
90
|
+
extends UnusedSkipTokenOptions<
|
|
91
|
+
coerceAsyncIterableToArray<TQueryFnData>,
|
|
92
|
+
TError,
|
|
93
|
+
coerceAsyncIterableToArray<TOutput>,
|
|
94
|
+
TRPCQueryKey
|
|
95
|
+
>,
|
|
96
|
+
TRPCQueryOptionsResult {
|
|
97
|
+
queryKey: DataTag<TRPCQueryKey, coerceAsyncIterableToArray<TOutput>>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface TRPCQueryOptions<TDef extends ResolverDef> {
|
|
101
|
+
<TQueryFnData extends TDef['output'], TData = TQueryFnData>(
|
|
102
|
+
input: TDef['input'] | SkipToken,
|
|
103
|
+
opts: DefinedTRPCQueryOptionsIn<
|
|
104
|
+
TQueryFnData,
|
|
105
|
+
TData,
|
|
106
|
+
TRPCClientErrorLike<{
|
|
107
|
+
transformer: TDef['transformer'];
|
|
108
|
+
errorShape: TDef['errorShape'];
|
|
109
|
+
}>
|
|
110
|
+
>,
|
|
111
|
+
): DefinedTRPCQueryOptionsOut<
|
|
112
|
+
TQueryFnData,
|
|
113
|
+
TData,
|
|
114
|
+
TRPCClientErrorLike<{
|
|
115
|
+
transformer: TDef['transformer'];
|
|
116
|
+
errorShape: TDef['errorShape'];
|
|
117
|
+
}>
|
|
118
|
+
>;
|
|
119
|
+
<TQueryFnData extends TDef['output'], TData = TQueryFnData>(
|
|
120
|
+
input: TDef['input'],
|
|
121
|
+
opts?: UnusedSkipTokenTRPCQueryOptionsIn<
|
|
122
|
+
TQueryFnData,
|
|
123
|
+
TData,
|
|
124
|
+
TRPCClientErrorLike<{
|
|
125
|
+
transformer: TDef['transformer'];
|
|
126
|
+
errorShape: TDef['errorShape'];
|
|
127
|
+
}>
|
|
128
|
+
>,
|
|
129
|
+
): UnusedSkipTokenTRPCQueryOptionsOut<
|
|
130
|
+
TQueryFnData,
|
|
131
|
+
TData,
|
|
132
|
+
TRPCClientErrorLike<{
|
|
133
|
+
transformer: TDef['transformer'];
|
|
134
|
+
errorShape: TDef['errorShape'];
|
|
135
|
+
}>
|
|
136
|
+
>;
|
|
137
|
+
<TQueryFnData extends TDef['output'], TData = TQueryFnData>(
|
|
138
|
+
input: TDef['input'] | SkipToken,
|
|
139
|
+
opts?: UndefinedTRPCQueryOptionsIn<
|
|
140
|
+
TQueryFnData,
|
|
141
|
+
TData,
|
|
142
|
+
TRPCClientErrorLike<{
|
|
143
|
+
transformer: TDef['transformer'];
|
|
144
|
+
errorShape: TDef['errorShape'];
|
|
145
|
+
}>
|
|
146
|
+
>,
|
|
147
|
+
): UndefinedTRPCQueryOptionsOut<
|
|
148
|
+
TQueryFnData,
|
|
149
|
+
TData,
|
|
150
|
+
TRPCClientErrorLike<{
|
|
151
|
+
transformer: TDef['transformer'];
|
|
152
|
+
errorShape: TDef['errorShape'];
|
|
153
|
+
}>
|
|
154
|
+
>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function trpcQueryOptions(args: {
|
|
158
|
+
query: typeof TRPCUntypedClient.prototype.query;
|
|
159
|
+
queryClient: QueryClient | (() => QueryClient);
|
|
160
|
+
path: readonly string[];
|
|
161
|
+
queryKey: TRPCQueryKey;
|
|
162
|
+
opts: UndefinedTRPCQueryOptionsIn<unknown, unknown, unknown>;
|
|
163
|
+
}) {
|
|
164
|
+
const { query, path, queryKey, opts } = args;
|
|
165
|
+
const queryClient = unwrapLazyArg(args.queryClient);
|
|
166
|
+
|
|
167
|
+
const inputIsSkipToken = queryKey[1]?.input === skipToken;
|
|
168
|
+
|
|
169
|
+
const queryFn: QueryFunction<unknown, TRPCQueryKey> = async (
|
|
170
|
+
queryFnContext,
|
|
171
|
+
) => {
|
|
172
|
+
const actualOpts = {
|
|
173
|
+
...opts,
|
|
174
|
+
trpc: {
|
|
175
|
+
...opts?.trpc,
|
|
176
|
+
...(opts?.trpc?.abortOnUnmount
|
|
177
|
+
? { signal: queryFnContext.signal }
|
|
178
|
+
: { signal: null }),
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = await query(...getClientArgs(queryKey, actualOpts));
|
|
183
|
+
|
|
184
|
+
if (isAsyncIterable(result)) {
|
|
185
|
+
return buildQueryFromAsyncIterable(result, queryClient, queryKey);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
return Object.assign(
|
|
192
|
+
queryOptions({
|
|
193
|
+
...opts,
|
|
194
|
+
queryKey,
|
|
195
|
+
queryFn: inputIsSkipToken ? skipToken : queryFn,
|
|
196
|
+
}),
|
|
197
|
+
{ trpc: createTRPCOptionsResult({ path }) },
|
|
198
|
+
);
|
|
199
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { hashKey, skipToken, type SkipToken } from '@tanstack/react-query';
|
|
2
|
+
import type { TRPCClientErrorLike, TRPCUntypedClient } from '@trpc/client';
|
|
3
|
+
import type { TRPCConnectionState } from '@trpc/client/unstable-internals';
|
|
4
|
+
import type { Unsubscribable } from '@trpc/server/observable';
|
|
5
|
+
import type { inferAsyncIterableYield } from '@trpc/server/unstable-core-do-not-import';
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import type {
|
|
8
|
+
ResolverDef,
|
|
9
|
+
TRPCQueryKey,
|
|
10
|
+
TRPCQueryOptionsResult,
|
|
11
|
+
} from './types';
|
|
12
|
+
import { createTRPCOptionsResult } from './utils';
|
|
13
|
+
|
|
14
|
+
interface BaseTRPCSubscriptionOptionsIn<TOutput, TError> {
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
onStarted?: () => void;
|
|
17
|
+
onData: (data: inferAsyncIterableYield<TOutput>) => void;
|
|
18
|
+
onError?: (err: TError) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface UnusedSkipTokenTRPCSubscriptionOptionsIn<TOutput, TError> {
|
|
22
|
+
onStarted?: () => void;
|
|
23
|
+
onData: (data: inferAsyncIterableYield<TOutput>) => void;
|
|
24
|
+
onError?: (err: TError) => void;
|
|
25
|
+
onConnectionStateChange: (state: TRPCConnectionState<TError>) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TRPCSubscriptionOptionsOut<TOutput, TError>
|
|
29
|
+
extends UnusedSkipTokenTRPCSubscriptionOptionsIn<TOutput, TError>,
|
|
30
|
+
TRPCQueryOptionsResult {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
queryKey: TRPCQueryKey;
|
|
33
|
+
subscribe: (
|
|
34
|
+
innerOpts: UnusedSkipTokenTRPCSubscriptionOptionsIn<TOutput, TError>,
|
|
35
|
+
) => Unsubscribable;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface TRPCSubscriptionOptions<TDef extends ResolverDef> {
|
|
39
|
+
(
|
|
40
|
+
input: TDef['input'],
|
|
41
|
+
opts?: UnusedSkipTokenTRPCSubscriptionOptionsIn<
|
|
42
|
+
inferAsyncIterableYield<TDef['output']>,
|
|
43
|
+
TRPCClientErrorLike<TDef>
|
|
44
|
+
>,
|
|
45
|
+
): TRPCSubscriptionOptionsOut<
|
|
46
|
+
inferAsyncIterableYield<TDef['output']>,
|
|
47
|
+
TRPCClientErrorLike<TDef>
|
|
48
|
+
>;
|
|
49
|
+
(
|
|
50
|
+
input: TDef['input'] | SkipToken,
|
|
51
|
+
opts?: BaseTRPCSubscriptionOptionsIn<
|
|
52
|
+
inferAsyncIterableYield<TDef['output']>,
|
|
53
|
+
TRPCClientErrorLike<TDef>
|
|
54
|
+
>,
|
|
55
|
+
): TRPCSubscriptionOptionsOut<
|
|
56
|
+
inferAsyncIterableYield<TDef['output']>,
|
|
57
|
+
TRPCClientErrorLike<TDef>
|
|
58
|
+
>;
|
|
59
|
+
}
|
|
60
|
+
export type TRPCSubscriptionStatus =
|
|
61
|
+
| 'idle'
|
|
62
|
+
| 'connecting'
|
|
63
|
+
| 'pending'
|
|
64
|
+
| 'error';
|
|
65
|
+
|
|
66
|
+
export interface TRPCSubscriptionBaseResult<TOutput, TError> {
|
|
67
|
+
status: TRPCSubscriptionStatus;
|
|
68
|
+
data: undefined | TOutput;
|
|
69
|
+
error: null | TError;
|
|
70
|
+
/**
|
|
71
|
+
* Reset the subscription
|
|
72
|
+
*/
|
|
73
|
+
reset: () => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TRPCSubscriptionIdleResult<TOutput>
|
|
77
|
+
extends TRPCSubscriptionBaseResult<TOutput, null> {
|
|
78
|
+
status: 'idle';
|
|
79
|
+
data: undefined;
|
|
80
|
+
error: null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TRPCSubscriptionConnectingResult<TOutput, TError>
|
|
84
|
+
extends TRPCSubscriptionBaseResult<TOutput, TError> {
|
|
85
|
+
status: 'connecting';
|
|
86
|
+
data: undefined | TOutput;
|
|
87
|
+
error: TError | null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface TRPCSubscriptionPendingResult<TOutput>
|
|
91
|
+
extends TRPCSubscriptionBaseResult<TOutput, undefined> {
|
|
92
|
+
status: 'pending';
|
|
93
|
+
data: TOutput;
|
|
94
|
+
error: null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface TRPCSubscriptionErrorResult<TOutput, TError>
|
|
98
|
+
extends TRPCSubscriptionBaseResult<TOutput, TError> {
|
|
99
|
+
status: 'error';
|
|
100
|
+
data: TOutput | undefined;
|
|
101
|
+
error: TError;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type TRPCSubscriptionResult<TOutput, TError> =
|
|
105
|
+
| TRPCSubscriptionIdleResult<TOutput>
|
|
106
|
+
| TRPCSubscriptionConnectingResult<TOutput, TError>
|
|
107
|
+
| TRPCSubscriptionErrorResult<TOutput, TError>
|
|
108
|
+
| TRPCSubscriptionPendingResult<TOutput>;
|
|
109
|
+
|
|
110
|
+
export const trpcSubscriptionOptions = (args: {
|
|
111
|
+
subscribe: typeof TRPCUntypedClient.prototype.subscription;
|
|
112
|
+
path: readonly string[];
|
|
113
|
+
queryKey: TRPCQueryKey;
|
|
114
|
+
opts: BaseTRPCSubscriptionOptionsIn<unknown, unknown> | undefined;
|
|
115
|
+
}) => {
|
|
116
|
+
const { subscribe, path, queryKey, opts } = args;
|
|
117
|
+
const input = queryKey[1]?.input;
|
|
118
|
+
const enabled = opts?.enabled ?? input !== skipToken;
|
|
119
|
+
|
|
120
|
+
const _subscribe: ReturnType<TRPCSubscriptionOptions<any>>['subscribe'] = (
|
|
121
|
+
innerOpts,
|
|
122
|
+
) => {
|
|
123
|
+
return subscribe(path.join('.'), input ?? undefined, innerOpts);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
...opts,
|
|
128
|
+
enabled,
|
|
129
|
+
subscribe: _subscribe,
|
|
130
|
+
queryKey,
|
|
131
|
+
trpc: createTRPCOptionsResult({ path }),
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export function useSubscription<TOutput, TError>(
|
|
136
|
+
opts: TRPCSubscriptionOptionsOut<TOutput, TError>,
|
|
137
|
+
): TRPCSubscriptionResult<TOutput, TError> {
|
|
138
|
+
type $Result = TRPCSubscriptionResult<TOutput, TError>;
|
|
139
|
+
|
|
140
|
+
const optsRef = React.useRef(opts);
|
|
141
|
+
optsRef.current = opts;
|
|
142
|
+
|
|
143
|
+
const trackedProps = React.useRef(new Set<keyof $Result>([]));
|
|
144
|
+
|
|
145
|
+
const addTrackedProp = React.useCallback((key: keyof $Result) => {
|
|
146
|
+
trackedProps.current.add(key);
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
type Unsubscribe = () => void;
|
|
150
|
+
const currentSubscriptionRef = React.useRef<Unsubscribe>();
|
|
151
|
+
|
|
152
|
+
const reset = React.useCallback((): void => {
|
|
153
|
+
// unsubscribe from the previous subscription
|
|
154
|
+
currentSubscriptionRef.current?.();
|
|
155
|
+
|
|
156
|
+
updateState(getInitialState);
|
|
157
|
+
if (!opts.enabled) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let isStopped = false;
|
|
162
|
+
const subscription = opts.subscribe({
|
|
163
|
+
onStarted: () => {
|
|
164
|
+
if (!isStopped) {
|
|
165
|
+
optsRef.current.onStarted?.();
|
|
166
|
+
updateState((prev) => ({
|
|
167
|
+
...(prev as any),
|
|
168
|
+
status: 'pending',
|
|
169
|
+
error: null,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
onData: (data) => {
|
|
174
|
+
if (!isStopped) {
|
|
175
|
+
optsRef.current.onData?.(data);
|
|
176
|
+
updateState((prev) => ({
|
|
177
|
+
...(prev as any),
|
|
178
|
+
status: 'pending',
|
|
179
|
+
data,
|
|
180
|
+
error: null,
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
onError: (error) => {
|
|
185
|
+
if (!isStopped) {
|
|
186
|
+
optsRef.current.onError?.(error);
|
|
187
|
+
updateState((prev) => ({
|
|
188
|
+
...(prev as any),
|
|
189
|
+
status: 'error',
|
|
190
|
+
error,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
onConnectionStateChange: (result) => {
|
|
195
|
+
const delta = {
|
|
196
|
+
status: result.state,
|
|
197
|
+
error: result.error,
|
|
198
|
+
} as $Result;
|
|
199
|
+
|
|
200
|
+
updateState((prev) => {
|
|
201
|
+
return {
|
|
202
|
+
...prev,
|
|
203
|
+
...delta,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
currentSubscriptionRef.current = () => {
|
|
210
|
+
isStopped = true;
|
|
211
|
+
subscription.unsubscribe();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
215
|
+
}, [hashKey(opts.queryKey), opts.enabled]);
|
|
216
|
+
|
|
217
|
+
const getInitialState = React.useCallback((): $Result => {
|
|
218
|
+
return opts.enabled
|
|
219
|
+
? {
|
|
220
|
+
data: undefined,
|
|
221
|
+
error: null,
|
|
222
|
+
status: 'connecting',
|
|
223
|
+
reset,
|
|
224
|
+
}
|
|
225
|
+
: {
|
|
226
|
+
data: undefined,
|
|
227
|
+
error: null,
|
|
228
|
+
status: 'idle',
|
|
229
|
+
reset,
|
|
230
|
+
};
|
|
231
|
+
}, [opts.enabled, reset]);
|
|
232
|
+
|
|
233
|
+
const resultRef = React.useRef<$Result>(getInitialState());
|
|
234
|
+
|
|
235
|
+
const [state, setState] = React.useState<$Result>(
|
|
236
|
+
trackResult(resultRef.current, addTrackedProp),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
state.reset = reset;
|
|
240
|
+
|
|
241
|
+
const updateState = React.useCallback(
|
|
242
|
+
(callback: (prevState: $Result) => $Result) => {
|
|
243
|
+
const prev = resultRef.current;
|
|
244
|
+
const next = (resultRef.current = callback(prev));
|
|
245
|
+
|
|
246
|
+
let shouldUpdate = false;
|
|
247
|
+
for (const key of trackedProps.current) {
|
|
248
|
+
if (prev[key] !== next[key]) {
|
|
249
|
+
shouldUpdate = true;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (shouldUpdate) {
|
|
254
|
+
setState(trackResult(next, addTrackedProp));
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
[addTrackedProp],
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
React.useEffect(() => {
|
|
261
|
+
if (!opts.enabled) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
reset();
|
|
265
|
+
|
|
266
|
+
return () => {
|
|
267
|
+
currentSubscriptionRef.current?.();
|
|
268
|
+
};
|
|
269
|
+
}, [reset, opts.enabled]);
|
|
270
|
+
|
|
271
|
+
return state;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function trackResult<T extends object>(
|
|
275
|
+
result: T,
|
|
276
|
+
onTrackResult: (key: keyof T) => void,
|
|
277
|
+
): T {
|
|
278
|
+
const trackedResult = new Proxy(result, {
|
|
279
|
+
get(target, prop) {
|
|
280
|
+
onTrackResult(prop as keyof T);
|
|
281
|
+
return target[prop as keyof T];
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return trackedResult;
|
|
286
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { TRPCRequestOptions } from '@trpc/client';
|
|
2
|
+
|
|
3
|
+
export type ResolverDef = {
|
|
4
|
+
input: any;
|
|
5
|
+
output: any;
|
|
6
|
+
transformer: boolean;
|
|
7
|
+
errorShape: any;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ExtractCursorType<TInput> = TInput extends { cursor?: any }
|
|
11
|
+
? TInput['cursor']
|
|
12
|
+
: unknown;
|
|
13
|
+
|
|
14
|
+
export interface TRPCReactRequestOptions
|
|
15
|
+
// For RQ, we use their internal AbortSignals instead of letting the user pass their own
|
|
16
|
+
extends Omit<TRPCRequestOptions, 'signal'> {
|
|
17
|
+
/**
|
|
18
|
+
* Opt out of SSR for this query by passing `ssr: false`
|
|
19
|
+
*/
|
|
20
|
+
ssr?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Opt out or into aborting request on unmount
|
|
23
|
+
*/
|
|
24
|
+
abortOnUnmount?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TRPCQueryBaseOptions {
|
|
28
|
+
/**
|
|
29
|
+
* tRPC-related options
|
|
30
|
+
*/
|
|
31
|
+
trpc?: TRPCReactRequestOptions;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TRPCQueryOptionsResult {
|
|
35
|
+
trpc: {
|
|
36
|
+
path: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type QueryType = 'any' | 'infinite' | 'query';
|
|
41
|
+
|
|
42
|
+
export type TRPCQueryKey = [
|
|
43
|
+
readonly string[],
|
|
44
|
+
{ input?: unknown; type?: Exclude<QueryType, 'any'> }?,
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export type TRPCMutationKey = [readonly string[]]; // = [TRPCQueryKey[0]]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { skipToken, type QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { isFunction, isObject } from '@trpc/server/unstable-core-do-not-import';
|
|
3
|
+
import type {
|
|
4
|
+
QueryType,
|
|
5
|
+
TRPCMutationKey,
|
|
6
|
+
TRPCQueryKey,
|
|
7
|
+
TRPCQueryOptionsResult,
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export function createTRPCOptionsResult(value: {
|
|
14
|
+
path: readonly string[];
|
|
15
|
+
}): TRPCQueryOptionsResult['trpc'] {
|
|
16
|
+
const path = value.path.join('.');
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
path,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export function getClientArgs<TOptions>(
|
|
27
|
+
queryKey: TRPCQueryKey,
|
|
28
|
+
opts: TOptions,
|
|
29
|
+
infiniteParams?: {
|
|
30
|
+
pageParam: any;
|
|
31
|
+
direction: 'forward' | 'backward';
|
|
32
|
+
},
|
|
33
|
+
) {
|
|
34
|
+
const path = queryKey[0];
|
|
35
|
+
let input = queryKey[1]?.input;
|
|
36
|
+
if (infiniteParams) {
|
|
37
|
+
input = {
|
|
38
|
+
...(input ?? {}),
|
|
39
|
+
...(infiniteParams.pageParam ? { cursor: infiniteParams.pageParam } : {}),
|
|
40
|
+
direction: infiniteParams.direction,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return [path.join('.'), input, (opts as any)?.trpc] as const;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export async function buildQueryFromAsyncIterable(
|
|
50
|
+
asyncIterable: AsyncIterable<unknown>,
|
|
51
|
+
queryClient: QueryClient,
|
|
52
|
+
queryKey: TRPCQueryKey,
|
|
53
|
+
) {
|
|
54
|
+
const queryCache = queryClient.getQueryCache();
|
|
55
|
+
|
|
56
|
+
const query = queryCache.build(queryClient, {
|
|
57
|
+
queryKey,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
query.setState({
|
|
61
|
+
data: [],
|
|
62
|
+
status: 'success',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const aggregate: unknown[] = [];
|
|
66
|
+
for await (const value of asyncIterable) {
|
|
67
|
+
aggregate.push(value);
|
|
68
|
+
|
|
69
|
+
query.setState({
|
|
70
|
+
data: [...aggregate],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return aggregate;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* To allow easy interactions with groups of related queries, such as
|
|
78
|
+
* invalidating all queries of a router, we use an array as the path when
|
|
79
|
+
* storing in tanstack query.
|
|
80
|
+
**/
|
|
81
|
+
export function getQueryKeyInternal(
|
|
82
|
+
path: readonly string[],
|
|
83
|
+
input: unknown,
|
|
84
|
+
type: QueryType,
|
|
85
|
+
): TRPCQueryKey {
|
|
86
|
+
// Construct a query key that is easy to destructure and flexible for
|
|
87
|
+
// partial selecting etc.
|
|
88
|
+
// https://github.com/trpc/trpc/issues/3128
|
|
89
|
+
|
|
90
|
+
// some parts of the path may be dot-separated, split them up
|
|
91
|
+
const splitPath = path.flatMap((part) => part.split('.'));
|
|
92
|
+
|
|
93
|
+
if (!input && (!type || type === 'any')) {
|
|
94
|
+
// this matches also all mutations (see `getMutationKeyInternal`)
|
|
95
|
+
|
|
96
|
+
// for `utils.invalidate()` to match all queries (including vanilla react-query)
|
|
97
|
+
// we don't want nested array if path is empty, i.e. `[]` instead of `[[]]`
|
|
98
|
+
return splitPath.length ? [splitPath] : ([] as unknown as TRPCQueryKey);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
type === 'infinite' &&
|
|
103
|
+
isObject(input) &&
|
|
104
|
+
('direction' in input || 'cursor' in input)
|
|
105
|
+
) {
|
|
106
|
+
const {
|
|
107
|
+
cursor: _,
|
|
108
|
+
direction: __,
|
|
109
|
+
...inputWithoutCursorAndDirection
|
|
110
|
+
} = input;
|
|
111
|
+
return [
|
|
112
|
+
splitPath,
|
|
113
|
+
{
|
|
114
|
+
input: inputWithoutCursorAndDirection,
|
|
115
|
+
type: 'infinite',
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
return [
|
|
120
|
+
splitPath,
|
|
121
|
+
{
|
|
122
|
+
...(typeof input !== 'undefined' &&
|
|
123
|
+
input !== skipToken && { input: input }),
|
|
124
|
+
...(type && type !== 'any' && { type: type }),
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function getMutationKeyInternal(
|
|
130
|
+
path: readonly string[],
|
|
131
|
+
): TRPCMutationKey {
|
|
132
|
+
// some parts of the path may be dot-separated, split them up
|
|
133
|
+
const splitPath = path.flatMap((part) => part.split('.'));
|
|
134
|
+
|
|
135
|
+
return splitPath.length ? [splitPath] : ([] as unknown as TRPCMutationKey);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function unwrapLazyArg<T>(valueOrLazy: T | (() => T)): T {
|
|
139
|
+
return (isFunction(valueOrLazy) ? valueOrLazy() : valueOrLazy) as T;
|
|
140
|
+
}
|