@orpc/react 0.0.0-unsafe-pr-2-20241118033608

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 (71) hide show
  1. package/dist/index.js +635 -0
  2. package/dist/src/general-hooks.d.ts +24 -0
  3. package/dist/src/general-hooks.d.ts.map +1 -0
  4. package/dist/src/general-utils.d.ts +41 -0
  5. package/dist/src/general-utils.d.ts.map +1 -0
  6. package/dist/src/index.d.ts +9 -0
  7. package/dist/src/index.d.ts.map +1 -0
  8. package/dist/src/orpc-path.d.ts +5 -0
  9. package/dist/src/orpc-path.d.ts.map +1 -0
  10. package/dist/src/procedure-hooks.d.ts +31 -0
  11. package/dist/src/procedure-hooks.d.ts.map +1 -0
  12. package/dist/src/procedure-utils.d.ts +35 -0
  13. package/dist/src/procedure-utils.d.ts.map +1 -0
  14. package/dist/src/react-context.d.ts +13 -0
  15. package/dist/src/react-context.d.ts.map +1 -0
  16. package/dist/src/react-hooks.d.ts +22 -0
  17. package/dist/src/react-hooks.d.ts.map +1 -0
  18. package/dist/src/react-utils.d.ts +22 -0
  19. package/dist/src/react-utils.d.ts.map +1 -0
  20. package/dist/src/react.d.ts +21 -0
  21. package/dist/src/react.d.ts.map +1 -0
  22. package/dist/src/tanstack-key.d.ts +15 -0
  23. package/dist/src/tanstack-key.d.ts.map +1 -0
  24. package/dist/src/tanstack-query.d.ts +19 -0
  25. package/dist/src/tanstack-query.d.ts.map +1 -0
  26. package/dist/src/types.d.ts +5 -0
  27. package/dist/src/types.d.ts.map +1 -0
  28. package/dist/src/use-queries/builder.d.ts +20 -0
  29. package/dist/src/use-queries/builder.d.ts.map +1 -0
  30. package/dist/src/use-queries/builders.d.ts +19 -0
  31. package/dist/src/use-queries/builders.d.ts.map +1 -0
  32. package/dist/src/use-queries/hook.d.ts +16 -0
  33. package/dist/src/use-queries/hook.d.ts.map +1 -0
  34. package/dist/tsconfig.tsbuildinfo +1 -0
  35. package/package.json +56 -0
  36. package/src/general-hooks.test-d.ts +151 -0
  37. package/src/general-hooks.test.tsx +232 -0
  38. package/src/general-hooks.ts +100 -0
  39. package/src/general-utils.test-d.ts +454 -0
  40. package/src/general-utils.test.tsx +818 -0
  41. package/src/general-utils.ts +397 -0
  42. package/src/index.ts +8 -0
  43. package/src/orpc-path.test-d.ts +13 -0
  44. package/src/orpc-path.test.ts +12 -0
  45. package/src/orpc-path.ts +24 -0
  46. package/src/procedure-hooks.test-d.ts +321 -0
  47. package/src/procedure-hooks.test.tsx +388 -0
  48. package/src/procedure-hooks.ts +271 -0
  49. package/src/procedure-utils.test-d.ts +476 -0
  50. package/src/procedure-utils.test.tsx +330 -0
  51. package/src/procedure-utils.ts +315 -0
  52. package/src/react-context.ts +43 -0
  53. package/src/react-hooks.ts +102 -0
  54. package/src/react-utils.ts +110 -0
  55. package/src/react.test-d.ts +89 -0
  56. package/src/react.test.tsx +102 -0
  57. package/src/react.tsx +80 -0
  58. package/src/tanstack-key.test-d.ts +35 -0
  59. package/src/tanstack-key.test.ts +62 -0
  60. package/src/tanstack-key.ts +64 -0
  61. package/src/tanstack-query.ts +27 -0
  62. package/src/types.ts +7 -0
  63. package/src/use-queries/builder.test-d.ts +29 -0
  64. package/src/use-queries/builder.test.ts +25 -0
  65. package/src/use-queries/builder.ts +72 -0
  66. package/src/use-queries/builders.test-d.ts +30 -0
  67. package/src/use-queries/builders.test.tsx +29 -0
  68. package/src/use-queries/builders.ts +101 -0
  69. package/src/use-queries/hook.test-d.ts +64 -0
  70. package/src/use-queries/hook.test.tsx +89 -0
  71. package/src/use-queries/hook.ts +57 -0
@@ -0,0 +1,102 @@
1
+ import type {
2
+ ContractProcedure,
3
+ ContractRouter,
4
+ SchemaOutput,
5
+ } from '@orpc/contract'
6
+ import type { Procedure, Router } from '@orpc/server'
7
+ import { type GeneralHooks, createGeneralHooks } from './general-hooks'
8
+ import { orpcPathSymbol } from './orpc-path'
9
+ import { type ProcedureHooks, createProcedureHooks } from './procedure-hooks'
10
+ import type { ORPCContext } from './react-context'
11
+
12
+ export type ORPCHooksWithContractRouter<TRouter extends ContractRouter> = {
13
+ [K in keyof TRouter]: TRouter[K] extends ContractProcedure<
14
+ infer UInputSchema,
15
+ infer UOutputSchema
16
+ >
17
+ ? ProcedureHooks<UInputSchema, UOutputSchema, SchemaOutput<UOutputSchema>> &
18
+ GeneralHooks<UInputSchema, UOutputSchema, SchemaOutput<UOutputSchema>>
19
+ : TRouter[K] extends ContractRouter
20
+ ? ORPCHooksWithContractRouter<TRouter[K]>
21
+ : never
22
+ } & GeneralHooks<undefined, undefined, unknown>
23
+
24
+ export type ORPCHooksWithRouter<TRouter extends Router<any>> = {
25
+ [K in keyof TRouter]: TRouter[K] extends Procedure<
26
+ any,
27
+ any,
28
+ infer UInputSchema,
29
+ infer UOutputSchema,
30
+ infer UHandlerOutput
31
+ >
32
+ ? ProcedureHooks<UInputSchema, UOutputSchema, UHandlerOutput> &
33
+ GeneralHooks<UInputSchema, UOutputSchema, UHandlerOutput>
34
+ : TRouter[K] extends Router<any>
35
+ ? ORPCHooksWithRouter<TRouter[K]>
36
+ : never
37
+ } & GeneralHooks<undefined, undefined, unknown>
38
+
39
+ export interface CreateORPCHooksOptions<
40
+ TRouter extends ContractRouter | Router<any>,
41
+ > {
42
+ context: ORPCContext<TRouter>
43
+
44
+ /**
45
+ * The path of the router.
46
+ *
47
+ * @internal
48
+ */
49
+ path?: string[]
50
+ }
51
+
52
+ export function createORPCHooks<TRouter extends ContractRouter | Router<any>>(
53
+ options: CreateORPCHooksOptions<TRouter>,
54
+ ): TRouter extends Router<any>
55
+ ? ORPCHooksWithRouter<TRouter>
56
+ : TRouter extends ContractRouter
57
+ ? ORPCHooksWithContractRouter<TRouter>
58
+ : never {
59
+ const path = options.path ?? []
60
+ const generalHooks = createGeneralHooks({ context: options.context, path })
61
+
62
+ // for sure root is not procedure, so do not it procedure hooks on root
63
+ const procedureHooks = path.length
64
+ ? createProcedureHooks({
65
+ context: options.context,
66
+ path,
67
+ })
68
+ : {}
69
+
70
+ return new Proxy(
71
+ {
72
+ [orpcPathSymbol]: path,
73
+
74
+ ...generalHooks,
75
+ ...procedureHooks,
76
+ },
77
+ {
78
+ get(target, key) {
79
+ const value = Reflect.get(target, key)
80
+
81
+ if (typeof key !== 'string') {
82
+ return value
83
+ }
84
+
85
+ const nextHooks = createORPCHooks({
86
+ context: options.context,
87
+ path: [...path, key],
88
+ })
89
+
90
+ if (typeof value !== 'function') {
91
+ return nextHooks
92
+ }
93
+
94
+ return new Proxy(value, {
95
+ get(_, key) {
96
+ return Reflect.get(nextHooks, key)
97
+ },
98
+ })
99
+ },
100
+ },
101
+ ) as any
102
+ }
@@ -0,0 +1,110 @@
1
+ import type {} from '@orpc/client'
2
+ import type {
3
+ ContractProcedure,
4
+ ContractRouter,
5
+ SchemaOutput,
6
+ } from '@orpc/contract'
7
+ import type { Procedure, Router } from '@orpc/server'
8
+ import { type GeneralUtils, createGeneralUtils } from './general-utils'
9
+ import { type ProcedureUtils, createProcedureUtils } from './procedure-utils'
10
+ import type { ORPCContextValue } from './react-context'
11
+
12
+ export type ORPCUtilsWithContractRouter<TRouter extends ContractRouter> = {
13
+ [K in keyof TRouter]: TRouter[K] extends ContractProcedure<
14
+ infer UInputSchema,
15
+ infer UOutputSchema
16
+ >
17
+ ? ProcedureUtils<UInputSchema, UOutputSchema, SchemaOutput<UOutputSchema>> &
18
+ GeneralUtils<UInputSchema, UOutputSchema, SchemaOutput<UOutputSchema>>
19
+ : TRouter[K] extends ContractRouter
20
+ ? ORPCUtilsWithContractRouter<TRouter[K]>
21
+ : never
22
+ } & GeneralUtils<undefined, undefined, unknown>
23
+
24
+ export type ORPCUtilsWithRouter<TRouter extends Router<any>> = {
25
+ [K in keyof TRouter]: TRouter[K] extends Procedure<
26
+ any,
27
+ any,
28
+ infer UInputSchema,
29
+ infer UOutputSchema,
30
+ infer UHandlerOutput
31
+ >
32
+ ? ProcedureUtils<UInputSchema, UOutputSchema, UHandlerOutput> &
33
+ GeneralUtils<UInputSchema, UOutputSchema, UHandlerOutput>
34
+ : TRouter[K] extends Router<any>
35
+ ? ORPCUtilsWithRouter<TRouter[K]>
36
+ : never
37
+ } & GeneralUtils<undefined, undefined, unknown>
38
+
39
+ export interface CreateORPCUtilsOptions<
40
+ TRouter extends ContractRouter | Router<any>,
41
+ > {
42
+ contextValue: ORPCContextValue<TRouter>
43
+
44
+ /**
45
+ * The path of the router.
46
+ *
47
+ * @internal
48
+ */
49
+ path?: string[]
50
+ }
51
+
52
+ export function createORPCUtils<TRouter extends ContractRouter | Router<any>>(
53
+ options: CreateORPCUtilsOptions<TRouter>,
54
+ ): TRouter extends Router<any>
55
+ ? ORPCUtilsWithRouter<TRouter>
56
+ : TRouter extends ContractRouter
57
+ ? ORPCUtilsWithContractRouter<TRouter>
58
+ : never {
59
+ const path = options.path ?? []
60
+ const client = options.contextValue.client as any
61
+
62
+ const generalUtils = createGeneralUtils({
63
+ queryClient: options.contextValue.queryClient,
64
+ path,
65
+ })
66
+
67
+ // for sure root is not procedure, so do not it procedure utils on root
68
+ const procedureUtils = path.length
69
+ ? createProcedureUtils({
70
+ client,
71
+ queryClient: options.contextValue.queryClient,
72
+ path,
73
+ })
74
+ : {}
75
+
76
+ return new Proxy(
77
+ {
78
+ ...generalUtils,
79
+ ...procedureUtils,
80
+ },
81
+ {
82
+ get(target, key) {
83
+ const value = Reflect.get(target, key)
84
+
85
+ if (typeof key !== 'string') {
86
+ return value
87
+ }
88
+
89
+ const nextUtils = createORPCUtils({
90
+ ...options,
91
+ contextValue: {
92
+ ...options.contextValue,
93
+ client: client[key],
94
+ },
95
+ path: [...path, key],
96
+ })
97
+
98
+ if (typeof value !== 'function') {
99
+ return nextUtils
100
+ }
101
+
102
+ return new Proxy(value, {
103
+ get(_, key) {
104
+ return Reflect.get(nextUtils, key)
105
+ },
106
+ })
107
+ },
108
+ },
109
+ ) as any
110
+ }
@@ -0,0 +1,89 @@
1
+ import type { SchemaOutput } from '@orpc/contract'
2
+ import type { QueryClient } from '@tanstack/react-query'
3
+ import {
4
+ ORPCContext,
5
+ type UserFindInputSchema,
6
+ type UserSchema,
7
+ orpc,
8
+ orpcClient,
9
+ } from '../tests/orpc'
10
+ import type { GeneralHooks } from './general-hooks'
11
+ import type { GeneralUtils } from './general-utils'
12
+ import type { ProcedureHooks } from './procedure-hooks'
13
+ import type { ProcedureUtils } from './procedure-utils'
14
+ import { useQueriesFactory } from './use-queries/hook'
15
+
16
+ describe('useUtils', () => {
17
+ const utils = orpc.useUtils()
18
+
19
+ it('router level', () => {
20
+ expectTypeOf(utils).toMatchTypeOf<
21
+ GeneralUtils<undefined, undefined, unknown>
22
+ >()
23
+
24
+ expectTypeOf(utils.user).toMatchTypeOf<
25
+ GeneralUtils<undefined, undefined, unknown>
26
+ >()
27
+ })
28
+
29
+ it('procedure level', () => {
30
+ expectTypeOf(utils.user.find).toMatchTypeOf<
31
+ GeneralUtils<
32
+ typeof UserFindInputSchema,
33
+ typeof UserSchema,
34
+ SchemaOutput<typeof UserSchema>
35
+ >
36
+ >()
37
+
38
+ expectTypeOf(utils.user.find).toMatchTypeOf<
39
+ ProcedureUtils<
40
+ typeof UserFindInputSchema,
41
+ typeof UserSchema,
42
+ SchemaOutput<typeof UserSchema>
43
+ >
44
+ >()
45
+ })
46
+ })
47
+
48
+ it('useContext', () => {
49
+ const context = orpc.useContext()
50
+
51
+ expectTypeOf(context.client).toEqualTypeOf(orpcClient)
52
+ expectTypeOf(context.queryClient).toEqualTypeOf<QueryClient>()
53
+ })
54
+
55
+ it('useQueries', () => {
56
+ expectTypeOf(orpc.useQueries).toEqualTypeOf(
57
+ useQueriesFactory({ context: ORPCContext }),
58
+ )
59
+ })
60
+
61
+ describe('hooks', () => {
62
+ it('router level', () => {
63
+ expectTypeOf(orpc).toMatchTypeOf<
64
+ GeneralHooks<undefined, undefined, unknown>
65
+ >()
66
+
67
+ expectTypeOf(orpc.user).toMatchTypeOf<
68
+ GeneralHooks<undefined, undefined, unknown>
69
+ >()
70
+ })
71
+
72
+ it('procedure level', () => {
73
+ expectTypeOf(orpc.user.find).toMatchTypeOf<
74
+ GeneralHooks<
75
+ typeof UserFindInputSchema,
76
+ typeof UserSchema,
77
+ SchemaOutput<typeof UserSchema>
78
+ >
79
+ >()
80
+
81
+ expectTypeOf(orpc.user.find).toMatchTypeOf<
82
+ ProcedureHooks<
83
+ typeof UserFindInputSchema,
84
+ typeof UserSchema,
85
+ SchemaOutput<typeof UserSchema>
86
+ >
87
+ >()
88
+ })
89
+ })
@@ -0,0 +1,102 @@
1
+ import { renderHook, waitFor } from '@testing-library/react'
2
+ import { orpc, orpcClient, queryClient, wrapper } from '../tests/orpc'
3
+
4
+ beforeEach(() => {
5
+ queryClient.clear()
6
+ })
7
+
8
+ it('useUtils', async () => {
9
+ const { result } = renderHook(() => orpc.useUtils(), { wrapper: wrapper })
10
+
11
+ const promise = result.current.user.find.ensureQueryData({ id: '1' })
12
+ expect(result.current.user.isFetching()).toBe(1)
13
+ const data = await promise
14
+ expect(result.current.user.isFetching()).toBe(0)
15
+
16
+ let old: any
17
+ result.current.user.setQueriesData({}, (old_: any) => {
18
+ old = old_
19
+ })
20
+
21
+ expect(data).toEqual({
22
+ id: '1',
23
+ name: 'name-1',
24
+ })
25
+ expect(old).toEqual(data)
26
+ })
27
+
28
+ it('useContext', async () => {
29
+ const { result } = renderHook(() => orpc.useContext(), { wrapper: wrapper })
30
+
31
+ expect(result.current.client).toBe(orpcClient)
32
+ expect(result.current.queryClient).toBe(queryClient)
33
+
34
+ expect(() => renderHook(() => orpc.useContext())).throws(
35
+ 'useORPCContext must be used within a <ORPCContext.Provider>, please see the docs',
36
+ )
37
+ })
38
+
39
+ it('useQueries', async () => {
40
+ const queries = renderHook(
41
+ () => orpc.useQueries((o) => [o.user.find({ id: '123' })]),
42
+ { wrapper: wrapper },
43
+ )
44
+
45
+ await waitFor(() =>
46
+ expect(queries.result.current[0].data).toEqual({
47
+ id: '123',
48
+ name: 'name-123',
49
+ }),
50
+ )
51
+ })
52
+
53
+ it('hooks', async () => {
54
+ const isFetching = renderHook(() => orpc.user.useIsFetching(), {
55
+ wrapper: wrapper,
56
+ })
57
+ const isMutating = renderHook(() => orpc.user.useIsMutating(), {
58
+ wrapper: wrapper,
59
+ })
60
+
61
+ expect(isFetching.result.current).toBe(0)
62
+ expect(isMutating.result.current).toBe(0)
63
+
64
+ const query = renderHook(() => orpc.user.find.useQuery({ id: '1' }), {
65
+ wrapper: wrapper,
66
+ })
67
+ const mutation = renderHook(() => orpc.user.create.useMutation(), {
68
+ wrapper: wrapper,
69
+ })
70
+
71
+ await waitFor(() => expect(query.result.current.status).toEqual('pending'))
72
+
73
+ expect(isFetching.result.current).toBe(1)
74
+ expect(isMutating.result.current).toBe(0)
75
+
76
+ await waitFor(() =>
77
+ expect(query.result.current.data).toEqual({
78
+ id: '1',
79
+ name: 'name-1',
80
+ }),
81
+ )
82
+
83
+ expect(isFetching.result.current).toBe(0)
84
+ expect(isMutating.result.current).toBe(0)
85
+
86
+ mutation.result.current.mutate({ name: 'name-2' })
87
+
88
+ await waitFor(() => expect(mutation.result.current.status).toEqual('pending'))
89
+
90
+ expect(isFetching.result.current).toBe(0)
91
+ expect(isMutating.result.current).toBe(1)
92
+
93
+ await waitFor(() => expect(mutation.result.current.status).toEqual('success'))
94
+
95
+ expect(isFetching.result.current).toBe(0)
96
+ expect(isMutating.result.current).toBe(0)
97
+
98
+ expect(mutation.result.current.data).toEqual({
99
+ id: expect.any(String),
100
+ name: 'name-2',
101
+ })
102
+ })
package/src/react.tsx ADDED
@@ -0,0 +1,80 @@
1
+ import type { ContractRouter } from '@orpc/contract'
2
+ import type { Router } from '@orpc/server'
3
+ import {
4
+ type ORPCContext,
5
+ type ORPCContextValue,
6
+ createORPCContext,
7
+ useORPCContext,
8
+ } from './react-context'
9
+ import {
10
+ type ORPCHooksWithContractRouter,
11
+ type ORPCHooksWithRouter,
12
+ createORPCHooks,
13
+ } from './react-hooks'
14
+ import {
15
+ type ORPCUtilsWithContractRouter,
16
+ type ORPCUtilsWithRouter,
17
+ createORPCUtils,
18
+ } from './react-utils'
19
+ import {
20
+ type UseQueriesWithContractRouter,
21
+ type UseQueriesWithRouter,
22
+ useQueriesFactory,
23
+ } from './use-queries/hook'
24
+
25
+ export type ORPCReactWithContractRouter<TRouter extends ContractRouter> =
26
+ ORPCHooksWithContractRouter<TRouter> & {
27
+ useContext: () => ORPCContextValue<TRouter>
28
+ useUtils: () => ORPCUtilsWithContractRouter<TRouter>
29
+ useQueries: () => UseQueriesWithContractRouter<TRouter>
30
+ }
31
+
32
+ export type ORPCReactWithRouter<TRouter extends Router<any>> =
33
+ ORPCHooksWithRouter<TRouter> & {
34
+ useContext: () => ORPCContextValue<TRouter>
35
+ useUtils: () => ORPCUtilsWithRouter<TRouter>
36
+ useQueries: UseQueriesWithRouter<TRouter>
37
+ }
38
+
39
+ export function createORPCReact<
40
+ TRouter extends ContractRouter | Router<any>,
41
+ >(): {
42
+ orpc: TRouter extends Router<any>
43
+ ? ORPCReactWithRouter<TRouter>
44
+ : TRouter extends ContractRouter
45
+ ? ORPCReactWithContractRouter<TRouter>
46
+ : never
47
+ ORPCContext: ORPCContext<TRouter>
48
+ } {
49
+ const Context = createORPCContext<TRouter>()
50
+ const useContext = () => useORPCContext(Context)
51
+ const useUtils = () => createORPCUtils({ contextValue: useContext() })
52
+ const useQueries = useQueriesFactory({ context: Context })
53
+ const hooks = createORPCHooks({ context: Context })
54
+
55
+ const orpc = new Proxy(
56
+ {
57
+ useContext,
58
+ useUtils,
59
+ useQueries,
60
+ },
61
+ {
62
+ get(target, key) {
63
+ const value = Reflect.get(target, key)
64
+ const nextHooks = Reflect.get(hooks, key)
65
+
66
+ if (typeof value !== 'function') {
67
+ return nextHooks
68
+ }
69
+
70
+ return new Proxy(value, {
71
+ get(_, key) {
72
+ return Reflect.get(nextHooks, key)
73
+ },
74
+ })
75
+ },
76
+ },
77
+ )
78
+
79
+ return { orpc: orpc as any, ORPCContext: Context }
80
+ }
@@ -0,0 +1,35 @@
1
+ import { orpc } from '../tests/orpc'
2
+ import { getMutationKey, getQueryKey } from './tanstack-key'
3
+
4
+ describe('query', () => {
5
+ it('required valid orpc', () => {
6
+ getQueryKey(orpc.ping)
7
+ getQueryKey(orpc.user.find)
8
+
9
+ // @ts-expect-error invalid orpc
10
+ getQueryKey({})
11
+
12
+ // @ts-expect-error cannot use in root
13
+ getQueryKey(orpc)
14
+ })
15
+
16
+ it('infer input', () => {
17
+ getQueryKey(orpc.user.find, { input: { id: '1' } })
18
+
19
+ // @ts-expect-error invalid input
20
+ getQueryKey(orpc.user.find, { input: { id: 1234 } })
21
+ })
22
+ })
23
+
24
+ describe('mutation', () => {
25
+ it('required valid orpc', () => {
26
+ getMutationKey(orpc.ping)
27
+ getMutationKey(orpc.user.find)
28
+
29
+ // @ts-expect-error invalid orpc
30
+ getMutationKey({})
31
+
32
+ // @ts-expect-error cannot use in root
33
+ getMutationKey(orpc)
34
+ })
35
+ })
@@ -0,0 +1,62 @@
1
+ import { orpc } from '../tests/orpc'
2
+ import {
3
+ getMutationKey,
4
+ getMutationKeyFromPath,
5
+ getQueryKey,
6
+ getQueryKeyFromPath,
7
+ } from './tanstack-key'
8
+
9
+ describe('query', () => {
10
+ it('from path', () => {
11
+ expect(getQueryKeyFromPath(['user', 'find'])).toEqual([
12
+ ['user', 'find'],
13
+ {},
14
+ ])
15
+
16
+ expect(
17
+ getQueryKeyFromPath(['user', 'find'], {
18
+ input: { id: '1' },
19
+ }),
20
+ ).toEqual([['user', 'find'], { input: { id: '1' } }])
21
+
22
+ expect(
23
+ getQueryKeyFromPath(['user', 'find'], {
24
+ input: { id: '1' },
25
+ type: 'query',
26
+ }),
27
+ ).toEqual([['user', 'find'], { input: { id: '1' }, type: 'query' }])
28
+
29
+ expect(
30
+ getQueryKeyFromPath(['user', 'find'], {
31
+ type: 'infinite',
32
+ }),
33
+ ).toEqual([['user', 'find'], { type: 'infinite' }])
34
+ })
35
+
36
+ it('from orpc', () => {
37
+ expect(getQueryKey(orpc.ping)).toEqual([['ping'], {}])
38
+
39
+ expect(getQueryKey(orpc.user.find)).toEqual([['user', 'find'], {}])
40
+
41
+ expect(getQueryKey(orpc.user.find, { input: { id: '1' } })).toEqual([
42
+ ['user', 'find'],
43
+ { input: { id: '1' } },
44
+ ])
45
+
46
+ expect(
47
+ getQueryKey(orpc.user.find, { input: { id: '1' }, type: 'infinite' }),
48
+ ).toEqual([['user', 'find'], { input: { id: '1' }, type: 'infinite' }])
49
+ })
50
+ })
51
+
52
+ describe('mutation', () => {
53
+ it('from path', () => {
54
+ expect(getMutationKeyFromPath(['user', 'create'])).toEqual([
55
+ ['user', 'create'],
56
+ ])
57
+ })
58
+
59
+ it('from orpc', () => {
60
+ expect(getMutationKey(orpc.user.create)).toEqual([['user', 'create']])
61
+ })
62
+ })
@@ -0,0 +1,64 @@
1
+ import type { SchemaInput } from '@orpc/contract'
2
+ import type { PartialDeep } from '@orpc/shared'
3
+ import type { MutationKey, QueryKey } from '@tanstack/react-query'
4
+ import { getORPCPath } from './orpc-path'
5
+ import type { ProcedureHooks } from './procedure-hooks'
6
+ import type {
7
+ ORPCHooksWithContractRouter,
8
+ ORPCHooksWithRouter,
9
+ } from './react-hooks'
10
+
11
+ export type QueryType = 'query' | 'infinite' | undefined
12
+
13
+ export interface GetQueryKeyOptions<TInput> {
14
+ input?: TInput
15
+ type?: QueryType
16
+ }
17
+
18
+ export function getQueryKey<
19
+ T extends
20
+ | ORPCHooksWithContractRouter<any>
21
+ | ORPCHooksWithRouter<any>
22
+ | ProcedureHooks<any, any, any>,
23
+ >(
24
+ orpc: T,
25
+ options?: GetQueryKeyOptions<
26
+ T extends ProcedureHooks<infer UInputSchema, any, any>
27
+ ? PartialDeep<SchemaInput<UInputSchema>>
28
+ : unknown
29
+ >,
30
+ ): QueryKey {
31
+ const path = getORPCPath(orpc)
32
+ return getQueryKeyFromPath(path, options)
33
+ }
34
+
35
+ export function getQueryKeyFromPath(
36
+ path: string[],
37
+ options?: GetQueryKeyOptions<unknown>,
38
+ ): QueryKey {
39
+ const withInput =
40
+ options?.input !== undefined ? { input: options?.input } : {}
41
+ const withType = options?.type !== undefined ? { type: options?.type } : {}
42
+
43
+ return [
44
+ path,
45
+ {
46
+ ...withInput,
47
+ ...withType,
48
+ },
49
+ ]
50
+ }
51
+
52
+ export function getMutationKey<
53
+ T extends
54
+ | ORPCHooksWithContractRouter<any>
55
+ | ORPCHooksWithRouter<any>
56
+ | ProcedureHooks<any, any, any>,
57
+ >(orpc: T): MutationKey {
58
+ const path = getORPCPath(orpc)
59
+ return getMutationKeyFromPath(path)
60
+ }
61
+
62
+ export function getMutationKeyFromPath(path: string[]): MutationKey {
63
+ return [path]
64
+ }
@@ -0,0 +1,27 @@
1
+ import type { SetOptional } from '@orpc/shared'
2
+ import type {
3
+ InvalidateQueryFilters,
4
+ QueryFilters,
5
+ } from '@tanstack/react-query'
6
+ import type { QueryType } from './tanstack-key'
7
+
8
+ export interface ORPCAdditionalQueryFilters<TFilterInput> {
9
+ /**
10
+ * The type of the query. useQuery=query, useInfiniteQuery=infinite
11
+ * If not specified, it will match all types.
12
+ */
13
+ queryType?: QueryType
14
+
15
+ /**
16
+ * The input of the query. If not specified, it will match all inputs.
17
+ */
18
+ input?: TFilterInput
19
+ }
20
+
21
+ export interface ORPCQueryFilters<TFilterInput>
22
+ extends SetOptional<QueryFilters, 'queryKey'>,
23
+ ORPCAdditionalQueryFilters<TFilterInput> {}
24
+
25
+ export interface ORPCInvalidateQueryFilters<TFilterInput>
26
+ extends SetOptional<InvalidateQueryFilters, 'queryKey'>,
27
+ ORPCAdditionalQueryFilters<TFilterInput> {}
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { Schema, SchemaInput } from '@orpc/contract'
2
+
3
+ export type SchemaInputForInfiniteQuery<TInputSchema extends Schema> = Omit<
4
+ SchemaInput<TInputSchema>,
5
+ 'cursor'
6
+ > &
7
+ (Record<string | number, any> & { cursor?: never })