@trpc/client 11.0.0-alpha-tmp-issues-5851-take-two.496 → 11.0.0-alpha-tmp-subscription-connection-state.485
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/dist/bundle-analysis.json +80 -63
- package/dist/index.js +6 -4
- package/dist/index.mjs +2 -1
- package/dist/internals/TRPCUntypedClient.d.ts +2 -1
- package/dist/internals/TRPCUntypedClient.d.ts.map +1 -1
- package/dist/internals/TRPCUntypedClient.js +2 -0
- package/dist/internals/TRPCUntypedClient.mjs +2 -0
- package/dist/links/httpSubscriptionLink.d.ts +3 -2
- package/dist/links/httpSubscriptionLink.d.ts.map +1 -1
- package/dist/links/httpSubscriptionLink.js +53 -3
- package/dist/links/httpSubscriptionLink.mjs +53 -3
- package/dist/links/internals/httpUtils.d.ts.map +1 -1
- package/dist/links/internals/httpUtils.js +28 -1
- package/dist/links/internals/httpUtils.mjs +28 -1
- package/dist/links/internals/urlWithConnectionParams.d.ts +1 -2
- package/dist/links/internals/urlWithConnectionParams.d.ts.map +1 -1
- package/dist/links/types.d.ts +23 -3
- package/dist/links/types.d.ts.map +1 -1
- package/dist/links/types.js +7 -0
- package/dist/links/types.mjs +5 -0
- package/dist/links/wsLink.d.ts +5 -3
- package/dist/links/wsLink.d.ts.map +1 -1
- package/dist/links/wsLink.js +86 -17
- package/dist/links/wsLink.mjs +86 -17
- package/package.json +4 -4
- package/src/internals/TRPCUntypedClient.ts +4 -0
- package/src/links/httpSubscriptionLink.ts +70 -5
- package/src/links/internals/httpUtils.ts +35 -1
- package/src/links/internals/urlWithConnectionParams.ts +1 -1
- package/src/links/types.ts +48 -2
- package/src/links/wsLink.ts +108 -21
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { TRPCClientError } from '../TRPCClientError';
|
|
13
13
|
import { getTransformer, type TransformerOptions } from '../unstable-internals';
|
|
14
14
|
import { getUrl } from './internals/httpUtils';
|
|
15
|
+
import type { CallbackOrValue } from './internals/urlWithConnectionParams';
|
|
15
16
|
import {
|
|
16
17
|
resultOf,
|
|
17
18
|
type UrlOptionsWithConnectionParams,
|
|
@@ -35,9 +36,9 @@ async function urlWithConnectionParams(
|
|
|
35
36
|
|
|
36
37
|
type HTTPSubscriptionLinkOptions<TRoot extends AnyClientTypes> = {
|
|
37
38
|
/**
|
|
38
|
-
* EventSource options
|
|
39
|
+
* EventSource options or a callback that returns them
|
|
39
40
|
*/
|
|
40
|
-
eventSourceOptions?: EventSourceInit
|
|
41
|
+
eventSourceOptions?: CallbackOrValue<EventSourceInit>;
|
|
41
42
|
} & TransformerOptions<TRoot> &
|
|
42
43
|
UrlOptionsWithConnectionParams;
|
|
43
44
|
|
|
@@ -50,6 +51,7 @@ export function unstable_httpSubscriptionLink<
|
|
|
50
51
|
opts: HTTPSubscriptionLinkOptions<inferClientTypes<TInferrable>>,
|
|
51
52
|
): TRPCLink<TInferrable> {
|
|
52
53
|
const transformer = getTransformer(opts.transformer);
|
|
54
|
+
|
|
53
55
|
return () => {
|
|
54
56
|
return ({ op }) => {
|
|
55
57
|
return observable((observer) => {
|
|
@@ -72,12 +74,21 @@ export function unstable_httpSubscriptionLink<
|
|
|
72
74
|
signal: null,
|
|
73
75
|
});
|
|
74
76
|
|
|
77
|
+
const eventSourceOptions = await resultOf(opts.eventSourceOptions);
|
|
75
78
|
/* istanbul ignore if -- @preserve */
|
|
76
79
|
if (unsubscribed) {
|
|
77
80
|
// already unsubscribed - rare race condition
|
|
78
81
|
return;
|
|
79
82
|
}
|
|
80
|
-
eventSource = new EventSource(url,
|
|
83
|
+
eventSource = new EventSource(url, eventSourceOptions);
|
|
84
|
+
observer.next({
|
|
85
|
+
result: {
|
|
86
|
+
type: 'state',
|
|
87
|
+
state: 'connecting',
|
|
88
|
+
data: null,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
81
92
|
const onStarted = () => {
|
|
82
93
|
observer.next({
|
|
83
94
|
result: {
|
|
@@ -93,9 +104,39 @@ export function unstable_httpSubscriptionLink<
|
|
|
93
104
|
};
|
|
94
105
|
// console.log('starting', new Date());
|
|
95
106
|
eventSource.addEventListener('open', onStarted);
|
|
107
|
+
|
|
108
|
+
eventSource.addEventListener('open', () => {
|
|
109
|
+
observer.next({
|
|
110
|
+
result: {
|
|
111
|
+
type: 'state',
|
|
112
|
+
state: 'pending',
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
eventSource.addEventListener('error', (event) => {
|
|
118
|
+
// sseStreamConsumer handles this already
|
|
119
|
+
if (eventSource?.readyState === EventSource.CLOSED) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const error =
|
|
124
|
+
event instanceof ErrorEvent
|
|
125
|
+
? TRPCClientError.from(event.error)
|
|
126
|
+
: TRPCClientError.from(new Error(`Unknown EventSource error`));
|
|
127
|
+
|
|
128
|
+
observer.next({
|
|
129
|
+
result: {
|
|
130
|
+
type: 'state',
|
|
131
|
+
state: 'connecting',
|
|
132
|
+
data: error,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
96
137
|
const iterable = sseStreamConsumer<Partial<SSEMessage>>({
|
|
97
138
|
from: eventSource,
|
|
98
|
-
deserialize: transformer.
|
|
139
|
+
deserialize: transformer.output.deserialize,
|
|
99
140
|
});
|
|
100
141
|
|
|
101
142
|
for await (const chunk of iterable) {
|
|
@@ -114,12 +155,36 @@ export function unstable_httpSubscriptionLink<
|
|
|
114
155
|
},
|
|
115
156
|
});
|
|
116
157
|
observer.complete();
|
|
158
|
+
|
|
159
|
+
observer.next({
|
|
160
|
+
result: {
|
|
161
|
+
type: 'state',
|
|
162
|
+
state: 'idle',
|
|
163
|
+
},
|
|
164
|
+
});
|
|
117
165
|
}).catch((error) => {
|
|
118
|
-
|
|
166
|
+
const trpcError = TRPCClientError.from(error);
|
|
167
|
+
|
|
168
|
+
observer.error(trpcError);
|
|
169
|
+
observer.next({
|
|
170
|
+
result: {
|
|
171
|
+
type: 'state',
|
|
172
|
+
state: 'error',
|
|
173
|
+
data: TRPCClientError.from(trpcError),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
119
176
|
});
|
|
120
177
|
|
|
121
178
|
return () => {
|
|
122
179
|
observer.complete();
|
|
180
|
+
|
|
181
|
+
observer.next({
|
|
182
|
+
result: {
|
|
183
|
+
type: 'state',
|
|
184
|
+
state: 'idle',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
123
188
|
eventSource?.close();
|
|
124
189
|
unsubscribed = true;
|
|
125
190
|
};
|
|
@@ -154,13 +154,47 @@ export const jsonHttpRequester: Requester = (opts) => {
|
|
|
154
154
|
});
|
|
155
155
|
};
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Polyfill for DOMException with AbortError name
|
|
159
|
+
*/
|
|
160
|
+
class AbortError extends Error {
|
|
161
|
+
constructor() {
|
|
162
|
+
const name = 'AbortError';
|
|
163
|
+
super(name);
|
|
164
|
+
this.name = name;
|
|
165
|
+
this.message = name;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
157
169
|
export type HTTPRequestOptions = ContentOptions &
|
|
158
170
|
HTTPBaseRequestOptions & {
|
|
159
171
|
headers: () => HTTPHeaders | Promise<HTTPHeaders>;
|
|
160
172
|
};
|
|
161
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Polyfill for `signal.throwIfAborted()`
|
|
176
|
+
*
|
|
177
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted
|
|
178
|
+
*/
|
|
179
|
+
const throwIfAborted = (signal: Maybe<AbortSignal>) => {
|
|
180
|
+
if (!signal?.aborted) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// If available, use the native implementation
|
|
184
|
+
signal.throwIfAborted?.();
|
|
185
|
+
|
|
186
|
+
// If we have `DOMException`, use it
|
|
187
|
+
if (typeof DOMException !== 'undefined') {
|
|
188
|
+
throw new DOMException('AbortError', 'AbortError');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Otherwise, use our own implementation
|
|
192
|
+
throw new AbortError();
|
|
193
|
+
};
|
|
194
|
+
|
|
162
195
|
export async function fetchHTTPResponse(opts: HTTPRequestOptions) {
|
|
163
|
-
opts.signal
|
|
196
|
+
throwIfAborted(opts.signal);
|
|
197
|
+
|
|
164
198
|
const url = opts.getUrl(opts);
|
|
165
199
|
const body = opts.getBody(opts);
|
|
166
200
|
const { type } = opts;
|
|
@@ -10,7 +10,7 @@ export const resultOf = <T>(value: T | (() => T)): T => {
|
|
|
10
10
|
/**
|
|
11
11
|
* A value that can be wrapped in callback
|
|
12
12
|
*/
|
|
13
|
-
type CallbackOrValue<T> = T | (() => T | Promise<T>);
|
|
13
|
+
export type CallbackOrValue<T> = T | (() => T | Promise<T>);
|
|
14
14
|
|
|
15
15
|
export interface UrlOptionsWithConnectionParams {
|
|
16
16
|
/**
|
package/src/links/types.ts
CHANGED
|
@@ -55,11 +55,54 @@ export interface TRPCClientRuntime {
|
|
|
55
55
|
// nothing here anymore
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export type ConnectionState = 'idle' | 'connecting' | 'pending' | 'error';
|
|
59
|
+
|
|
60
|
+
export interface ConnectionStateMessageBase {
|
|
61
|
+
type: 'state';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface IdleStateMessage extends ConnectionStateMessageBase {
|
|
65
|
+
state: 'idle';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ConnectingStateMessage<TError>
|
|
69
|
+
extends ConnectionStateMessageBase {
|
|
70
|
+
state: 'connecting';
|
|
71
|
+
data: TError | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PendingStateMessage extends ConnectionStateMessageBase {
|
|
75
|
+
state: 'pending';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ErrorStateMessage<TError> extends ConnectionStateMessageBase {
|
|
79
|
+
state: 'error';
|
|
80
|
+
data: TError;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type TRPCConnectionStateMessage<TError> =
|
|
84
|
+
| IdleStateMessage
|
|
85
|
+
| ConnectingStateMessage<TError>
|
|
86
|
+
| PendingStateMessage
|
|
87
|
+
| ErrorStateMessage<TError>;
|
|
88
|
+
|
|
89
|
+
export const isConnectionStateMessage = <TError>(
|
|
90
|
+
msg: unknown,
|
|
91
|
+
): msg is TRPCConnectionStateMessage<TError> => {
|
|
92
|
+
return (
|
|
93
|
+
typeof msg === 'object' &&
|
|
94
|
+
msg !== null &&
|
|
95
|
+
'type' in msg &&
|
|
96
|
+
msg.type === 'state'
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
58
100
|
/**
|
|
59
101
|
* @internal
|
|
60
102
|
*/
|
|
61
|
-
export interface OperationResultEnvelope<TOutput> {
|
|
103
|
+
export interface OperationResultEnvelope<TOutput, TError = unknown> {
|
|
62
104
|
result:
|
|
105
|
+
| TRPCConnectionStateMessage<TError>
|
|
63
106
|
| TRPCResultMessage<TOutput>['result']
|
|
64
107
|
| TRPCSuccessResponse<TOutput>['result'];
|
|
65
108
|
context?: OperationContext;
|
|
@@ -71,7 +114,10 @@ export interface OperationResultEnvelope<TOutput> {
|
|
|
71
114
|
export type OperationResultObservable<
|
|
72
115
|
TInferrable extends InferrableClientTypes,
|
|
73
116
|
TOutput,
|
|
74
|
-
> = Observable<
|
|
117
|
+
> = Observable<
|
|
118
|
+
OperationResultEnvelope<TOutput, TRPCClientError<TInferrable>>,
|
|
119
|
+
TRPCClientError<TInferrable>
|
|
120
|
+
>;
|
|
75
121
|
|
|
76
122
|
/**
|
|
77
123
|
* @internal
|
package/src/links/wsLink.ts
CHANGED
|
@@ -20,14 +20,18 @@ import {
|
|
|
20
20
|
resultOf,
|
|
21
21
|
type UrlOptionsWithConnectionParams,
|
|
22
22
|
} from './internals/urlWithConnectionParams';
|
|
23
|
-
import
|
|
23
|
+
import {
|
|
24
|
+
isConnectionStateMessage,
|
|
25
|
+
type Operation,
|
|
26
|
+
type TRPCConnectionStateMessage,
|
|
27
|
+
type TRPCLink,
|
|
28
|
+
} from './types';
|
|
24
29
|
|
|
25
30
|
const run = <TResult>(fn: () => TResult): TResult => fn();
|
|
26
31
|
|
|
27
|
-
type WSCallbackResult<TRouter extends AnyRouter, TOutput> =
|
|
28
|
-
TOutput,
|
|
29
|
-
inferRouterError<TRouter
|
|
30
|
-
>;
|
|
32
|
+
type WSCallbackResult<TRouter extends AnyRouter, TOutput> =
|
|
33
|
+
| TRPCResponseMessage<TOutput, inferRouterError<TRouter>>
|
|
34
|
+
| { result: TRPCConnectionStateMessage<inferRouterError<TRouter>> };
|
|
31
35
|
|
|
32
36
|
type WSCallbackObserver<TRouter extends AnyRouter, TOutput> = Observer<
|
|
33
37
|
WSCallbackResult<TRouter, TOutput>,
|
|
@@ -121,7 +125,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
121
125
|
undefined;
|
|
122
126
|
let activeConnection: null | Connection = lazyOpts.enabled
|
|
123
127
|
? null
|
|
124
|
-
: createConnection();
|
|
128
|
+
: createConnection(null);
|
|
125
129
|
|
|
126
130
|
type Connection = {
|
|
127
131
|
id: number;
|
|
@@ -145,7 +149,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
145
149
|
*/
|
|
146
150
|
function dispatch() {
|
|
147
151
|
if (!activeConnection) {
|
|
148
|
-
activeConnection = createConnection();
|
|
152
|
+
activeConnection = createConnection(null);
|
|
149
153
|
return;
|
|
150
154
|
}
|
|
151
155
|
// using a timeout to batch messages
|
|
@@ -171,14 +175,14 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
171
175
|
startLazyDisconnectTimer();
|
|
172
176
|
});
|
|
173
177
|
}
|
|
174
|
-
function tryReconnect(conn: Connection) {
|
|
178
|
+
function tryReconnect(conn: Connection, cause: Error | null) {
|
|
175
179
|
if (!!connectTimer) {
|
|
176
180
|
return;
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
conn.state = 'connecting';
|
|
180
184
|
const timeout = retryDelayFn(connectAttempt++);
|
|
181
|
-
reconnectInMs(timeout);
|
|
185
|
+
reconnectInMs(timeout, cause);
|
|
182
186
|
}
|
|
183
187
|
function hasPendingRequests(conn?: Connection) {
|
|
184
188
|
const requests = Object.values(pendingRequests);
|
|
@@ -188,20 +192,20 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
188
192
|
return requests.some((req) => req.connection === conn);
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
function reconnect() {
|
|
195
|
+
function reconnect(cause: Error | null) {
|
|
192
196
|
if (lazyOpts.enabled && !hasPendingRequests()) {
|
|
193
197
|
// Skip reconnecting if there are pending requests and we're in lazy mode
|
|
194
198
|
return;
|
|
195
199
|
}
|
|
196
200
|
const oldConnection = activeConnection;
|
|
197
|
-
activeConnection = createConnection();
|
|
201
|
+
activeConnection = createConnection(cause);
|
|
198
202
|
oldConnection && closeIfNoPending(oldConnection);
|
|
199
203
|
}
|
|
200
|
-
function reconnectInMs(ms: number) {
|
|
204
|
+
function reconnectInMs(ms: number, cause: Error | null) {
|
|
201
205
|
if (connectTimer) {
|
|
202
206
|
return;
|
|
203
207
|
}
|
|
204
|
-
connectTimer = setTimeout(reconnect, ms);
|
|
208
|
+
connectTimer = setTimeout(reconnect, ms, cause);
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
function closeIfNoPending(conn: Connection) {
|
|
@@ -235,18 +239,30 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
235
239
|
}, lazyOpts.closeMs);
|
|
236
240
|
};
|
|
237
241
|
|
|
238
|
-
function createConnection(): Connection {
|
|
242
|
+
function createConnection(cause: Error | null): Connection {
|
|
239
243
|
const self: Connection = {
|
|
240
244
|
id: ++connectionIndex,
|
|
241
245
|
state: 'connecting',
|
|
242
246
|
} as Connection;
|
|
243
247
|
|
|
248
|
+
for (const req of Object.values(pendingRequests)) {
|
|
249
|
+
if (req.type === 'subscription') {
|
|
250
|
+
req.callbacks.next?.({
|
|
251
|
+
result: {
|
|
252
|
+
type: 'state',
|
|
253
|
+
state: 'connecting',
|
|
254
|
+
data: cause ?? null,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
244
260
|
clearTimeout(lazyDisconnectTimer);
|
|
245
261
|
|
|
246
|
-
const onError = () => {
|
|
262
|
+
const onError = (cause: Error | null) => {
|
|
247
263
|
self.state = 'closed';
|
|
248
264
|
if (self === activeConnection) {
|
|
249
|
-
tryReconnect(self);
|
|
265
|
+
tryReconnect(self, cause);
|
|
250
266
|
}
|
|
251
267
|
};
|
|
252
268
|
run(async () => {
|
|
@@ -281,6 +297,17 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
281
297
|
connectAttempt = 0;
|
|
282
298
|
self.state = 'open';
|
|
283
299
|
|
|
300
|
+
for (const req of Object.values(pendingRequests)) {
|
|
301
|
+
if (req.type === 'subscription') {
|
|
302
|
+
req.callbacks.next?.({
|
|
303
|
+
result: {
|
|
304
|
+
type: 'state',
|
|
305
|
+
state: 'pending',
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
284
311
|
onOpen?.();
|
|
285
312
|
dispatch();
|
|
286
313
|
}).catch((cause) => {
|
|
@@ -289,17 +316,23 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
289
316
|
3000,
|
|
290
317
|
cause,
|
|
291
318
|
);
|
|
292
|
-
onError();
|
|
319
|
+
onError(cause);
|
|
293
320
|
});
|
|
294
321
|
});
|
|
295
|
-
ws.addEventListener('error',
|
|
322
|
+
ws.addEventListener('error', (event) => {
|
|
323
|
+
if (event instanceof ErrorEvent) {
|
|
324
|
+
onError(event.error);
|
|
325
|
+
} else {
|
|
326
|
+
onError(new Error('Unknown WebSocket error'));
|
|
327
|
+
}
|
|
328
|
+
});
|
|
296
329
|
const handleIncomingRequest = (req: TRPCClientIncomingRequest) => {
|
|
297
330
|
if (self !== activeConnection) {
|
|
298
331
|
return;
|
|
299
332
|
}
|
|
300
333
|
|
|
301
334
|
if (req.method === 'reconnect') {
|
|
302
|
-
reconnect();
|
|
335
|
+
reconnect(new Error('Server requested reconnect'));
|
|
303
336
|
// notify subscribers
|
|
304
337
|
for (const pendingReq of Object.values(pendingRequests)) {
|
|
305
338
|
if (pendingReq.type === 'subscription') {
|
|
@@ -347,7 +380,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
347
380
|
}
|
|
348
381
|
});
|
|
349
382
|
|
|
350
|
-
ws.addEventListener('close', ({ code }) => {
|
|
383
|
+
ws.addEventListener('close', ({ code, reason }) => {
|
|
351
384
|
if (self.state === 'open') {
|
|
352
385
|
onClose?.({ code });
|
|
353
386
|
}
|
|
@@ -355,7 +388,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
355
388
|
|
|
356
389
|
if (activeConnection === self) {
|
|
357
390
|
// connection might have been replaced already
|
|
358
|
-
tryReconnect(self);
|
|
391
|
+
tryReconnect(self, new Error(reason));
|
|
359
392
|
}
|
|
360
393
|
|
|
361
394
|
for (const [key, req] of Object.entries(pendingRequests)) {
|
|
@@ -490,20 +523,67 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
490
523
|
{
|
|
491
524
|
error(err) {
|
|
492
525
|
observer.error(err as TRPCClientError<any>);
|
|
526
|
+
|
|
527
|
+
observer.next({
|
|
528
|
+
result: {
|
|
529
|
+
type: 'state',
|
|
530
|
+
state: 'error',
|
|
531
|
+
data: err,
|
|
532
|
+
},
|
|
533
|
+
context: context,
|
|
534
|
+
});
|
|
535
|
+
|
|
493
536
|
unsub();
|
|
494
537
|
},
|
|
495
538
|
complete() {
|
|
496
539
|
observer.complete();
|
|
540
|
+
|
|
541
|
+
observer.next({
|
|
542
|
+
result: {
|
|
543
|
+
type: 'state',
|
|
544
|
+
state: 'idle',
|
|
545
|
+
},
|
|
546
|
+
context: context,
|
|
547
|
+
});
|
|
497
548
|
},
|
|
498
549
|
next(message) {
|
|
550
|
+
if (
|
|
551
|
+
'result' in message &&
|
|
552
|
+
isConnectionStateMessage<TRPCClientError<TRouter>>(
|
|
553
|
+
message.result,
|
|
554
|
+
)
|
|
555
|
+
) {
|
|
556
|
+
message.result;
|
|
557
|
+
|
|
558
|
+
observer.next({
|
|
559
|
+
result: message.result,
|
|
560
|
+
context: context,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (!('id' in message)) return;
|
|
567
|
+
|
|
499
568
|
const transformed = transformResult(message, transformer.output);
|
|
500
569
|
|
|
501
570
|
if (!transformed.ok) {
|
|
571
|
+
const error = TRPCClientError.from(transformed.error);
|
|
572
|
+
|
|
573
|
+
observer.next({
|
|
574
|
+
result: {
|
|
575
|
+
type: 'state',
|
|
576
|
+
state: 'error',
|
|
577
|
+
data: error,
|
|
578
|
+
},
|
|
579
|
+
context: context,
|
|
580
|
+
});
|
|
502
581
|
observer.error(TRPCClientError.from(transformed.error));
|
|
503
582
|
return;
|
|
504
583
|
}
|
|
505
584
|
observer.next({
|
|
506
585
|
result: transformed.result,
|
|
586
|
+
context: context,
|
|
507
587
|
});
|
|
508
588
|
|
|
509
589
|
if (op.type !== 'subscription') {
|
|
@@ -517,6 +597,13 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
517
597
|
);
|
|
518
598
|
return () => {
|
|
519
599
|
unsub();
|
|
600
|
+
observer.next({
|
|
601
|
+
result: {
|
|
602
|
+
type: 'state',
|
|
603
|
+
state: 'idle',
|
|
604
|
+
},
|
|
605
|
+
context: context,
|
|
606
|
+
});
|
|
520
607
|
};
|
|
521
608
|
});
|
|
522
609
|
};
|