@trpc/client 9.17.0 → 10.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.
Files changed (152) hide show
  1. package/dist/{TRPCClientError-bfee2bf5.cjs.dev.js → TRPCClientError-a5312a5f.cjs.dev.js} +14 -19
  2. package/dist/{TRPCClientError-09b8a26b.esm.js → TRPCClientError-e1da1ef0.esm.js} +14 -19
  3. package/dist/{TRPCClientError-e9bf96e9.cjs.prod.js → TRPCClientError-ea7d89b2.cjs.prod.js} +14 -19
  4. package/dist/createChain-93a38e1c.cjs.dev.js +27 -0
  5. package/dist/createChain-e45e052a.cjs.prod.js +27 -0
  6. package/dist/createChain-e45e6cf2.esm.js +25 -0
  7. package/dist/declarations/src/TRPCClientError.d.ts +6 -10
  8. package/dist/declarations/src/TRPCClientError.d.ts.map +1 -1
  9. package/dist/declarations/src/createTRPCClient.d.ts +1 -2
  10. package/dist/declarations/src/createTRPCClient.d.ts.map +1 -1
  11. package/dist/declarations/src/index.d.ts +1 -1
  12. package/dist/declarations/src/index.d.ts.map +1 -1
  13. package/dist/declarations/src/internals/TRPCClient.d.ts +17 -14
  14. package/dist/declarations/src/internals/TRPCClient.d.ts.map +1 -1
  15. package/dist/declarations/src/internals/dataLoader.d.ts +1 -1
  16. package/dist/declarations/src/internals/dataLoader.d.ts.map +1 -1
  17. package/dist/declarations/src/links/httpBatchLink.d.ts +2 -1
  18. package/dist/declarations/src/links/httpBatchLink.d.ts.map +1 -1
  19. package/dist/declarations/src/links/httpLink.d.ts +2 -1
  20. package/dist/declarations/src/links/httpLink.d.ts.map +1 -1
  21. package/dist/declarations/src/links/httpUtils.d.ts +22 -0
  22. package/dist/declarations/src/links/httpUtils.d.ts.map +1 -0
  23. package/dist/declarations/src/links/internals/createChain.d.ts +8 -0
  24. package/dist/declarations/src/links/internals/createChain.d.ts.map +1 -0
  25. package/dist/declarations/src/links/internals/transformOperationResult.d.ts +16 -0
  26. package/dist/declarations/src/links/internals/transformOperationResult.d.ts.map +1 -0
  27. package/dist/declarations/src/links/loggerLink.d.ts +4 -3
  28. package/dist/declarations/src/links/loggerLink.d.ts.map +1 -1
  29. package/dist/declarations/src/links/splitLink.d.ts +4 -14
  30. package/dist/declarations/src/links/splitLink.d.ts.map +1 -1
  31. package/dist/declarations/src/links/transformerLink.d.ts +4 -0
  32. package/dist/declarations/src/links/transformerLink.d.ts.map +1 -0
  33. package/dist/declarations/src/links/types.d.ts +40 -0
  34. package/dist/declarations/src/links/types.d.ts.map +1 -0
  35. package/dist/declarations/src/links/wsLink.d.ts +4 -14
  36. package/dist/declarations/src/links/wsLink.d.ts.map +1 -1
  37. package/dist/declarations/src/rx/index.d.ts +3 -0
  38. package/dist/declarations/src/rx/index.d.ts.map +1 -0
  39. package/dist/declarations/src/rx/observable.d.ts +4 -0
  40. package/dist/declarations/src/rx/observable.d.ts.map +1 -0
  41. package/dist/declarations/src/rx/operators/index.d.ts +4 -0
  42. package/dist/declarations/src/rx/operators/index.d.ts.map +1 -0
  43. package/dist/declarations/src/rx/operators/map.d.ts +3 -0
  44. package/dist/declarations/src/rx/operators/map.d.ts.map +1 -0
  45. package/dist/declarations/src/rx/operators/share.d.ts +6 -0
  46. package/dist/declarations/src/rx/operators/share.d.ts.map +1 -0
  47. package/dist/declarations/src/rx/operators/tap.d.ts +3 -0
  48. package/dist/declarations/src/rx/operators/tap.d.ts.map +1 -0
  49. package/dist/declarations/src/rx/types.d.ts +29 -0
  50. package/dist/declarations/src/rx/types.d.ts.map +1 -0
  51. package/dist/declarations/src/rx/util/identity.d.ts +2 -0
  52. package/dist/declarations/src/rx/util/identity.d.ts.map +1 -0
  53. package/dist/declarations/src/rx/util/observableToPromise.d.ts +10 -0
  54. package/dist/declarations/src/rx/util/observableToPromise.d.ts.map +1 -0
  55. package/dist/declarations/src/rx/util/pipe.d.ts +4 -0
  56. package/dist/declarations/src/rx/util/pipe.d.ts.map +1 -0
  57. package/dist/httpUtils-299a66f6.cjs.prod.js +89 -0
  58. package/dist/httpUtils-a1426e14.cjs.dev.js +89 -0
  59. package/dist/httpUtils-e45c9056.esm.js +87 -0
  60. package/dist/observable-01534ea8.esm.js +102 -0
  61. package/dist/observable-9aad8c3e.cjs.dev.js +104 -0
  62. package/dist/observable-ccb54234.cjs.prod.js +104 -0
  63. package/dist/share-5a8c2543.cjs.prod.js +106 -0
  64. package/dist/share-ca585761.esm.js +104 -0
  65. package/dist/share-f2ac9332.cjs.dev.js +106 -0
  66. package/dist/tap-2d565250.cjs.dev.js +32 -0
  67. package/dist/tap-606957cb.esm.js +30 -0
  68. package/dist/tap-d907998d.cjs.prod.js +32 -0
  69. package/dist/trpc-client.cjs.dev.js +136 -215
  70. package/dist/trpc-client.cjs.prod.js +136 -215
  71. package/dist/trpc-client.esm.js +137 -216
  72. package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.dev.js +34 -52
  73. package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.cjs.prod.js +34 -52
  74. package/links/httpBatchLink/dist/trpc-client-links-httpBatchLink.esm.js +33 -51
  75. package/links/httpLink/dist/trpc-client-links-httpLink.cjs.dev.js +29 -52
  76. package/links/httpLink/dist/trpc-client-links-httpLink.cjs.prod.js +29 -52
  77. package/links/httpLink/dist/trpc-client-links-httpLink.esm.js +29 -52
  78. package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.dev.js +31 -19
  79. package/links/loggerLink/dist/trpc-client-links-loggerLink.cjs.prod.js +31 -19
  80. package/links/loggerLink/dist/trpc-client-links-loggerLink.esm.js +31 -19
  81. package/links/splitLink/dist/trpc-client-links-splitLink.cjs.dev.js +20 -7
  82. package/links/splitLink/dist/trpc-client-links-splitLink.cjs.prod.js +20 -7
  83. package/links/splitLink/dist/trpc-client-links-splitLink.esm.js +20 -7
  84. package/links/transformerLink/package.json +4 -0
  85. package/links/wsLink/dist/trpc-client-links-wsLink.cjs.dev.js +57 -82
  86. package/links/wsLink/dist/trpc-client-links-wsLink.cjs.prod.js +57 -82
  87. package/links/wsLink/dist/trpc-client-links-wsLink.esm.js +57 -82
  88. package/package.json +12 -8
  89. package/rx/dist/trpc-client-rx.cjs.d.ts +1 -0
  90. package/rx/dist/trpc-client-rx.cjs.dev.js +9 -0
  91. package/rx/dist/trpc-client-rx.cjs.js +7 -0
  92. package/rx/dist/trpc-client-rx.cjs.prod.js +9 -0
  93. package/rx/dist/trpc-client-rx.esm.js +1 -0
  94. package/rx/operators/dist/trpc-client-rx-operators.cjs.d.ts +1 -0
  95. package/rx/operators/dist/trpc-client-rx-operators.cjs.dev.js +39 -0
  96. package/rx/operators/dist/trpc-client-rx-operators.cjs.js +7 -0
  97. package/rx/operators/dist/trpc-client-rx-operators.cjs.prod.js +39 -0
  98. package/rx/operators/dist/trpc-client-rx-operators.esm.js +33 -0
  99. package/rx/operators/package.json +4 -0
  100. package/rx/package.json +4 -0
  101. package/src/TRPCClientError.ts +17 -21
  102. package/src/createTRPCClient.ts +4 -3
  103. package/src/index.ts +1 -1
  104. package/src/internals/TRPCClient.ts +80 -88
  105. package/src/internals/dataLoader.ts +1 -1
  106. package/src/internals/transformRPCResponse.ts +2 -2
  107. package/src/links/dedupeLink.test.ts +134 -0
  108. package/src/links/dedupeLink.ts +53 -0
  109. package/src/links/httpBatchLink.ts +37 -37
  110. package/src/links/httpLink.ts +26 -37
  111. package/src/{internals/httpRequest.ts → links/httpUtils.ts} +30 -21
  112. package/src/links/internals/createChain.test.ts +132 -0
  113. package/src/links/internals/createChain.ts +31 -0
  114. package/src/links/internals/transformOperationResult.ts +25 -0
  115. package/src/links/loggerLink.ts +40 -22
  116. package/src/links/retryLink.ts +45 -14
  117. package/src/links/splitLink.test.ts +56 -0
  118. package/src/links/splitLink.ts +23 -38
  119. package/src/links/transformerLink.ts +70 -0
  120. package/src/links/{core.ts → types.ts} +40 -46
  121. package/src/links/wsLink.ts +55 -77
  122. package/src/rx/index.ts +2 -0
  123. package/src/rx/observable.test.ts +83 -0
  124. package/src/rx/observable.ts +70 -0
  125. package/src/rx/operators/index.ts +3 -0
  126. package/src/rx/operators/map.test.ts +92 -0
  127. package/src/rx/operators/map.ts +25 -0
  128. package/src/rx/operators/share.test.ts +60 -0
  129. package/src/rx/operators/share.ts +67 -0
  130. package/src/rx/operators/tap.ts +26 -0
  131. package/src/rx/types.ts +69 -0
  132. package/src/rx/util/identity.ts +3 -0
  133. package/src/rx/util/observableToPromise.ts +49 -0
  134. package/src/rx/util/pipe.ts +22 -0
  135. package/dist/declarations/src/internals/TRPCAbortError.d.ts +0 -4
  136. package/dist/declarations/src/internals/TRPCAbortError.d.ts.map +0 -1
  137. package/dist/declarations/src/internals/executeChain.d.ts +0 -9
  138. package/dist/declarations/src/internals/executeChain.d.ts.map +0 -1
  139. package/dist/declarations/src/internals/httpRequest.d.ts +0 -14
  140. package/dist/declarations/src/internals/httpRequest.d.ts.map +0 -1
  141. package/dist/declarations/src/internals/observable.d.ts +0 -18
  142. package/dist/declarations/src/internals/observable.d.ts.map +0 -1
  143. package/dist/declarations/src/internals/transformRPCResponse.d.ts +0 -15
  144. package/dist/declarations/src/internals/transformRPCResponse.d.ts.map +0 -1
  145. package/dist/declarations/src/links/core.d.ts +0 -45
  146. package/dist/declarations/src/links/core.d.ts.map +0 -1
  147. package/dist/httpRequest-672ff1f0.esm.js +0 -128
  148. package/dist/httpRequest-7bfaf300.cjs.dev.js +0 -132
  149. package/dist/httpRequest-8c422e7a.cjs.prod.js +0 -132
  150. package/src/internals/TRPCAbortError.ts +0 -7
  151. package/src/internals/executeChain.ts +0 -67
  152. package/src/internals/observable.ts +0 -89
@@ -1,6 +1,8 @@
1
1
  import { ProcedureType } from '@trpc/server';
2
- import { TRPCResponse } from '@trpc/server/rpc';
3
- import { LinkRuntimeOptions, PromiseAndCancel } from '../links/core';
2
+ import { LinkRuntime, PromiseAndCancel } from './types';
3
+ export interface HTTPLinkOptions {
4
+ url: string;
5
+ }
4
6
 
5
7
  // https://github.com/trpc/trpc/pull/669
6
8
  function arrayToDict(array: unknown[]) {
@@ -11,28 +13,30 @@ function arrayToDict(array: unknown[]) {
11
13
  }
12
14
  return dict;
13
15
  }
16
+ const METHOD = {
17
+ query: 'GET',
18
+ mutation: 'POST',
19
+ subscription: 'PATCH',
20
+ } as const;
14
21
 
15
- export function httpRequest<TResponseShape = TRPCResponse>(
22
+ export interface ResponseShape {
23
+ json: unknown;
24
+ meta: {
25
+ response: Response;
26
+ };
27
+ }
28
+ export function httpRequest(
16
29
  props: {
17
- runtime: LinkRuntimeOptions;
30
+ runtime: LinkRuntime;
18
31
  type: ProcedureType;
19
32
  path: string;
20
33
  url: string;
21
34
  } & ({ inputs: unknown[] } | { input: unknown }),
22
- ): PromiseAndCancel<TResponseShape> {
35
+ ): PromiseAndCancel<ResponseShape> {
23
36
  const { type, runtime: rt, path } = props;
24
37
  const ac = rt.AbortController ? new rt.AbortController() : null;
25
- const method = {
26
- query: 'GET',
27
- mutation: 'POST',
28
- subscription: 'PATCH',
29
- };
30
- const input =
31
- 'input' in props
32
- ? rt.transformer.serialize(props.input)
33
- : arrayToDict(
34
- props.inputs.map((_input) => rt.transformer.serialize(_input)),
35
- );
38
+
39
+ const input = 'input' in props ? props.input : arrayToDict(props.inputs);
36
40
 
37
41
  function getUrl() {
38
42
  let url = props.url + '/' + path;
@@ -55,13 +59,14 @@ export function httpRequest<TResponseShape = TRPCResponse>(
55
59
  return input !== undefined ? JSON.stringify(input) : undefined;
56
60
  }
57
61
 
58
- const promise = new Promise<TResponseShape>((resolve, reject) => {
62
+ const promise = new Promise<ResponseShape>((resolve, reject) => {
59
63
  const url = getUrl();
60
64
 
65
+ const meta = {} as any as ResponseShape['meta'];
61
66
  Promise.resolve(rt.headers())
62
67
  .then((headers) =>
63
68
  rt.fetch(url, {
64
- method: method[type],
69
+ method: METHOD[type],
65
70
  signal: ac?.signal,
66
71
  body: getBody(),
67
72
  headers: {
@@ -70,11 +75,15 @@ export function httpRequest<TResponseShape = TRPCResponse>(
70
75
  },
71
76
  }),
72
77
  )
73
- .then((res) => {
74
- return res.json();
78
+ .then((_res) => {
79
+ meta.response = _res;
80
+ return _res.json();
75
81
  })
76
82
  .then((json) => {
77
- resolve(json);
83
+ resolve({
84
+ json,
85
+ meta,
86
+ });
78
87
  })
79
88
  .catch(reject);
80
89
  });
@@ -0,0 +1,132 @@
1
+ import { AnyRouter } from '@trpc/server/src';
2
+ import { createChain } from './createChain';
3
+ import { observable } from '../../rx/observable';
4
+
5
+ describe('chain', () => {
6
+ test('trivial', () => {
7
+ const result$ = createChain<AnyRouter, unknown, unknown>({
8
+ links: [
9
+ ({ next, op }) => {
10
+ return observable((observer) => {
11
+ const next$ = next(op).subscribe(observer);
12
+ return () => {
13
+ next$.unsubscribe();
14
+ };
15
+ });
16
+ },
17
+ ({ op }) => {
18
+ return observable((observer) => {
19
+ observer.next({
20
+ context: {},
21
+ data: {
22
+ id: null,
23
+ result: {
24
+ type: 'data',
25
+ data: {
26
+ input: op.input,
27
+ },
28
+ },
29
+ },
30
+ });
31
+ observer.complete();
32
+ });
33
+ },
34
+ ],
35
+ op: {
36
+ type: 'query',
37
+ id: 1,
38
+ input: 'world',
39
+ path: 'hello',
40
+ context: {},
41
+ },
42
+ });
43
+
44
+ const next = jest.fn();
45
+
46
+ result$.subscribe({ next });
47
+ // console.log(next.mock.calls);
48
+ expect(next).toHaveBeenCalledTimes(1);
49
+ });
50
+ test('multiple responses', () => {
51
+ const result$ = createChain<AnyRouter, unknown, unknown>({
52
+ links: [
53
+ ({ next, op }) => {
54
+ return observable((observer) => {
55
+ observer.next({
56
+ context: {},
57
+ data: {
58
+ id: null,
59
+ result: {
60
+ type: 'data',
61
+ data: 'from cache',
62
+ },
63
+ },
64
+ });
65
+ const next$ = next(op).subscribe(observer);
66
+ return () => {
67
+ next$.unsubscribe();
68
+ };
69
+ });
70
+ },
71
+ ({ op }) => {
72
+ return observable((observer) => {
73
+ observer.next({
74
+ data: {
75
+ id: null,
76
+ result: {
77
+ type: 'data',
78
+ data: {
79
+ input: op.input,
80
+ },
81
+ },
82
+ },
83
+ });
84
+ observer.complete();
85
+ });
86
+ },
87
+ ],
88
+ op: {
89
+ type: 'query',
90
+ id: 1,
91
+ input: 'world',
92
+ path: 'hello',
93
+ context: {},
94
+ },
95
+ });
96
+
97
+ const next = jest.fn();
98
+
99
+ result$.subscribe({ next });
100
+
101
+ expect(next).toHaveBeenCalledTimes(2);
102
+ expect(next.mock.calls[0]).toMatchInlineSnapshot(`
103
+ Array [
104
+ Object {
105
+ "context": Object {},
106
+ "data": Object {
107
+ "id": null,
108
+ "result": Object {
109
+ "data": "from cache",
110
+ "type": "data",
111
+ },
112
+ },
113
+ },
114
+ ]
115
+ `);
116
+ expect(next.mock.calls[1]).toMatchInlineSnapshot(`
117
+ Array [
118
+ Object {
119
+ "data": Object {
120
+ "id": null,
121
+ "result": Object {
122
+ "data": Object {
123
+ "input": "world",
124
+ },
125
+ "type": "data",
126
+ },
127
+ },
128
+ },
129
+ ]
130
+ `);
131
+ });
132
+ });
@@ -0,0 +1,31 @@
1
+ import { AnyRouter } from '@trpc/server';
2
+ import { observable } from '../../rx/observable';
3
+ import { Operation, OperationLink, OperationResultObservable } from '../types';
4
+
5
+ /** @internal */
6
+ export function createChain<
7
+ TRouter extends AnyRouter,
8
+ TInput = unknown,
9
+ TOutput = unknown,
10
+ >(opts: {
11
+ links: OperationLink<TRouter, TInput, TOutput>[];
12
+ op: Operation<TInput>;
13
+ }): OperationResultObservable<TRouter, TOutput> {
14
+ return observable((observer) => {
15
+ function execute(index = 0, op = opts.op) {
16
+ const next = opts.links[index];
17
+ const next$ = next({
18
+ op,
19
+ next(nextOp) {
20
+ const nextObserver = execute(index + 1, nextOp);
21
+
22
+ return nextObserver;
23
+ },
24
+ });
25
+ return next$;
26
+ }
27
+
28
+ const obs$ = execute();
29
+ return obs$.subscribe(observer);
30
+ });
31
+ }
@@ -0,0 +1,25 @@
1
+ import { AnyRouter } from '@trpc/server';
2
+ import { TRPCClientError } from '../../TRPCClientError';
3
+ import { OperationResult } from '../types';
4
+
5
+ /** @internal */
6
+ export function transformOperationResult<TRouter extends AnyRouter, TOutput>(
7
+ result: OperationResult<TRouter, TOutput>,
8
+ ) {
9
+ const { context } = result;
10
+
11
+ if ('error' in result.data) {
12
+ const error = TRPCClientError.from<TRouter>(result.data);
13
+ return {
14
+ ok: false,
15
+ error,
16
+ context,
17
+ } as const;
18
+ }
19
+ const data = (result.data.result as any).data as TOutput;
20
+ return {
21
+ ok: true,
22
+ data,
23
+ context,
24
+ } as const;
25
+ }
@@ -1,5 +1,8 @@
1
1
  import { AnyRouter } from '@trpc/server';
2
- import { Operation, OperationResponse, TRPCLink } from './core';
2
+ import { TRPCClientError } from '..';
3
+ import { observable } from '../rx/observable';
4
+ import { tap } from '../rx/operators';
5
+ import { Operation, OperationResult, TRPCLink } from './types';
3
6
 
4
7
  type ConsoleEsque = {
5
8
  log: (...args: any[]) => void;
@@ -12,7 +15,7 @@ type EnableFnOptions<TRouter extends AnyRouter> =
12
15
  })
13
16
  | {
14
17
  direction: 'down';
15
- result: OperationResponse<TRouter>;
18
+ result: OperationResult<TRouter, unknown> | TRPCClientError<TRouter>;
16
19
  };
17
20
  type EnabledFn<TRouter extends AnyRouter> = (
18
21
  opts: EnableFnOptions<TRouter>,
@@ -31,7 +34,7 @@ type LogFnOptions<TRouter extends AnyRouter> = Operation &
31
34
  * Request result
32
35
  */
33
36
  direction: 'down';
34
- result: OperationResponse<TRouter>;
37
+ result: OperationResult<TRouter, unknown> | TRPCClientError<TRouter>;
35
38
  elapsedMs: number;
36
39
  }
37
40
  );
@@ -78,7 +81,7 @@ const defaultLogger =
78
81
  `${css}; font-weight: normal;`,
79
82
  ];
80
83
  if (props.direction === 'up') {
81
- args.push({ input, context });
84
+ args.push({ input, context: context });
82
85
  } else {
83
86
  args.push({
84
87
  input,
@@ -90,7 +93,7 @@ const defaultLogger =
90
93
  const fn: 'error' | 'log' =
91
94
  props.direction === 'down' &&
92
95
  props.result &&
93
- props.result instanceof Error
96
+ (props.result instanceof Error || 'error' in props.result.data)
94
97
  ? 'error'
95
98
  : 'log';
96
99
 
@@ -102,27 +105,42 @@ export function loggerLink<TRouter extends AnyRouter = AnyRouter>(
102
105
  const { enabled = () => true } = opts;
103
106
 
104
107
  const { logger = defaultLogger(opts.console) } = opts;
105
- return () => {
106
- return ({ op, next, prev }) => {
107
- // ->
108
- enabled({ ...op, direction: 'up' }) &&
109
- logger({
110
- ...op,
111
- direction: 'up',
112
- });
113
- const requestStartTime = Date.now();
114
- next(op, (result) => {
115
- const elapsedMs = Date.now() - requestStartTime;
116
108
 
117
- enabled({ ...op, direction: 'down', result }) &&
109
+ return () => {
110
+ return ({ op, next }) => {
111
+ return observable((observer) => {
112
+ // ->
113
+ enabled({ ...op, direction: 'up' }) &&
118
114
  logger({
119
115
  ...op,
120
- direction: 'down',
121
- elapsedMs,
122
- result,
116
+ direction: 'up',
123
117
  });
124
- // <-
125
- prev(result);
118
+ const requestStartTime = Date.now();
119
+ function logResult(
120
+ result: OperationResult<TRouter, unknown> | TRPCClientError<TRouter>,
121
+ ) {
122
+ const elapsedMs = Date.now() - requestStartTime;
123
+
124
+ enabled({ ...op, direction: 'down', result }) &&
125
+ logger({
126
+ ...op,
127
+ direction: 'down',
128
+ elapsedMs,
129
+ result,
130
+ });
131
+ }
132
+ return next(op)
133
+ .pipe(
134
+ tap({
135
+ next(result) {
136
+ logResult(result);
137
+ },
138
+ error(result) {
139
+ logResult(result);
140
+ },
141
+ }),
142
+ )
143
+ .subscribe(observer);
126
144
  });
127
145
  };
128
146
  };
@@ -1,5 +1,7 @@
1
1
  import { AnyRouter } from '@trpc/server';
2
- import { TRPCLink } from './core';
2
+ import { observable } from '../rx/observable';
3
+ import { Unsubscribable } from '../rx/types';
4
+ import { TRPCLink } from './types';
3
5
 
4
6
  export function retryLink<TRouter extends AnyRouter = AnyRouter>(opts: {
5
7
  attempts: number;
@@ -7,20 +9,49 @@ export function retryLink<TRouter extends AnyRouter = AnyRouter>(opts: {
7
9
  // initialized config
8
10
  return () => {
9
11
  // initialized in app
10
- return ({ op, next, prev }) => {
12
+ return ({ op, next }) => {
11
13
  // initialized for request
12
- let attempts = 0;
13
- const fn = () => {
14
- attempts++;
15
- next(op, (result) => {
16
- if (result instanceof Error) {
17
- attempts < opts.attempts ? fn() : prev(result);
18
- } else {
19
- prev(result);
20
- }
21
- });
22
- };
23
- fn();
14
+ return observable((observer) => {
15
+ let next$: Unsubscribable | null = null;
16
+ let attempts = 0;
17
+ let isDone = false;
18
+ function attempt() {
19
+ attempts++;
20
+ next$?.unsubscribe();
21
+ next$ = next(op).subscribe({
22
+ error(error) {
23
+ if (attempts >= opts.attempts) {
24
+ observer.error(error);
25
+ return;
26
+ }
27
+ attempt();
28
+ },
29
+ next(result) {
30
+ if ('result' in result.data) {
31
+ isDone = true;
32
+ observer.next(result);
33
+ return;
34
+ }
35
+ if (attempts >= opts.attempts) {
36
+ isDone = true;
37
+ observer.next(result);
38
+ return;
39
+ }
40
+ attempt();
41
+ },
42
+ complete() {
43
+ if (isDone) {
44
+ observer.complete();
45
+ }
46
+ },
47
+ });
48
+ }
49
+ attempt();
50
+ return () => {
51
+ isDone = true;
52
+ next$?.unsubscribe();
53
+ };
54
+ });
24
55
  };
25
56
  };
26
57
  }
@@ -0,0 +1,56 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-function */
2
+ import { AnyRouter } from '../../../server/src';
3
+ import { createChain } from '../links/internals/createChain';
4
+ import { splitLink } from '../links/splitLink';
5
+ import { OperationLink, TRPCLink } from '../links/types';
6
+ import { observable } from '../rx';
7
+
8
+ test('splitLink', () => {
9
+ const wsLinkSpy = jest.fn();
10
+ const wsLink: TRPCLink<any> = () => () =>
11
+ observable(() => {
12
+ wsLinkSpy();
13
+ });
14
+ const httpLinkSpy = jest.fn();
15
+ const httpLink: TRPCLink<any> = () => () =>
16
+ observable(() => {
17
+ httpLinkSpy();
18
+ });
19
+ const links: OperationLink<AnyRouter, any, any>[] = [
20
+ // "dedupe link"
21
+ splitLink({
22
+ condition(op) {
23
+ return op.type === 'subscription';
24
+ },
25
+ true: wsLink,
26
+ false: [httpLink],
27
+ })(null as any),
28
+ ];
29
+
30
+ createChain({
31
+ links,
32
+ op: {
33
+ type: 'query',
34
+ input: null,
35
+ path: '.',
36
+ id: 0,
37
+ context: {},
38
+ },
39
+ }).subscribe({});
40
+ expect(httpLinkSpy).toHaveBeenCalledTimes(1);
41
+ expect(wsLinkSpy).toHaveBeenCalledTimes(0);
42
+ jest.resetAllMocks();
43
+
44
+ createChain({
45
+ links,
46
+ op: {
47
+ type: 'subscription',
48
+ input: null,
49
+ path: '.',
50
+ id: 0,
51
+ context: {},
52
+ },
53
+ }).subscribe({});
54
+ expect(httpLinkSpy).toHaveBeenCalledTimes(0);
55
+ expect(wsLinkSpy).toHaveBeenCalledTimes(1);
56
+ });
@@ -1,45 +1,30 @@
1
1
  import { AnyRouter } from '@trpc/server';
2
- import { TRPCLink, Operation } from './core';
2
+ import { observable } from '../rx/observable';
3
+ import { TRPCLink, Operation } from './types';
4
+ import { createChain } from './internals/createChain';
3
5
 
4
- export function splitLink<TRouter extends AnyRouter = AnyRouter>(
5
- opts: {
6
- condition: (op: Operation) => boolean;
7
- } & (
8
- | {
9
- /**
10
- * The link to execute next if the test function returns `true`.
11
- */
12
- left: TRPCLink<TRouter>;
13
- /**
14
- * The link to execute next if the test function returns `false`.
15
- */
16
- right: TRPCLink<TRouter>;
17
- }
18
- | {
19
- /**
20
- * The link to execute next if the test function returns `true`.
21
- */
22
- true: TRPCLink<TRouter>;
23
- /**
24
- * The link to execute next if the test function returns `false`.
25
- */
26
- false: TRPCLink<TRouter>;
27
- }
28
- ),
29
- ): TRPCLink<TRouter> {
6
+ function asArray<T>(value: T | T[]) {
7
+ return Array.isArray(value) ? value : [value];
8
+ }
9
+ export function splitLink<TRouter extends AnyRouter = AnyRouter>(opts: {
10
+ condition: (op: Operation) => boolean;
11
+ /**
12
+ * The link to execute next if the test function returns `true`.
13
+ */
14
+ true: TRPCLink<TRouter> | TRPCLink<TRouter>[];
15
+ /**
16
+ * The link to execute next if the test function returns `false`.
17
+ */
18
+ false: TRPCLink<TRouter> | TRPCLink<TRouter>[];
19
+ }): TRPCLink<TRouter> {
30
20
  return (rt) => {
31
- const links =
32
- 'left' in opts
33
- ? {
34
- true: opts.left,
35
- false: opts.right,
36
- }
37
- : opts;
38
-
39
- const yes = links.true(rt);
40
- const no = links.false(rt);
21
+ const yes = asArray(opts.true).map((link) => link(rt));
22
+ const no = asArray(opts.false).map((link) => link(rt));
41
23
  return (props) => {
42
- opts.condition(props.op) ? yes(props) : no(props);
24
+ return observable((observer) => {
25
+ const links = opts.condition(props.op) ? yes : no;
26
+ return createChain({ op: props.op, links }).subscribe(observer);
27
+ });
43
28
  };
44
29
  };
45
30
  }
@@ -0,0 +1,70 @@
1
+ import {
2
+ AnyRouter,
3
+ ClientDataTransformerOptions,
4
+ DataTransformer,
5
+ } from '@trpc/server';
6
+ import { observable } from '../rx/observable';
7
+ import { OperationResult, TRPCLink } from './types';
8
+
9
+ /**
10
+ * @internal
11
+ */
12
+ function transformOperationResult<TResult extends OperationResult<any, any>>(
13
+ result: TResult,
14
+ transformer: DataTransformer,
15
+ ) {
16
+ if ('error' in result.data) {
17
+ return {
18
+ ...result,
19
+ data: {
20
+ ...result.data,
21
+ error: transformer.deserialize(result.data.error),
22
+ },
23
+ };
24
+ }
25
+ if (result.data.result.type !== 'data') {
26
+ return result;
27
+ }
28
+ return {
29
+ ...result,
30
+ data: {
31
+ ...result.data,
32
+ result: {
33
+ ...result.data.result,
34
+ data: transformer.deserialize(result.data.result.data),
35
+ },
36
+ },
37
+ };
38
+ }
39
+ export function transformerLink<TRouter extends AnyRouter = AnyRouter>(
40
+ transformer: ClientDataTransformerOptions,
41
+ ): TRPCLink<TRouter> {
42
+ const _transformer: DataTransformer = transformer
43
+ ? 'input' in transformer
44
+ ? {
45
+ serialize: transformer.input.serialize,
46
+ deserialize: transformer.output.deserialize,
47
+ }
48
+ : transformer
49
+ : {
50
+ serialize: (data) => data,
51
+ deserialize: (data) => data,
52
+ };
53
+
54
+ return () => {
55
+ return (props) => {
56
+ const input = _transformer.serialize(props.op.input);
57
+ return observable((observer) => {
58
+ const next$ = props.next({ ...props.op, input }).subscribe({
59
+ next(value) {
60
+ const transformed = transformOperationResult(value, _transformer);
61
+ observer.next(transformed);
62
+ },
63
+ error: observer.error,
64
+ complete: observer.complete,
65
+ });
66
+ return next$;
67
+ });
68
+ };
69
+ };
70
+ }