@trpc/client 9.20.1 → 10.0.0-alpha.10

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 (154) hide show
  1. package/dist/TRPCClientError-17b117e1.mjs +81 -0
  2. package/dist/TRPCClientError-ec6bd2ca.js +94 -0
  3. package/dist/{declarations/src/TRPCClientError.d.ts → TRPCClientError.d.ts} +29 -33
  4. package/dist/TRPCClientError.d.ts.map +1 -0
  5. package/dist/createChain-3b458753.js +27 -0
  6. package/dist/createChain-b7cb1f5a.mjs +25 -0
  7. package/dist/{declarations/src/createTRPCClient.d.ts → createTRPCClient.d.ts} +5 -6
  8. package/dist/createTRPCClient.d.ts.map +1 -0
  9. package/dist/{declarations/src/getFetch.d.ts → getFetch.d.ts} +1 -1
  10. package/dist/getFetch.d.ts.map +1 -0
  11. package/dist/httpUtils-1f9e4e3c.js +97 -0
  12. package/dist/httpUtils-c09d7b79.mjs +91 -0
  13. package/dist/{declarations/src/index.d.ts → index.d.ts} +4 -4
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +243 -0
  16. package/dist/index.mjs +233 -0
  17. package/dist/index.ts.js +1 -0
  18. package/dist/{declarations/src/internals → internals}/TRPCClient.d.ts +62 -61
  19. package/dist/internals/TRPCClient.d.ts.map +1 -0
  20. package/dist/{declarations/src/internals → internals}/dataLoader.d.ts +16 -16
  21. package/dist/internals/dataLoader.d.ts.map +1 -0
  22. package/dist/{declarations/src/internals → internals}/fetchHelpers.d.ts +2 -2
  23. package/dist/internals/fetchHelpers.d.ts.map +1 -0
  24. package/dist/{declarations/src/internals → internals}/retryDelay.d.ts +1 -1
  25. package/dist/internals/retryDelay.d.ts.map +1 -0
  26. package/dist/links/dedupeLink.d.ts +4 -0
  27. package/dist/links/dedupeLink.d.ts.map +1 -0
  28. package/dist/{declarations/src/links → links}/httpBatchLink.d.ts +7 -6
  29. package/dist/links/httpBatchLink.d.ts.map +1 -0
  30. package/dist/links/httpBatchLink.js +195 -0
  31. package/dist/links/httpBatchLink.mjs +191 -0
  32. package/dist/links/httpBatchLink.ts.js +1 -0
  33. package/dist/{declarations/src/links → links}/httpLink.d.ts +4 -3
  34. package/dist/links/httpLink.d.ts.map +1 -0
  35. package/dist/links/httpLink.js +44 -0
  36. package/dist/links/httpLink.mjs +40 -0
  37. package/dist/links/httpLink.ts.js +1 -0
  38. package/dist/links/httpUtils.d.ts +22 -0
  39. package/dist/links/httpUtils.d.ts.map +1 -0
  40. package/dist/links/internals/createChain.d.ts +8 -0
  41. package/dist/links/internals/createChain.d.ts.map +1 -0
  42. package/dist/links/internals/transformOperationResult.d.ts +16 -0
  43. package/dist/links/internals/transformOperationResult.d.ts.map +1 -0
  44. package/dist/{declarations/src/links → links}/loggerLink.d.ts +38 -37
  45. package/dist/links/loggerLink.d.ts.map +1 -0
  46. package/dist/links/loggerLink.js +107 -0
  47. package/dist/links/loggerLink.mjs +98 -0
  48. package/dist/links/loggerLink.ts.js +1 -0
  49. package/dist/links/retryLink.d.ts +6 -0
  50. package/dist/links/retryLink.d.ts.map +1 -0
  51. package/dist/links/splitLink.d.ts +14 -0
  52. package/dist/links/splitLink.d.ts.map +1 -0
  53. package/dist/links/splitLink.js +32 -0
  54. package/dist/links/splitLink.mjs +28 -0
  55. package/dist/links/splitLink.ts.js +1 -0
  56. package/dist/links/transformerLink.d.ts +4 -0
  57. package/dist/links/transformerLink.d.ts.map +1 -0
  58. package/dist/links/transformerLink.js +79 -0
  59. package/dist/links/transformerLink.mjs +71 -0
  60. package/dist/links/transformerLink.ts.js +1 -0
  61. package/dist/links/types.d.ts +39 -0
  62. package/dist/links/types.d.ts.map +1 -0
  63. package/dist/links/wsLink.d.ts +21 -0
  64. package/dist/links/wsLink.d.ts.map +1 -0
  65. package/dist/links/wsLink.js +385 -0
  66. package/dist/links/wsLink.mjs +370 -0
  67. package/dist/links/wsLink.ts.js +1 -0
  68. package/dist/rx.d.ts +11 -0
  69. package/dist/rx.d.ts.map +1 -0
  70. package/dist/rx.js +88 -0
  71. package/dist/rx.mjs +73 -0
  72. package/dist/rx.ts.js +1 -0
  73. package/links/httpBatchLink/index.d.ts +1 -0
  74. package/links/httpBatchLink/index.js +1 -0
  75. package/links/httpLink/index.d.ts +1 -0
  76. package/links/httpLink/index.js +1 -0
  77. package/links/loggerLink/index.d.ts +1 -0
  78. package/links/loggerLink/index.js +1 -0
  79. package/links/splitLink/index.d.ts +1 -0
  80. package/links/splitLink/index.js +1 -0
  81. package/links/transformerLink/index.d.ts +1 -0
  82. package/links/transformerLink/index.js +1 -0
  83. package/links/wsLink/index.d.ts +1 -0
  84. package/links/wsLink/index.js +1 -0
  85. package/package.json +53 -18
  86. package/src/TRPCClientError.ts +17 -21
  87. package/src/createTRPCClient.ts +4 -3
  88. package/src/index.ts +1 -1
  89. package/src/internals/TRPCClient.ts +78 -92
  90. package/src/internals/dataLoader.ts +1 -1
  91. package/src/links/dedupeLink.test.ts +134 -0
  92. package/src/links/dedupeLink.ts +54 -0
  93. package/src/links/httpBatchLink.ts +36 -46
  94. package/src/links/httpLink.ts +26 -37
  95. package/src/{internals/httpRequest.ts → links/httpUtils.ts} +30 -21
  96. package/src/links/internals/createChain.test.ts +132 -0
  97. package/src/links/internals/createChain.ts +31 -0
  98. package/src/links/internals/transformOperationResult.ts +25 -0
  99. package/src/links/loggerLink.ts +39 -22
  100. package/src/links/retryLink.ts +44 -14
  101. package/src/links/splitLink.test.ts +56 -0
  102. package/src/links/splitLink.ts +23 -38
  103. package/src/links/transformerLink.ts +74 -0
  104. package/src/links/types.ts +64 -0
  105. package/src/links/wsLink.ts +57 -77
  106. package/src/rx.ts +50 -0
  107. package/dist/TRPCClientError-09b8a26b.esm.js +0 -77
  108. package/dist/TRPCClientError-bfee2bf5.cjs.dev.js +0 -79
  109. package/dist/TRPCClientError-e9bf96e9.cjs.prod.js +0 -79
  110. package/dist/declarations/src/TRPCClientError.d.ts.map +0 -1
  111. package/dist/declarations/src/createTRPCClient.d.ts.map +0 -1
  112. package/dist/declarations/src/getFetch.d.ts.map +0 -1
  113. package/dist/declarations/src/index.d.ts.map +0 -1
  114. package/dist/declarations/src/internals/TRPCAbortError.d.ts +0 -4
  115. package/dist/declarations/src/internals/TRPCAbortError.d.ts.map +0 -1
  116. package/dist/declarations/src/internals/TRPCClient.d.ts.map +0 -1
  117. package/dist/declarations/src/internals/dataLoader.d.ts.map +0 -1
  118. package/dist/declarations/src/internals/executeChain.d.ts +0 -9
  119. package/dist/declarations/src/internals/executeChain.d.ts.map +0 -1
  120. package/dist/declarations/src/internals/fetchHelpers.d.ts.map +0 -1
  121. package/dist/declarations/src/internals/httpRequest.d.ts +0 -14
  122. package/dist/declarations/src/internals/httpRequest.d.ts.map +0 -1
  123. package/dist/declarations/src/internals/observable.d.ts +0 -18
  124. package/dist/declarations/src/internals/observable.d.ts.map +0 -1
  125. package/dist/declarations/src/internals/retryDelay.d.ts.map +0 -1
  126. package/dist/declarations/src/internals/transformRPCResponse.d.ts +0 -15
  127. package/dist/declarations/src/internals/transformRPCResponse.d.ts.map +0 -1
  128. package/dist/declarations/src/links/core.d.ts +0 -45
  129. package/dist/declarations/src/links/core.d.ts.map +0 -1
  130. package/dist/declarations/src/links/httpBatchLink.d.ts.map +0 -1
  131. package/dist/declarations/src/links/httpLink.d.ts.map +0 -1
  132. package/dist/declarations/src/links/loggerLink.d.ts.map +0 -1
  133. package/dist/declarations/src/links/splitLink.d.ts +0 -24
  134. package/dist/declarations/src/links/splitLink.d.ts.map +0 -1
  135. package/dist/declarations/src/links/wsLink.d.ts +0 -30
  136. package/dist/declarations/src/links/wsLink.d.ts.map +0 -1
  137. package/dist/transformRPCResponse-57e80136.cjs.dev.js +0 -133
  138. package/dist/transformRPCResponse-739c13d0.cjs.prod.js +0 -133
  139. package/dist/transformRPCResponse-f7f8b0e1.esm.js +0 -129
  140. package/dist/trpc-client.cjs.d.ts +0 -1
  141. package/dist/trpc-client.cjs.dev.js +0 -379
  142. package/dist/trpc-client.cjs.js +0 -7
  143. package/dist/trpc-client.cjs.prod.js +0 -379
  144. package/dist/trpc-client.esm.js +0 -374
  145. package/links/httpBatchLink/package.json +0 -4
  146. package/links/httpLink/package.json +0 -4
  147. package/links/loggerLink/package.json +0 -4
  148. package/links/splitLink/package.json +0 -4
  149. package/links/wsLink/package.json +0 -4
  150. package/src/internals/TRPCAbortError.ts +0 -7
  151. package/src/internals/executeChain.ts +0 -67
  152. package/src/internals/observable.ts +0 -89
  153. package/src/internals/transformRPCResponse.ts +0 -25
  154. package/src/links/core.ts +0 -71
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trpc/client",
3
- "version": "9.20.1",
3
+ "version": "10.0.0-alpha.10",
4
4
  "description": "tRPC Client lib",
5
5
  "author": "KATT",
6
6
  "license": "MIT",
@@ -10,37 +10,72 @@
10
10
  "url": "git+https://github.com/trpc/trpc.git",
11
11
  "directory": "packages/client"
12
12
  },
13
- "preconstruct": {
14
- "entrypoints": [
15
- "index.ts",
16
- "links/httpLink.ts",
17
- "links/httpBatchLink.ts",
18
- "links/splitLink.ts",
19
- "links/loggerLink.ts",
20
- "links/wsLink.ts"
21
- ]
13
+ "main": "dist/index.js",
14
+ "module": "dist/index.mjs",
15
+ "typings": "dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./rx": {
23
+ "import": "./dist/rx.mjs",
24
+ "require": "./dist/rx.js",
25
+ "default": "./dist/rx.js"
26
+ },
27
+ "./links/httpBatchLink": {
28
+ "import": "./dist/links/httpBatchLink.mjs",
29
+ "require": "./dist/links/httpBatchLink.js",
30
+ "default": "./dist/links/httpBatchLink.js"
31
+ },
32
+ "./links/httpLink": {
33
+ "import": "./dist/links/httpLink.mjs",
34
+ "require": "./dist/links/httpLink.js",
35
+ "default": "./dist/links/httpLink.js"
36
+ },
37
+ "./links/loggerLink": {
38
+ "import": "./dist/links/loggerLink.mjs",
39
+ "require": "./dist/links/loggerLink.js",
40
+ "default": "./dist/links/loggerLink.js"
41
+ },
42
+ "./links/splitLink": {
43
+ "import": "./dist/links/splitLink.mjs",
44
+ "require": "./dist/links/splitLink.js",
45
+ "default": "./dist/links/splitLink.js"
46
+ },
47
+ "./links/transformerLink": {
48
+ "import": "./dist/links/transformerLink.mjs",
49
+ "require": "./dist/links/transformerLink.js",
50
+ "default": "./dist/links/transformerLink.js"
51
+ },
52
+ "./links/wsLink": {
53
+ "import": "./dist/links/wsLink.mjs",
54
+ "require": "./dist/links/wsLink.js",
55
+ "default": "./dist/links/wsLink.js"
56
+ }
22
57
  },
23
- "scripts": {},
24
- "main": "dist/trpc-client.cjs.js",
25
- "module": "dist/trpc-client.esm.js",
26
- "typings": "dist/trpc-client.cjs.d.ts",
27
58
  "files": [
28
59
  "README.md",
29
60
  "dist",
30
61
  "links",
31
62
  "src"
32
63
  ],
64
+ "scripts": {
65
+ "build": "rollup -c"
66
+ },
33
67
  "dependencies": {
34
- "@babel/runtime": "^7.9.0"
68
+ "@babel/runtime": "^7.9.0",
69
+ "rxjs": "^7.0.0"
35
70
  },
36
71
  "peerDependencies": {
37
- "@trpc/server": "9.20.1"
72
+ "@trpc/server": "10.0.0-alpha.10"
38
73
  },
39
74
  "devDependencies": {
40
- "@trpc/server": "9.20.1"
75
+ "@trpc/server": "10.0.0-alpha.10"
41
76
  },
42
77
  "publishConfig": {
43
78
  "access": "public"
44
79
  },
45
- "gitHead": "fcacb235651d8ba9640af6b9752a981c520957ba"
80
+ "gitHead": "a1de7cd99bc36e26da285ed1cfdf8cb24dd5fd0b"
46
81
  }
@@ -18,58 +18,54 @@ export class TRPCClientError<TRouter extends AnyRouter>
18
18
  public readonly cause;
19
19
  public readonly shape: Maybe<inferRouterError<TRouter>>;
20
20
  public readonly data: Maybe<inferRouterError<TRouter>['data']>;
21
- /**
22
- * Fatal error - expect no more results after this error
23
- * Used for when WebSockets disconnect prematurely.
24
- */
25
- public readonly isDone: boolean;
21
+ public readonly meta;
26
22
 
27
23
  constructor(
28
24
  message: string,
29
- opts: {
25
+ opts?: {
30
26
  result: Maybe<TRPCErrorResponse<inferRouterError<TRouter>>>;
31
27
  /**
32
- * @deprecated use cause
28
+ * @deprecated use `cause`
33
29
  **/
34
30
  originalError?: Maybe<Error>;
35
31
  cause?: Maybe<Error>;
36
- isDone?: boolean;
32
+ meta?: Record<string, unknown>;
37
33
  },
38
34
  ) {
39
- const cause = opts.cause ?? opts.originalError;
35
+ const cause = opts?.cause ?? opts?.originalError;
40
36
 
41
37
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
42
38
  // @ts-ignore https://github.com/tc39/proposal-error-cause
43
39
  super(message, { cause });
44
40
 
45
- this.isDone = opts.isDone ?? false;
41
+ this.meta = opts?.meta;
46
42
 
47
43
  this.cause = this.originalError = cause;
48
- this.shape = opts.result?.error;
49
- this.data = opts.result?.error.data;
44
+ this.shape = opts?.result?.error;
45
+ this.data = opts?.result?.error.data;
50
46
  this.name = 'TRPCClientError';
51
47
 
52
48
  Object.setPrototypeOf(this, TRPCClientError.prototype);
53
49
  }
54
50
 
55
51
  public static from<TRouter extends AnyRouter>(
56
- result: Error | TRPCErrorResponse<any>,
57
- opts: { isDone?: boolean } = {},
52
+ cause: Error | TRPCErrorResponse<any>,
53
+ opts: { meta?: Record<string, unknown> } = {},
58
54
  ): TRPCClientError<TRouter> {
59
- if (!(result instanceof Error)) {
60
- return new TRPCClientError<TRouter>((result.error as any).message ?? '', {
55
+ if (!(cause instanceof Error)) {
56
+ return new TRPCClientError<TRouter>((cause.error as any).message ?? '', {
61
57
  ...opts,
62
58
  cause: null,
63
- result: result,
59
+ result: cause,
64
60
  });
65
61
  }
66
- if (result.name === 'TRPCClientError') {
67
- return result as TRPCClientError<any>;
62
+ if (cause.name === 'TRPCClientError') {
63
+ return cause as TRPCClientError<any>;
68
64
  }
69
65
 
70
- return new TRPCClientError<TRouter>(result.message, {
66
+ return new TRPCClientError<TRouter>(cause.message, {
71
67
  ...opts,
72
- cause: result,
68
+ cause,
73
69
  result: null,
74
70
  });
75
71
  }
@@ -11,8 +11,9 @@ export function createTRPCClient<TRouter extends AnyRouter>(
11
11
  return new Client<TRouter>(opts);
12
12
  }
13
13
 
14
- export type { CreateTRPCClientOptions } from './internals/TRPCClient';
15
-
16
14
  export type TRPCClient<TRouter extends AnyRouter> = Client<TRouter>;
17
15
 
18
- export type { TRPCRequestOptions } from './internals/TRPCClient';
16
+ export type {
17
+ TRPCRequestOptions,
18
+ CreateTRPCClientOptions,
19
+ } from './internals/TRPCClient';
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './createTRPCClient';
2
2
  export * from './getFetch';
3
- export * from './links/core';
3
+ export * from './links/types';
4
4
  export * from './TRPCClientError';
@@ -1,28 +1,28 @@
1
1
  import {
2
2
  AnyRouter,
3
- ClientDataTransformerOptions,
4
- DataTransformer,
5
3
  inferHandlerInput,
6
4
  inferProcedureInput,
7
5
  inferProcedureOutput,
8
6
  inferSubscriptionOutput,
9
7
  } from '@trpc/server';
10
8
  import { TRPCResult } from '@trpc/server/rpc';
11
- import { executeChain } from './executeChain';
12
- import { getAbortController } from './fetchHelpers';
9
+ import { CancelFn } from '..';
13
10
  import { getFetch } from '../getFetch';
14
- import { ObservableCallbacks, UnsubscribeFn } from './observable';
15
- import { TRPCAbortError } from './TRPCAbortError';
11
+ import { httpBatchLink } from '../links/httpBatchLink';
12
+ import { createChain } from '../links/internals/createChain';
13
+ import { transformOperationResult } from '../links/internals/transformOperationResult';
16
14
  import {
17
- CancelFn,
18
15
  HTTPHeaders,
19
- LinkRuntimeOptions,
16
+ LinkRuntime,
20
17
  OperationContext,
21
18
  OperationLink,
22
19
  TRPCLink,
23
- } from '../links/core';
24
- import { httpBatchLink } from '../links/httpBatchLink';
20
+ } from '../links/types';
21
+ import { inferObservableValue, observableToPromise } from '../rx';
22
+ import { share } from 'rxjs/operators';
23
+ import { Observer, Subscription } from 'rxjs';
25
24
  import { TRPCClientError } from '../TRPCClientError';
25
+ import { getAbortController } from './fetchHelpers';
26
26
 
27
27
  type CancellablePromise<T = unknown> = Promise<T> & {
28
28
  cancel: CancelFn;
@@ -39,8 +39,7 @@ let idCounter = 0;
39
39
  function getRequestId() {
40
40
  return ++idCounter;
41
41
  }
42
-
43
- export type CreateTRPCClientOptions<TRouter extends AnyRouter> = {
42
+ interface CreateTRPCClientBaseOptions {
44
43
  /**
45
44
  * Add ponyfill for fetch
46
45
  */
@@ -53,25 +52,26 @@ export type CreateTRPCClientOptions<TRouter extends AnyRouter> = {
53
52
  * headers to be set on outgoing requests / callback that of said headers
54
53
  */
55
54
  headers?: HTTPHeaders | (() => HTTPHeaders | Promise<HTTPHeaders>);
55
+ }
56
+
57
+ /** @internal */
58
+ export interface CreateTRPCClientWithURLOptions
59
+ extends CreateTRPCClientBaseOptions {
56
60
  /**
57
- * Data transformer
58
- * @link http://localhost:3000/docs/data-transformers
61
+ * HTTP URL of API
59
62
  **/
60
- transformer?: ClientDataTransformerOptions;
61
- } & (
62
- | {
63
- /**
64
- * HTTP URL of API
65
- **/
66
- url: string;
67
- }
68
- | {
69
- /**
70
- * @link http://localhost:3000/docs/links
71
- **/
72
- links: TRPCLink<TRouter>[];
73
- }
74
- );
63
+ url: string;
64
+ }
65
+
66
+ /** @internal */
67
+ export interface CreateTRPCClientWithLinksOptions<TRouter extends AnyRouter>
68
+ extends CreateTRPCClientBaseOptions {
69
+ /**
70
+ * @link http://localhost:3000/docs/links
71
+ **/
72
+ links: TRPCLink<TRouter>[];
73
+ }
74
+
75
75
  type TRPCType = 'subscription' | 'query' | 'mutation';
76
76
  export interface TRPCRequestOptions {
77
77
  /**
@@ -79,27 +79,20 @@ export interface TRPCRequestOptions {
79
79
  */
80
80
  context?: OperationContext;
81
81
  }
82
+
83
+ /** @internal */
84
+ export type CreateTRPCClientOptions<TRouter extends AnyRouter> =
85
+ | CreateTRPCClientWithLinksOptions<TRouter>
86
+ | CreateTRPCClientWithURLOptions;
82
87
  export class TRPCClient<TRouter extends AnyRouter> {
83
88
  private readonly links: OperationLink<TRouter>[];
84
- public readonly runtime: LinkRuntimeOptions;
89
+ public readonly runtime: LinkRuntime;
85
90
 
86
91
  constructor(opts: CreateTRPCClientOptions<TRouter>) {
87
- const transformer: DataTransformer = opts.transformer
88
- ? 'input' in opts.transformer
89
- ? {
90
- serialize: opts.transformer.input.serialize,
91
- deserialize: opts.transformer.output.deserialize,
92
- }
93
- : opts.transformer
94
- : {
95
- serialize: (data) => data,
96
- deserialize: (data) => data,
97
- };
98
-
99
92
  const _fetch = getFetch(opts?.fetch);
100
93
  const AC = getAbortController(opts?.AbortController);
101
94
 
102
- function getHeadersFn(): LinkRuntimeOptions['headers'] {
95
+ function getHeadersFn(): LinkRuntime['headers'] {
103
96
  if (opts.headers) {
104
97
  const headers = opts.headers;
105
98
  return typeof headers === 'function' ? headers : () => headers;
@@ -107,7 +100,6 @@ export class TRPCClient<TRouter extends AnyRouter> {
107
100
  return () => ({});
108
101
  }
109
102
  this.runtime = {
110
- transformer,
111
103
  AbortController: AC as any,
112
104
  fetch: _fetch,
113
105
  headers: getHeadersFn(),
@@ -135,8 +127,8 @@ export class TRPCClient<TRouter extends AnyRouter> {
135
127
  path: string;
136
128
  context?: OperationContext;
137
129
  }) {
138
- const $result = executeChain<TRouter, TInput, TOutput>({
139
- links: this.links as any,
130
+ const chain$ = createChain<TRouter, TInput, TOutput>({
131
+ links: this.links as OperationLink<any, any, any>[],
140
132
  op: {
141
133
  id: getRequestId(),
142
134
  type,
@@ -145,8 +137,7 @@ export class TRPCClient<TRouter extends AnyRouter> {
145
137
  context,
146
138
  },
147
139
  });
148
-
149
- return $result;
140
+ return chain$.pipe(share());
150
141
  }
151
142
  private requestAsPromise<TInput = unknown, TOutput = unknown>(opts: {
152
143
  type: TRPCType;
@@ -154,38 +145,29 @@ export class TRPCClient<TRouter extends AnyRouter> {
154
145
  path: string;
155
146
  context?: OperationContext;
156
147
  }): CancellablePromise<TOutput> {
157
- const $result = this.$request<TInput, TOutput>(opts);
158
-
159
- const promise = new Promise<TOutput>((resolve, reject) => {
160
- const res = $result.get();
161
- if (res?.type === 'data') {
162
- resolve(res.data);
163
- $result.done();
164
- return;
165
- }
166
- $result.subscribe({
167
- onNext: (result) => {
168
- if (result?.type !== 'data') {
169
- return;
170
- }
171
- resolve(result.data);
148
+ const req$ = this.$request<TInput, TOutput>(opts);
149
+ type TValue = inferObservableValue<typeof req$>;
150
+ const { promise, abort } = observableToPromise<TValue>(req$);
172
151
 
173
- $result.done();
174
- },
175
- onError(err) {
176
- reject(err);
177
- $result.done();
178
- },
179
- onDone() {
180
- reject(TRPCClientError.from(new TRPCAbortError()));
181
- },
182
- });
183
- }) as CancellablePromise<TOutput>;
184
- promise.cancel = () => {
185
- $result.done();
186
- };
152
+ const cancellablePromise: CancellablePromise<any> = new Promise<TOutput>(
153
+ (resolve, reject) => {
154
+ promise
155
+ .then((result) => {
156
+ const transformed = transformOperationResult(result);
157
+ if (transformed.ok) {
158
+ resolve(transformed.data as any);
159
+ return;
160
+ }
161
+ reject(transformed.error);
162
+ })
163
+ .catch((err) => {
164
+ reject(TRPCClientError.from(err));
165
+ });
166
+ },
167
+ ) as any;
168
+ cancellablePromise.cancel = abort;
187
169
 
188
- return promise;
170
+ return cancellablePromise;
189
171
  }
190
172
  public query<
191
173
  TQueries extends TRouter['_def']['queries'],
@@ -232,28 +214,32 @@ export class TRPCClient<TRouter extends AnyRouter> {
232
214
  >(
233
215
  path: TPath,
234
216
  input: TInput,
235
- opts: TRPCRequestOptions &
236
- ObservableCallbacks<TRPCResult<TOutput>, TRPCClientError<TRouter>>,
237
- ): UnsubscribeFn {
238
- const $res = this.$request<TInput, TOutput>({
217
+ opts: TRPCRequestOptions & Partial<Observer<TRPCResult<TOutput>>>,
218
+ ): Subscription {
219
+ const observable$ = this.$request<TInput, TOutput>({
239
220
  type: 'subscription',
240
221
  path,
241
222
  input,
242
223
  context: opts.context,
243
224
  });
244
- $res.subscribe({
245
- onNext(output) {
246
- if (output) {
247
- opts.onNext?.(output);
225
+ return observable$.subscribe({
226
+ next(result) {
227
+ if ('error' in result.data) {
228
+ const err = TRPCClientError.from(result.data, {
229
+ meta: result.context,
230
+ });
231
+
232
+ opts.error?.(err);
233
+ return;
248
234
  }
235
+ opts.next?.(result.data.result);
236
+ },
237
+ error(err) {
238
+ opts.error?.(err);
249
239
  },
250
- onError(err) {
251
- opts.onError?.(err);
240
+ complete() {
241
+ opts.complete?.();
252
242
  },
253
- onDone: opts.onDone,
254
243
  });
255
- return () => {
256
- $res.done();
257
- };
258
244
  }
259
245
  }
@@ -1,4 +1,4 @@
1
- import { CancelFn, PromiseAndCancel } from '../links/core';
1
+ import { CancelFn, PromiseAndCancel } from '../links/types';
2
2
 
3
3
  type BatchItem<TKey, TValue> = {
4
4
  cancelled: boolean;
@@ -0,0 +1,134 @@
1
+ import { waitFor } from '@testing-library/dom';
2
+ import { AnyRouter } from '@trpc/server';
3
+ import { OperationLink } from '..';
4
+ import { Observable } from 'rxjs';
5
+ import { dedupeLink } from './dedupeLink';
6
+ import { createChain } from './internals/createChain';
7
+
8
+ test('dedupeLink', async () => {
9
+ const endingLinkTriggered = jest.fn();
10
+ const timerTriggered = jest.fn();
11
+ const links: OperationLink<AnyRouter, any, any>[] = [
12
+ // "dedupe link"
13
+ dedupeLink()(null as any),
14
+ ({ op }) => {
15
+ return new Observable((subscribe) => {
16
+ endingLinkTriggered();
17
+ const timer = setTimeout(() => {
18
+ timerTriggered();
19
+ subscribe.next({
20
+ data: {
21
+ id: null,
22
+ result: {
23
+ type: 'data',
24
+ data: {
25
+ input: op.input,
26
+ },
27
+ },
28
+ },
29
+ });
30
+ subscribe.complete();
31
+ }, 1);
32
+ return () => clearTimeout(timer);
33
+ });
34
+ },
35
+ ];
36
+ {
37
+ const call1 = createChain<AnyRouter, unknown, unknown>({
38
+ links,
39
+ op: {
40
+ type: 'query',
41
+ id: 1,
42
+ input: 'world',
43
+ path: 'hello',
44
+ context: {},
45
+ },
46
+ });
47
+
48
+ const call2 = createChain<AnyRouter, unknown, unknown>({
49
+ links,
50
+ op: {
51
+ type: 'query',
52
+ id: 1,
53
+ input: 'world',
54
+ path: 'hello',
55
+ context: {},
56
+ },
57
+ });
58
+ const next = jest.fn();
59
+ call1.subscribe({ next });
60
+ call2.subscribe({ next });
61
+
62
+ expect(endingLinkTriggered).toHaveBeenCalledTimes(1);
63
+ await waitFor(() => {
64
+ expect(timerTriggered).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ expect(next).toHaveBeenCalledTimes(2);
68
+ }
69
+ });
70
+
71
+ test('dedupe - cancel one does not cancel the other', async () => {
72
+ const endingLinkTriggered = jest.fn();
73
+ const timerTriggered = jest.fn();
74
+ const links: OperationLink<AnyRouter, any, any>[] = [
75
+ // "dedupe link"
76
+ dedupeLink()(null as any),
77
+ ({ op }) => {
78
+ return new Observable((subscribe) => {
79
+ endingLinkTriggered();
80
+ const timer = setTimeout(() => {
81
+ timerTriggered();
82
+ subscribe.next({
83
+ data: {
84
+ id: null,
85
+ result: {
86
+ type: 'data',
87
+ data: {
88
+ input: op.input,
89
+ },
90
+ },
91
+ },
92
+ });
93
+ subscribe.complete();
94
+ }, 1);
95
+ return () => clearTimeout(timer);
96
+ });
97
+ },
98
+ ];
99
+
100
+ {
101
+ const call1 = createChain<AnyRouter, unknown, unknown>({
102
+ links,
103
+ op: {
104
+ type: 'query',
105
+ id: 1,
106
+ input: 'world',
107
+ path: 'hello',
108
+ context: {},
109
+ },
110
+ });
111
+
112
+ const call2 = createChain<AnyRouter, unknown, unknown>({
113
+ links,
114
+ op: {
115
+ type: 'query',
116
+ id: 1,
117
+ input: 'world',
118
+ path: 'hello',
119
+ context: {},
120
+ },
121
+ });
122
+ const next = jest.fn();
123
+ const call1$ = call1.subscribe({ next });
124
+ call2.subscribe({ next });
125
+ call1$.unsubscribe();
126
+
127
+ expect(endingLinkTriggered).toHaveBeenCalledTimes(1);
128
+ await waitFor(() => {
129
+ expect(timerTriggered).toHaveBeenCalledTimes(1);
130
+
131
+ expect(next).toHaveBeenCalledTimes(1);
132
+ });
133
+ }
134
+ });
@@ -0,0 +1,54 @@
1
+ import { AnyRouter } from '@trpc/server';
2
+ import { Observable } from 'rxjs';
3
+ import { share } from 'rxjs';
4
+ import { OperationResult, TRPCLink } from './types';
5
+
6
+ export function dedupeLink<
7
+ TRouter extends AnyRouter = AnyRouter,
8
+ >(): TRPCLink<TRouter> {
9
+ // initialized config
10
+ return () => {
11
+ // initialized in app
12
+ const pending: Record<string, Observable<any>> = {};
13
+ return ({ op, next }) => {
14
+ // initialized for request
15
+
16
+ if (op.type !== 'query') {
17
+ // pass through
18
+ return next(op);
19
+ }
20
+ const key = JSON.stringify([op.path, op.input]);
21
+ if (pending[key]) {
22
+ // console.log('hooking into pending', { op });
23
+ return new Observable((observer) => pending[key].subscribe(observer));
24
+ }
25
+
26
+ const shared$ = new Observable<OperationResult<TRouter, unknown>>(
27
+ (observer) => {
28
+ function reset() {
29
+ delete pending[key];
30
+ }
31
+ const next$ = next(op).subscribe({
32
+ next(v) {
33
+ observer.next(v);
34
+ },
35
+ error(e) {
36
+ reset();
37
+ observer.error(e);
38
+ },
39
+ complete() {
40
+ reset();
41
+ observer.complete();
42
+ },
43
+ });
44
+ return () => {
45
+ reset();
46
+ next$.unsubscribe();
47
+ };
48
+ },
49
+ ).pipe(share());
50
+ pending[key] = shared$;
51
+ return shared$;
52
+ };
53
+ };
54
+ }