@trpc/client 11.0.0-next.92 → 11.0.0-rc.330
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/README.md +4 -4
- package/dist/TRPCClientError.d.ts +8 -7
- package/dist/TRPCClientError.d.ts.map +1 -1
- package/dist/{TRPCClientError-e224e397.js → TRPCClientError.js} +3 -3
- package/dist/{TRPCClientError-0de4d231.mjs → TRPCClientError.mjs} +4 -4
- package/dist/bundle-analysis.json +126 -155
- package/dist/createTRPCClient.d.ts +27 -19
- package/dist/createTRPCClient.d.ts.map +1 -1
- package/dist/createTRPCClient.js +50 -0
- package/dist/createTRPCClient.mjs +45 -0
- package/dist/createTRPCUntypedClient.d.ts +3 -2
- package/dist/createTRPCUntypedClient.d.ts.map +1 -1
- package/dist/createTRPCUntypedClient.js +10 -0
- package/dist/createTRPCUntypedClient.mjs +7 -0
- package/dist/getFetch.d.ts +1 -1
- package/dist/getFetch.d.ts.map +1 -1
- package/dist/getFetch.js +17 -0
- package/dist/getFetch.mjs +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -370
- package/dist/index.mjs +9 -354
- package/dist/internals/TRPCUntypedClient.d.ts +5 -30
- package/dist/internals/TRPCUntypedClient.d.ts.map +1 -1
- package/dist/internals/TRPCUntypedClient.js +85 -0
- package/dist/internals/TRPCUntypedClient.mjs +83 -0
- package/dist/internals/dataLoader.d.ts +1 -1
- package/dist/internals/dataLoader.d.ts.map +1 -1
- package/dist/{httpBatchLink-204206a5.mjs → internals/dataLoader.js} +2 -117
- package/dist/internals/dataLoader.mjs +131 -0
- package/dist/internals/getAbortController.d.ts +2 -2
- package/dist/internals/getAbortController.d.ts.map +1 -1
- package/dist/internals/getAbortController.js +18 -0
- package/dist/internals/getAbortController.mjs +16 -0
- package/dist/internals/transformer.d.ts +42 -0
- package/dist/internals/transformer.d.ts.map +1 -0
- package/dist/internals/transformer.js +30 -0
- package/dist/internals/transformer.mjs +28 -0
- package/dist/internals/types.d.ts +4 -2
- package/dist/internals/types.d.ts.map +1 -1
- package/dist/links/HTTPBatchLinkOptions.d.ts +6 -5
- package/dist/links/HTTPBatchLinkOptions.d.ts.map +1 -1
- package/dist/links/httpBatchLink.d.ts +2 -2
- package/dist/links/httpBatchLink.d.ts.map +1 -1
- package/dist/links/httpBatchLink.js +37 -9
- package/dist/links/httpBatchLink.mjs +39 -5
- package/dist/links/httpBatchStreamLink.d.ts +6 -5
- package/dist/links/httpBatchStreamLink.d.ts.map +1 -1
- package/dist/links/httpBatchStreamLink.js +43 -0
- package/dist/links/httpBatchStreamLink.mjs +41 -0
- package/dist/links/httpFormDataLink.d.ts +1 -1
- package/dist/links/httpFormDataLink.d.ts.map +1 -1
- package/dist/links/httpFormDataLink.js +31 -0
- package/dist/links/httpFormDataLink.mjs +29 -0
- package/dist/links/httpLink.d.ts +8 -8
- package/dist/links/httpLink.d.ts.map +1 -1
- package/dist/links/httpLink.js +6 -9
- package/dist/links/httpLink.mjs +6 -7
- package/dist/links/internals/createChain.d.ts +2 -2
- package/dist/links/internals/createChain.d.ts.map +1 -1
- package/dist/{splitLink-f29e84be.js → links/internals/createChain.js} +0 -22
- package/dist/{splitLink-4c75f7be.mjs → links/internals/createChain.mjs} +1 -22
- package/dist/links/internals/createHTTPBatchLink.d.ts +6 -6
- package/dist/links/internals/createHTTPBatchLink.d.ts.map +1 -1
- package/dist/links/internals/createHTTPBatchLink.js +85 -0
- package/dist/links/internals/createHTTPBatchLink.mjs +83 -0
- package/dist/links/internals/dedupeLink.d.ts +2 -2
- package/dist/links/internals/dedupeLink.d.ts.map +1 -1
- package/dist/links/internals/getTextDecoder.d.ts +1 -1
- package/dist/links/internals/getTextDecoder.d.ts.map +1 -1
- package/dist/links/internals/getTextDecoder.js +18 -0
- package/dist/links/internals/getTextDecoder.mjs +16 -0
- package/dist/links/internals/httpUtils.d.ts +19 -11
- package/dist/links/internals/httpUtils.d.ts.map +1 -1
- package/dist/{httpUtils-c0e7bf5a.js → links/internals/httpUtils.js} +20 -39
- package/dist/{httpUtils-f58ceda1.mjs → links/internals/httpUtils.mjs} +20 -38
- package/dist/links/internals/parseJSONStream.d.ts +5 -6
- package/dist/links/internals/parseJSONStream.d.ts.map +1 -1
- package/dist/links/internals/parseJSONStream.js +118 -0
- package/dist/links/internals/parseJSONStream.mjs +115 -0
- package/dist/links/internals/retryLink.d.ts +2 -2
- package/dist/links/internals/retryLink.d.ts.map +1 -1
- package/dist/links/loggerLink.d.ts +4 -4
- package/dist/links/loggerLink.d.ts.map +1 -1
- package/dist/links/loggerLink.js +6 -4
- package/dist/links/loggerLink.mjs +6 -2
- package/dist/links/splitLink.d.ts +2 -2
- package/dist/links/splitLink.d.ts.map +1 -1
- package/dist/links/splitLink.js +23 -6
- package/dist/links/splitLink.mjs +25 -2
- package/dist/links/types.d.ts +15 -14
- package/dist/links/types.d.ts.map +1 -1
- package/dist/links/wsLink.d.ts +44 -13
- package/dist/links/wsLink.d.ts.map +1 -1
- package/dist/links/wsLink.js +64 -24
- package/dist/links/wsLink.mjs +64 -22
- package/dist/links.d.ts +10 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/unstable-internals.d.ts +2 -0
- package/dist/unstable-internals.d.ts.map +1 -0
- package/dist/unstable-internals.js +7 -0
- package/dist/unstable-internals.mjs +1 -0
- package/package.json +26 -28
- package/src/TRPCClientError.ts +24 -13
- package/src/createTRPCClient.ts +58 -51
- package/src/createTRPCUntypedClient.ts +3 -5
- package/src/getFetch.ts +1 -1
- package/src/index.ts +2 -0
- package/src/internals/TRPCUntypedClient.ts +14 -84
- package/src/internals/dataLoader.ts +1 -1
- package/src/internals/getAbortController.ts +2 -2
- package/src/internals/transformer.ts +76 -0
- package/src/internals/types.ts +8 -1
- package/src/links/HTTPBatchLinkOptions.ts +17 -15
- package/src/links/httpBatchLink.ts +9 -8
- package/src/links/httpBatchStreamLink.ts +17 -17
- package/src/links/httpFormDataLink.ts +11 -12
- package/src/links/httpLink.ts +27 -19
- package/src/links/internals/createChain.ts +6 -2
- package/src/links/internals/createHTTPBatchLink.ts +24 -16
- package/src/links/internals/dedupeLink.ts +4 -3
- package/src/links/internals/getTextDecoder.ts +1 -1
- package/src/links/internals/httpUtils.ts +42 -21
- package/src/links/internals/parseJSONStream.ts +12 -14
- package/src/links/internals/retryLink.ts +4 -3
- package/src/links/loggerLink.ts +5 -5
- package/src/links/splitLink.ts +2 -2
- package/src/links/types.ts +27 -22
- package/src/links/wsLink.ts +124 -36
- package/src/links.ts +14 -0
- package/src/unstable-internals.ts +1 -0
- package/unstable-internals/index.d.ts +1 -0
- package/unstable-internals/index.js +1 -0
- package/dist/TRPCClientError-23c8aa93.js +0 -61
- package/dist/httpBatchLink-64fceaac.js +0 -251
- package/dist/httpBatchLink-92dab48e.js +0 -247
- package/dist/httpUtils-35e50476.js +0 -145
- package/dist/internals/isObject.d.ts +0 -2
- package/dist/internals/isObject.d.ts.map +0 -1
- package/dist/internals/retryDelay.d.ts +0 -2
- package/dist/internals/retryDelay.d.ts.map +0 -1
- package/dist/links/index.d.ts +0 -10
- package/dist/links/index.d.ts.map +0 -1
- package/dist/shared/index.d.ts +0 -2
- package/dist/shared/index.d.ts.map +0 -1
- package/dist/shared/index.js +0 -9
- package/dist/shared/index.mjs +0 -1
- package/dist/shared/transformResult.d.ts +0 -34
- package/dist/shared/transformResult.d.ts.map +0 -1
- package/dist/splitLink-0df96fdc.js +0 -41
- package/dist/transformResult-ace864b8.mjs +0 -58
- package/dist/transformResult-c1422cb5.js +0 -60
- package/dist/transformResult-dfce8f15.js +0 -61
- package/shared/index.d.ts +0 -1
- package/shared/index.js +0 -1
- package/src/internals/isObject.ts +0 -4
- package/src/internals/retryDelay.ts +0 -3
- package/src/links/index.ts +0 -14
- package/src/shared/index.ts +0 -1
- package/src/shared/transformResult.ts +0 -79
package/src/links/wsLink.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Observer, UnsubscribeFn } from '@trpc/server/observable';
|
|
2
|
+
import { observable } from '@trpc/server/observable';
|
|
3
|
+
import type {
|
|
2
4
|
AnyRouter,
|
|
5
|
+
inferClientTypes,
|
|
3
6
|
inferRouterError,
|
|
4
7
|
MaybePromise,
|
|
5
8
|
ProcedureType,
|
|
6
|
-
} from '@trpc/server';
|
|
7
|
-
import { observable, Observer, UnsubscribeFn } from '@trpc/server/observable';
|
|
8
|
-
import {
|
|
9
9
|
TRPCClientIncomingMessage,
|
|
10
10
|
TRPCClientIncomingRequest,
|
|
11
11
|
TRPCClientOutgoingMessage,
|
|
12
12
|
TRPCRequestMessage,
|
|
13
13
|
TRPCResponseMessage,
|
|
14
|
-
} from '@trpc/server/
|
|
15
|
-
import {
|
|
16
|
-
import { transformResult } from '../shared/transformResult';
|
|
14
|
+
} from '@trpc/server/unstable-core-do-not-import';
|
|
15
|
+
import { transformResult } from '@trpc/server/unstable-core-do-not-import';
|
|
17
16
|
import { TRPCClientError } from '../TRPCClientError';
|
|
18
|
-
import {
|
|
17
|
+
import type { TransformerOptions } from '../unstable-internals';
|
|
18
|
+
import { getTransformer } from '../unstable-internals';
|
|
19
|
+
import type { Operation, TRPCLink } from './types';
|
|
19
20
|
|
|
20
21
|
const run = <TResult>(fn: () => TResult): TResult => fn();
|
|
21
22
|
|
|
@@ -29,22 +30,66 @@ type WSCallbackObserver<TRouter extends AnyRouter, TOutput> = Observer<
|
|
|
29
30
|
TRPCClientError<TRouter>
|
|
30
31
|
>;
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
const exponentialBackoff = (attemptIndex: number) =>
|
|
34
|
+
attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);
|
|
35
|
+
|
|
36
|
+
export interface WebSocketClientOptions {
|
|
37
|
+
/**
|
|
38
|
+
* The URL to connect to (can be a function that returns a URL)
|
|
39
|
+
*/
|
|
33
40
|
url: string | (() => MaybePromise<string>);
|
|
41
|
+
/**
|
|
42
|
+
* Ponyfill which WebSocket implementation to use
|
|
43
|
+
*/
|
|
34
44
|
WebSocket?: typeof WebSocket;
|
|
35
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The number of milliseconds before a reconnect is attempted.
|
|
47
|
+
* @default {@link exponentialBackoff}
|
|
48
|
+
*/
|
|
49
|
+
retryDelayMs?: typeof exponentialBackoff;
|
|
50
|
+
/**
|
|
51
|
+
* Triggered when a WebSocket connection is established
|
|
52
|
+
*/
|
|
36
53
|
onOpen?: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* Triggered when a WebSocket connection is closed
|
|
56
|
+
*/
|
|
37
57
|
onClose?: (cause?: { code?: number }) => void;
|
|
38
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Lazy mode will close the WebSocket automatically after a period of inactivity (no messages sent or received and no pending requests)
|
|
60
|
+
*/
|
|
61
|
+
lazy?: {
|
|
62
|
+
/**
|
|
63
|
+
* Enable lazy mode
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Close the WebSocket after this many milliseconds
|
|
69
|
+
* @default 0
|
|
70
|
+
*/
|
|
71
|
+
closeMs: number;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
39
74
|
|
|
75
|
+
type LazyOptions = Required<NonNullable<WebSocketClientOptions['lazy']>>;
|
|
76
|
+
const lazyDefaults: LazyOptions = {
|
|
77
|
+
enabled: false,
|
|
78
|
+
closeMs: 0,
|
|
79
|
+
};
|
|
40
80
|
export function createWSClient(opts: WebSocketClientOptions) {
|
|
41
81
|
const {
|
|
42
82
|
url,
|
|
43
83
|
WebSocket: WebSocketImpl = WebSocket,
|
|
44
|
-
retryDelayMs: retryDelayFn =
|
|
84
|
+
retryDelayMs: retryDelayFn = exponentialBackoff,
|
|
45
85
|
onOpen,
|
|
46
86
|
onClose,
|
|
47
87
|
} = opts;
|
|
88
|
+
const lazyOpts: LazyOptions = {
|
|
89
|
+
...lazyDefaults,
|
|
90
|
+
...opts.lazy,
|
|
91
|
+
};
|
|
92
|
+
|
|
48
93
|
/* istanbul ignore next -- @preserve */
|
|
49
94
|
if (!WebSocketImpl) {
|
|
50
95
|
throw new Error(
|
|
@@ -73,11 +118,11 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
73
118
|
let connectAttempt = 0;
|
|
74
119
|
let connectTimer: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
75
120
|
let connectionIndex = 0;
|
|
76
|
-
let
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
121
|
+
let lazyDisconnectTimer: ReturnType<typeof setTimeout> | undefined =
|
|
122
|
+
undefined;
|
|
123
|
+
let activeConnection: null | Connection = lazyOpts.enabled
|
|
124
|
+
? null
|
|
125
|
+
: createConnection();
|
|
81
126
|
|
|
82
127
|
type Connection = {
|
|
83
128
|
id: number;
|
|
@@ -100,6 +145,10 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
100
145
|
* tries to send the list of messages
|
|
101
146
|
*/
|
|
102
147
|
function dispatch() {
|
|
148
|
+
if (!activeConnection) {
|
|
149
|
+
activeConnection = createConnection();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
103
152
|
// using a timeout to batch messages
|
|
104
153
|
setTimeout(() => {
|
|
105
154
|
if (activeConnection?.state !== 'open') {
|
|
@@ -119,16 +168,32 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
119
168
|
}
|
|
120
169
|
// clear
|
|
121
170
|
outgoing = [];
|
|
171
|
+
|
|
172
|
+
startLazyDisconnectTimer();
|
|
122
173
|
});
|
|
123
174
|
}
|
|
124
|
-
function tryReconnect() {
|
|
125
|
-
if (!!connectTimer
|
|
175
|
+
function tryReconnect(conn: Connection) {
|
|
176
|
+
if (!!connectTimer) {
|
|
126
177
|
return;
|
|
127
178
|
}
|
|
179
|
+
|
|
180
|
+
conn.state = 'connecting';
|
|
128
181
|
const timeout = retryDelayFn(connectAttempt++);
|
|
129
182
|
reconnectInMs(timeout);
|
|
130
183
|
}
|
|
184
|
+
function hasPendingRequests(conn?: Connection) {
|
|
185
|
+
const requests = Object.values(pendingRequests);
|
|
186
|
+
if (!conn) {
|
|
187
|
+
return requests.length > 0;
|
|
188
|
+
}
|
|
189
|
+
return requests.some((req) => req.connection === conn);
|
|
190
|
+
}
|
|
191
|
+
|
|
131
192
|
function reconnect() {
|
|
193
|
+
if (lazyOpts.enabled && !hasPendingRequests()) {
|
|
194
|
+
// Skip reconnecting if there are pending requests and we're in lazy mode
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
132
197
|
const oldConnection = activeConnection;
|
|
133
198
|
activeConnection = createConnection();
|
|
134
199
|
oldConnection && closeIfNoPending(oldConnection);
|
|
@@ -141,11 +206,8 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
141
206
|
}
|
|
142
207
|
|
|
143
208
|
function closeIfNoPending(conn: Connection) {
|
|
144
|
-
// disconnect as soon as there are are no pending
|
|
145
|
-
|
|
146
|
-
(p) => p.connection === conn,
|
|
147
|
-
);
|
|
148
|
-
if (!hasPendingRequests) {
|
|
209
|
+
// disconnect as soon as there are are no pending requests
|
|
210
|
+
if (!hasPendingRequests(conn)) {
|
|
149
211
|
conn.ws?.close();
|
|
150
212
|
}
|
|
151
213
|
}
|
|
@@ -156,16 +218,36 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
156
218
|
request(req.op, req.callbacks);
|
|
157
219
|
}
|
|
158
220
|
|
|
221
|
+
const startLazyDisconnectTimer = () => {
|
|
222
|
+
if (!lazyOpts.enabled) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
clearTimeout(lazyDisconnectTimer);
|
|
227
|
+
lazyDisconnectTimer = setTimeout(() => {
|
|
228
|
+
if (!activeConnection) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!hasPendingRequests(activeConnection)) {
|
|
233
|
+
activeConnection.ws?.close();
|
|
234
|
+
activeConnection = null;
|
|
235
|
+
}
|
|
236
|
+
}, lazyOpts.closeMs);
|
|
237
|
+
};
|
|
238
|
+
|
|
159
239
|
function createConnection(): Connection {
|
|
160
240
|
const self: Connection = {
|
|
161
241
|
id: ++connectionIndex,
|
|
162
242
|
state: 'connecting',
|
|
163
243
|
} as Connection;
|
|
164
244
|
|
|
245
|
+
clearTimeout(lazyDisconnectTimer);
|
|
246
|
+
|
|
165
247
|
const onError = () => {
|
|
166
248
|
self.state = 'closed';
|
|
167
249
|
if (self === activeConnection) {
|
|
168
|
-
tryReconnect();
|
|
250
|
+
tryReconnect(self);
|
|
169
251
|
}
|
|
170
252
|
};
|
|
171
253
|
run(async () => {
|
|
@@ -192,6 +274,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
192
274
|
if (self !== activeConnection) {
|
|
193
275
|
return;
|
|
194
276
|
}
|
|
277
|
+
|
|
195
278
|
if (req.method === 'reconnect') {
|
|
196
279
|
reconnect();
|
|
197
280
|
// notify subscribers
|
|
@@ -226,6 +309,8 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
226
309
|
}
|
|
227
310
|
};
|
|
228
311
|
ws.addEventListener('message', ({ data }) => {
|
|
312
|
+
startLazyDisconnectTimer();
|
|
313
|
+
|
|
229
314
|
const msg = JSON.parse(data) as TRPCClientIncomingMessage;
|
|
230
315
|
|
|
231
316
|
if ('method' in msg) {
|
|
@@ -247,7 +332,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
247
332
|
|
|
248
333
|
if (activeConnection === self) {
|
|
249
334
|
// connection might have been replaced already
|
|
250
|
-
tryReconnect();
|
|
335
|
+
tryReconnect(self);
|
|
251
336
|
}
|
|
252
337
|
|
|
253
338
|
for (const [key, req] of Object.entries(pendingRequests)) {
|
|
@@ -299,6 +384,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
299
384
|
|
|
300
385
|
// enqueue message
|
|
301
386
|
outgoing.push(envelope);
|
|
387
|
+
|
|
302
388
|
dispatch();
|
|
303
389
|
|
|
304
390
|
return () => {
|
|
@@ -314,11 +400,12 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
314
400
|
});
|
|
315
401
|
dispatch();
|
|
316
402
|
}
|
|
403
|
+
startLazyDisconnectTimer();
|
|
317
404
|
};
|
|
318
405
|
}
|
|
319
406
|
return {
|
|
320
407
|
close: () => {
|
|
321
|
-
|
|
408
|
+
connectAttempt = 0;
|
|
322
409
|
|
|
323
410
|
for (const req of Object.values(pendingRequests)) {
|
|
324
411
|
if (req.type === 'subscription') {
|
|
@@ -334,20 +421,20 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
334
421
|
}
|
|
335
422
|
activeConnection && closeIfNoPending(activeConnection);
|
|
336
423
|
clearTimeout(connectTimer);
|
|
337
|
-
|
|
338
424
|
connectTimer = undefined;
|
|
425
|
+
activeConnection = null;
|
|
339
426
|
},
|
|
340
427
|
request,
|
|
341
|
-
|
|
428
|
+
get connection() {
|
|
342
429
|
return activeConnection;
|
|
343
430
|
},
|
|
344
431
|
};
|
|
345
432
|
}
|
|
346
433
|
export type TRPCWebSocketClient = ReturnType<typeof createWSClient>;
|
|
347
434
|
|
|
348
|
-
export
|
|
435
|
+
export type WebSocketLinkOptions<TRouter extends AnyRouter> = {
|
|
349
436
|
client: TRPCWebSocketClient;
|
|
350
|
-
}
|
|
437
|
+
} & TransformerOptions<inferClientTypes<TRouter>>;
|
|
351
438
|
class TRPCWebSocketClosedError extends Error {
|
|
352
439
|
constructor(message: string) {
|
|
353
440
|
super(message);
|
|
@@ -357,18 +444,19 @@ class TRPCWebSocketClosedError extends Error {
|
|
|
357
444
|
}
|
|
358
445
|
|
|
359
446
|
/**
|
|
360
|
-
* @
|
|
447
|
+
* @link https://trpc.io/docs/v11/client/links/wsLink
|
|
361
448
|
*/
|
|
362
449
|
export function wsLink<TRouter extends AnyRouter>(
|
|
363
|
-
opts: WebSocketLinkOptions
|
|
450
|
+
opts: WebSocketLinkOptions<TRouter>,
|
|
364
451
|
): TRPCLink<TRouter> {
|
|
365
|
-
|
|
452
|
+
const transformer = getTransformer(opts.transformer);
|
|
453
|
+
return () => {
|
|
366
454
|
const { client } = opts;
|
|
367
455
|
return ({ op }) => {
|
|
368
456
|
return observable((observer) => {
|
|
369
457
|
const { type, path, id, context } = op;
|
|
370
458
|
|
|
371
|
-
const input =
|
|
459
|
+
const input = transformer.input.serialize(op.input);
|
|
372
460
|
|
|
373
461
|
const unsub = client.request(
|
|
374
462
|
{ type, path, input, id, context },
|
|
@@ -381,7 +469,7 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
381
469
|
observer.complete();
|
|
382
470
|
},
|
|
383
471
|
next(message) {
|
|
384
|
-
const transformed = transformResult(message,
|
|
472
|
+
const transformed = transformResult(message, transformer.output);
|
|
385
473
|
|
|
386
474
|
if (!transformed.ok) {
|
|
387
475
|
observer.error(TRPCClientError.from(transformed.error));
|
package/src/links.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './links/types';
|
|
2
|
+
|
|
3
|
+
export * from './links/HTTPBatchLinkOptions';
|
|
4
|
+
export * from './links/httpBatchLink';
|
|
5
|
+
export * from './links/httpBatchStreamLink';
|
|
6
|
+
export * from './links/httpLink';
|
|
7
|
+
export * from './links/loggerLink';
|
|
8
|
+
export * from './links/splitLink';
|
|
9
|
+
export * from './links/wsLink';
|
|
10
|
+
export * from './links/httpFormDataLink';
|
|
11
|
+
|
|
12
|
+
// These are not public (yet) as we get this functionality from tanstack query
|
|
13
|
+
// export * from './links/internals/retryLink';
|
|
14
|
+
// export * from './links/internals/dedupeLink';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './internals/transformer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../dist/unstable-internals';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../dist/unstable-internals');
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { i as isObject } from './transformResult-c1422cb5.js';
|
|
2
|
-
|
|
3
|
-
function isTRPCClientError(cause) {
|
|
4
|
-
return (cause instanceof TRPCClientError ||
|
|
5
|
-
/**
|
|
6
|
-
* @deprecated
|
|
7
|
-
* Delete in next major
|
|
8
|
-
*/
|
|
9
|
-
(cause instanceof Error && cause.name === 'TRPCClientError'));
|
|
10
|
-
}
|
|
11
|
-
function isTRPCErrorResponse(obj) {
|
|
12
|
-
return (isObject(obj) &&
|
|
13
|
-
isObject(obj.error) &&
|
|
14
|
-
typeof obj.error.code === 'number' &&
|
|
15
|
-
typeof obj.error.message === 'string');
|
|
16
|
-
}
|
|
17
|
-
class TRPCClientError extends Error {
|
|
18
|
-
constructor(message, opts) {
|
|
19
|
-
const cause = opts?.cause;
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
21
|
-
// @ts-ignore https://github.com/tc39/proposal-error-cause
|
|
22
|
-
super(message, { cause });
|
|
23
|
-
this.meta = opts?.meta;
|
|
24
|
-
this.cause = cause;
|
|
25
|
-
this.shape = opts?.result?.error;
|
|
26
|
-
this.data = opts?.result?.error.data;
|
|
27
|
-
this.name = 'TRPCClientError';
|
|
28
|
-
Object.setPrototypeOf(this, TRPCClientError.prototype);
|
|
29
|
-
}
|
|
30
|
-
static from(_cause, opts = {}) {
|
|
31
|
-
const cause = _cause;
|
|
32
|
-
if (isTRPCClientError(cause)) {
|
|
33
|
-
if (opts.meta) {
|
|
34
|
-
// Decorate with meta error data
|
|
35
|
-
cause.meta = {
|
|
36
|
-
...cause.meta,
|
|
37
|
-
...opts.meta,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return cause;
|
|
41
|
-
}
|
|
42
|
-
if (isTRPCErrorResponse(cause)) {
|
|
43
|
-
return new TRPCClientError(cause.error.message, {
|
|
44
|
-
...opts,
|
|
45
|
-
result: cause,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
if (!(cause instanceof Error)) {
|
|
49
|
-
return new TRPCClientError('Unknown error', {
|
|
50
|
-
...opts,
|
|
51
|
-
cause: cause,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return new TRPCClientError(cause.message, {
|
|
55
|
-
...opts,
|
|
56
|
-
cause,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export { TRPCClientError as T };
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var observable = require('@trpc/server/observable');
|
|
4
|
-
var transformResult = require('./transformResult-dfce8f15.js');
|
|
5
|
-
var TRPCClientError = require('./TRPCClientError-e224e397.js');
|
|
6
|
-
var httpUtils = require('./httpUtils-c0e7bf5a.js');
|
|
7
|
-
|
|
8
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /**
|
|
9
|
-
* A function that should never be called unless we messed something up.
|
|
10
|
-
*/ const throwFatalError = ()=>{
|
|
11
|
-
throw new Error('Something went wrong. Please submit an issue at https://github.com/trpc/trpc/issues/new');
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Dataloader that's very inspired by https://github.com/graphql/dataloader
|
|
15
|
-
* Less configuration, no caching, and allows you to cancel requests
|
|
16
|
-
* When cancelling a single fetch the whole batch will be cancelled only when _all_ items are cancelled
|
|
17
|
-
*/ function dataLoader(batchLoader) {
|
|
18
|
-
let pendingItems = null;
|
|
19
|
-
let dispatchTimer = null;
|
|
20
|
-
const destroyTimerAndPendingItems = ()=>{
|
|
21
|
-
clearTimeout(dispatchTimer);
|
|
22
|
-
dispatchTimer = null;
|
|
23
|
-
pendingItems = null;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Iterate through the items and split them into groups based on the `batchLoader`'s validate function
|
|
27
|
-
*/ function groupItems(items) {
|
|
28
|
-
const groupedItems = [
|
|
29
|
-
[]
|
|
30
|
-
];
|
|
31
|
-
let index = 0;
|
|
32
|
-
while(true){
|
|
33
|
-
const item = items[index];
|
|
34
|
-
if (!item) {
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
const lastGroup = groupedItems[groupedItems.length - 1];
|
|
38
|
-
if (item.aborted) {
|
|
39
|
-
// Item was aborted before it was dispatched
|
|
40
|
-
item.reject?.(new Error('Aborted'));
|
|
41
|
-
index++;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
const isValid = batchLoader.validate(lastGroup.concat(item).map((it)=>it.key));
|
|
45
|
-
if (isValid) {
|
|
46
|
-
lastGroup.push(item);
|
|
47
|
-
index++;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (lastGroup.length === 0) {
|
|
51
|
-
item.reject?.(new Error('Input is too big for a single dispatch'));
|
|
52
|
-
index++;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
// Create new group, next iteration will try to add the item to that
|
|
56
|
-
groupedItems.push([]);
|
|
57
|
-
}
|
|
58
|
-
return groupedItems;
|
|
59
|
-
}
|
|
60
|
-
function dispatch() {
|
|
61
|
-
const groupedItems = groupItems(pendingItems);
|
|
62
|
-
destroyTimerAndPendingItems();
|
|
63
|
-
// Create batches for each group of items
|
|
64
|
-
for (const items of groupedItems){
|
|
65
|
-
if (!items.length) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const batch = {
|
|
69
|
-
items,
|
|
70
|
-
cancel: throwFatalError
|
|
71
|
-
};
|
|
72
|
-
for (const item of items){
|
|
73
|
-
item.batch = batch;
|
|
74
|
-
}
|
|
75
|
-
const unitResolver = (index, value)=>{
|
|
76
|
-
const item = batch.items[index];
|
|
77
|
-
item.resolve?.(value);
|
|
78
|
-
item.batch = null;
|
|
79
|
-
item.reject = null;
|
|
80
|
-
item.resolve = null;
|
|
81
|
-
};
|
|
82
|
-
const { promise , cancel } = batchLoader.fetch(batch.items.map((_item)=>_item.key), unitResolver);
|
|
83
|
-
batch.cancel = cancel;
|
|
84
|
-
promise.then((result)=>{
|
|
85
|
-
for(let i = 0; i < result.length; i++){
|
|
86
|
-
const value = result[i];
|
|
87
|
-
unitResolver(i, value);
|
|
88
|
-
}
|
|
89
|
-
for (const item of batch.items){
|
|
90
|
-
item.reject?.(new Error('Missing result'));
|
|
91
|
-
item.batch = null;
|
|
92
|
-
}
|
|
93
|
-
}).catch((cause)=>{
|
|
94
|
-
for (const item of batch.items){
|
|
95
|
-
item.reject?.(cause);
|
|
96
|
-
item.batch = null;
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
function load(key) {
|
|
102
|
-
const item = {
|
|
103
|
-
aborted: false,
|
|
104
|
-
key,
|
|
105
|
-
batch: null,
|
|
106
|
-
resolve: throwFatalError,
|
|
107
|
-
reject: throwFatalError
|
|
108
|
-
};
|
|
109
|
-
const promise = new Promise((resolve, reject)=>{
|
|
110
|
-
item.reject = reject;
|
|
111
|
-
item.resolve = resolve;
|
|
112
|
-
if (!pendingItems) {
|
|
113
|
-
pendingItems = [];
|
|
114
|
-
}
|
|
115
|
-
pendingItems.push(item);
|
|
116
|
-
});
|
|
117
|
-
if (!dispatchTimer) {
|
|
118
|
-
dispatchTimer = setTimeout(dispatch);
|
|
119
|
-
}
|
|
120
|
-
const cancel = ()=>{
|
|
121
|
-
item.aborted = true;
|
|
122
|
-
if (item.batch?.items.every((item)=>item.aborted)) {
|
|
123
|
-
// All items in the batch have been cancelled
|
|
124
|
-
item.batch.cancel();
|
|
125
|
-
item.batch = null;
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
return {
|
|
129
|
-
promise,
|
|
130
|
-
cancel
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
load
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* @internal
|
|
140
|
-
*/ function createHTTPBatchLink(requester) {
|
|
141
|
-
return function httpBatchLink(opts) {
|
|
142
|
-
const resolvedOpts = httpUtils.resolveHTTPLinkOptions(opts);
|
|
143
|
-
const maxURLLength = opts.maxURLLength ?? Infinity;
|
|
144
|
-
// initialized config
|
|
145
|
-
return (runtime)=>{
|
|
146
|
-
const batchLoader = (type)=>{
|
|
147
|
-
const validate = (batchOps)=>{
|
|
148
|
-
if (maxURLLength === Infinity) {
|
|
149
|
-
// escape hatch for quick calcs
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
const path = batchOps.map((op)=>op.path).join(',');
|
|
153
|
-
const inputs = batchOps.map((op)=>op.input);
|
|
154
|
-
const url = httpUtils.getUrl({
|
|
155
|
-
...resolvedOpts,
|
|
156
|
-
runtime,
|
|
157
|
-
type,
|
|
158
|
-
path,
|
|
159
|
-
inputs
|
|
160
|
-
});
|
|
161
|
-
return url.length <= maxURLLength;
|
|
162
|
-
};
|
|
163
|
-
const fetch = requester({
|
|
164
|
-
...resolvedOpts,
|
|
165
|
-
runtime,
|
|
166
|
-
type,
|
|
167
|
-
opts
|
|
168
|
-
});
|
|
169
|
-
return {
|
|
170
|
-
validate,
|
|
171
|
-
fetch
|
|
172
|
-
};
|
|
173
|
-
};
|
|
174
|
-
const query = dataLoader(batchLoader('query'));
|
|
175
|
-
const mutation = dataLoader(batchLoader('mutation'));
|
|
176
|
-
const subscription = dataLoader(batchLoader('subscription'));
|
|
177
|
-
const loaders = {
|
|
178
|
-
query,
|
|
179
|
-
subscription,
|
|
180
|
-
mutation
|
|
181
|
-
};
|
|
182
|
-
return ({ op })=>{
|
|
183
|
-
return observable.observable((observer)=>{
|
|
184
|
-
const loader = loaders[op.type];
|
|
185
|
-
const { promise , cancel } = loader.load(op);
|
|
186
|
-
let _res = undefined;
|
|
187
|
-
promise.then((res)=>{
|
|
188
|
-
_res = res;
|
|
189
|
-
const transformed = transformResult.transformResult(res.json, runtime);
|
|
190
|
-
if (!transformed.ok) {
|
|
191
|
-
observer.error(TRPCClientError.TRPCClientError.from(transformed.error, {
|
|
192
|
-
meta: res.meta
|
|
193
|
-
}));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
observer.next({
|
|
197
|
-
context: res.meta,
|
|
198
|
-
result: transformed.result
|
|
199
|
-
});
|
|
200
|
-
observer.complete();
|
|
201
|
-
}).catch((err)=>{
|
|
202
|
-
observer.error(TRPCClientError.TRPCClientError.from(err, {
|
|
203
|
-
meta: _res?.meta
|
|
204
|
-
}));
|
|
205
|
-
});
|
|
206
|
-
return ()=>{
|
|
207
|
-
cancel();
|
|
208
|
-
};
|
|
209
|
-
});
|
|
210
|
-
};
|
|
211
|
-
};
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const batchRequester = (requesterOpts)=>{
|
|
216
|
-
return (batchOps)=>{
|
|
217
|
-
const path = batchOps.map((op)=>op.path).join(',');
|
|
218
|
-
const inputs = batchOps.map((op)=>op.input);
|
|
219
|
-
const { promise , cancel } = httpUtils.jsonHttpRequester({
|
|
220
|
-
...requesterOpts,
|
|
221
|
-
path,
|
|
222
|
-
inputs,
|
|
223
|
-
headers () {
|
|
224
|
-
if (!requesterOpts.opts.headers) {
|
|
225
|
-
return {};
|
|
226
|
-
}
|
|
227
|
-
if (typeof requesterOpts.opts.headers === 'function') {
|
|
228
|
-
return requesterOpts.opts.headers({
|
|
229
|
-
opList: batchOps
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
return requesterOpts.opts.headers;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
return {
|
|
236
|
-
promise: promise.then((res)=>{
|
|
237
|
-
const resJSON = Array.isArray(res.json) ? res.json : batchOps.map(()=>res.json);
|
|
238
|
-
const result = resJSON.map((item)=>({
|
|
239
|
-
meta: res.meta,
|
|
240
|
-
json: item
|
|
241
|
-
}));
|
|
242
|
-
return result;
|
|
243
|
-
}),
|
|
244
|
-
cancel
|
|
245
|
-
};
|
|
246
|
-
};
|
|
247
|
-
};
|
|
248
|
-
const httpBatchLink = createHTTPBatchLink(batchRequester);
|
|
249
|
-
|
|
250
|
-
exports.createHTTPBatchLink = createHTTPBatchLink;
|
|
251
|
-
exports.httpBatchLink = httpBatchLink;
|