@trpc/client 10.27.2 → 10.28.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.
- package/dist/links/wsLink.d.ts.map +1 -1
- package/dist/links/wsLink.js +9 -17
- package/dist/links/wsLink.mjs +9 -17
- package/package.json +9 -5
- package/src/links/wsLink.ts +10 -24
- package/src/internals/types.test.ts +0 -66
- package/src/links/dedupeLink.test.ts +0 -128
- package/src/links/internals/createChain.test.ts +0 -117
- package/src/links/splitLink.test.ts +0 -55
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wsLink.d.ts","sourceRoot":"","sources":["../../src/links/wsLink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAc,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAKL,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE9C,aAAK,gBAAgB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,mBAAmB,CAC7E,OAAO,EACP,gBAAgB,CAAC,OAAO,CAAC,CAC1B,CAAC;AAEF,aAAK,kBAAkB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,QAAQ,CACpE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAClC,eAAe,CAAC,OAAO,CAAC,CACzB,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,UAAU,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,sBAAsB;;
|
|
1
|
+
{"version":3,"file":"wsLink.d.ts","sourceRoot":"","sources":["../../src/links/wsLink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAc,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAKL,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE9C,aAAK,gBAAgB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,mBAAmB,CAC7E,OAAO,EACP,gBAAgB,CAAC,OAAO,CAAC,CAC1B,CAAC;AAEF,aAAK,kBAAkB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,QAAQ,CACpE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAClC,eAAe,CAAC,OAAO,CAAC,CACzB,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,UAAU,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,sBAAsB;;kBAwNpC,SAAS,wDAA0B,aAAa;;EAqDtE;AACD,oBAAY,mBAAmB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AASD,wBAAgB,MAAM,CAAC,OAAO,SAAS,SAAS,EAC9C,IAAI,EAAE,oBAAoB,GACzB,QAAQ,CAAC,OAAO,CAAC,CA6CnB"}
|
package/dist/links/wsLink.js
CHANGED
|
@@ -67,6 +67,13 @@ function createWSClient(opts) {
|
|
|
67
67
|
conn.close();
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
function closeActiveSubscriptions() {
|
|
71
|
+
Object.values(pendingRequests).forEach((req)=>{
|
|
72
|
+
if (req.type === 'subscription') {
|
|
73
|
+
req.callbacks.complete();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
70
77
|
function resumeSubscriptionOnReconnect(req) {
|
|
71
78
|
if (outgoing.some((r)=>r.id === req.op.id)) {
|
|
72
79
|
return;
|
|
@@ -205,6 +212,7 @@ function createWSClient(opts) {
|
|
|
205
212
|
close: ()=>{
|
|
206
213
|
state = 'closed';
|
|
207
214
|
onClose?.();
|
|
215
|
+
closeActiveSubscriptions();
|
|
208
216
|
closeIfNoPending(activeConnection);
|
|
209
217
|
clearTimeout(connectTimer);
|
|
210
218
|
connectTimer = null;
|
|
@@ -222,13 +230,6 @@ class TRPCWebSocketClosedError extends Error {
|
|
|
222
230
|
Object.setPrototypeOf(this, TRPCWebSocketClosedError.prototype);
|
|
223
231
|
}
|
|
224
232
|
}
|
|
225
|
-
class TRPCSubscriptionEndedError extends Error {
|
|
226
|
-
constructor(message){
|
|
227
|
-
super(message);
|
|
228
|
-
this.name = 'TRPCSubscriptionEndedError';
|
|
229
|
-
Object.setPrototypeOf(this, TRPCSubscriptionEndedError.prototype);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
233
|
function wsLink(opts) {
|
|
233
234
|
return (runtime)=>{
|
|
234
235
|
const { client } = opts;
|
|
@@ -236,7 +237,6 @@ function wsLink(opts) {
|
|
|
236
237
|
return observable.observable((observer)=>{
|
|
237
238
|
const { type , path , id , context } = op;
|
|
238
239
|
const input = runtime.transformer.serialize(op.input);
|
|
239
|
-
let isDone = false;
|
|
240
240
|
const unsub = client.request({
|
|
241
241
|
type,
|
|
242
242
|
path,
|
|
@@ -245,17 +245,11 @@ function wsLink(opts) {
|
|
|
245
245
|
context
|
|
246
246
|
}, {
|
|
247
247
|
error (err) {
|
|
248
|
-
isDone = true;
|
|
249
248
|
observer.error(err);
|
|
250
249
|
unsub();
|
|
251
250
|
},
|
|
252
251
|
complete () {
|
|
253
|
-
|
|
254
|
-
isDone = true;
|
|
255
|
-
observer.error(transformResult.TRPCClientError.from(new TRPCSubscriptionEndedError('Operation ended prematurely')));
|
|
256
|
-
} else {
|
|
257
|
-
observer.complete();
|
|
258
|
-
}
|
|
252
|
+
observer.complete();
|
|
259
253
|
},
|
|
260
254
|
next (message) {
|
|
261
255
|
const transformed = transformResult.transformResult(message, runtime);
|
|
@@ -268,14 +262,12 @@ function wsLink(opts) {
|
|
|
268
262
|
});
|
|
269
263
|
if (op.type !== 'subscription') {
|
|
270
264
|
// if it isn't a subscription we don't care about next response
|
|
271
|
-
isDone = true;
|
|
272
265
|
unsub();
|
|
273
266
|
observer.complete();
|
|
274
267
|
}
|
|
275
268
|
}
|
|
276
269
|
});
|
|
277
270
|
return ()=>{
|
|
278
|
-
isDone = true;
|
|
279
271
|
unsub();
|
|
280
272
|
};
|
|
281
273
|
});
|
package/dist/links/wsLink.mjs
CHANGED
|
@@ -63,6 +63,13 @@ function createWSClient(opts) {
|
|
|
63
63
|
conn.close();
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
function closeActiveSubscriptions() {
|
|
67
|
+
Object.values(pendingRequests).forEach((req)=>{
|
|
68
|
+
if (req.type === 'subscription') {
|
|
69
|
+
req.callbacks.complete();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
66
73
|
function resumeSubscriptionOnReconnect(req) {
|
|
67
74
|
if (outgoing.some((r)=>r.id === req.op.id)) {
|
|
68
75
|
return;
|
|
@@ -201,6 +208,7 @@ function createWSClient(opts) {
|
|
|
201
208
|
close: ()=>{
|
|
202
209
|
state = 'closed';
|
|
203
210
|
onClose?.();
|
|
211
|
+
closeActiveSubscriptions();
|
|
204
212
|
closeIfNoPending(activeConnection);
|
|
205
213
|
clearTimeout(connectTimer);
|
|
206
214
|
connectTimer = null;
|
|
@@ -218,13 +226,6 @@ class TRPCWebSocketClosedError extends Error {
|
|
|
218
226
|
Object.setPrototypeOf(this, TRPCWebSocketClosedError.prototype);
|
|
219
227
|
}
|
|
220
228
|
}
|
|
221
|
-
class TRPCSubscriptionEndedError extends Error {
|
|
222
|
-
constructor(message){
|
|
223
|
-
super(message);
|
|
224
|
-
this.name = 'TRPCSubscriptionEndedError';
|
|
225
|
-
Object.setPrototypeOf(this, TRPCSubscriptionEndedError.prototype);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
229
|
function wsLink(opts) {
|
|
229
230
|
return (runtime)=>{
|
|
230
231
|
const { client } = opts;
|
|
@@ -232,7 +233,6 @@ function wsLink(opts) {
|
|
|
232
233
|
return observable((observer)=>{
|
|
233
234
|
const { type , path , id , context } = op;
|
|
234
235
|
const input = runtime.transformer.serialize(op.input);
|
|
235
|
-
let isDone = false;
|
|
236
236
|
const unsub = client.request({
|
|
237
237
|
type,
|
|
238
238
|
path,
|
|
@@ -241,17 +241,11 @@ function wsLink(opts) {
|
|
|
241
241
|
context
|
|
242
242
|
}, {
|
|
243
243
|
error (err) {
|
|
244
|
-
isDone = true;
|
|
245
244
|
observer.error(err);
|
|
246
245
|
unsub();
|
|
247
246
|
},
|
|
248
247
|
complete () {
|
|
249
|
-
|
|
250
|
-
isDone = true;
|
|
251
|
-
observer.error(TRPCClientError.from(new TRPCSubscriptionEndedError('Operation ended prematurely')));
|
|
252
|
-
} else {
|
|
253
|
-
observer.complete();
|
|
254
|
-
}
|
|
248
|
+
observer.complete();
|
|
255
249
|
},
|
|
256
250
|
next (message) {
|
|
257
251
|
const transformed = transformResult(message, runtime);
|
|
@@ -264,14 +258,12 @@ function wsLink(opts) {
|
|
|
264
258
|
});
|
|
265
259
|
if (op.type !== 'subscription') {
|
|
266
260
|
// if it isn't a subscription we don't care about next response
|
|
267
|
-
isDone = true;
|
|
268
261
|
unsub();
|
|
269
262
|
observer.complete();
|
|
270
263
|
}
|
|
271
264
|
}
|
|
272
265
|
});
|
|
273
266
|
return ()=>{
|
|
274
|
-
isDone = true;
|
|
275
267
|
unsub();
|
|
276
268
|
};
|
|
277
269
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trpc/client",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.28.0",
|
|
4
4
|
"description": "The tRPC client library",
|
|
5
5
|
"author": "KATT",
|
|
6
6
|
"license": "MIT",
|
|
@@ -72,14 +72,15 @@
|
|
|
72
72
|
"README.md",
|
|
73
73
|
"package.json",
|
|
74
74
|
"links",
|
|
75
|
-
"shared"
|
|
75
|
+
"shared",
|
|
76
|
+
"!**/*.test.*"
|
|
76
77
|
],
|
|
77
78
|
"peerDependencies": {
|
|
78
|
-
"@trpc/server": "10.
|
|
79
|
+
"@trpc/server": "10.28.0"
|
|
79
80
|
},
|
|
80
81
|
"devDependencies": {
|
|
81
82
|
"@testing-library/dom": "^9.0.0",
|
|
82
|
-
"@trpc/server": "10.
|
|
83
|
+
"@trpc/server": "10.28.0",
|
|
83
84
|
"@types/isomorphic-fetch": "^0.0.36",
|
|
84
85
|
"@types/node": "^18.7.20",
|
|
85
86
|
"eslint": "^8.40.0",
|
|
@@ -94,5 +95,8 @@
|
|
|
94
95
|
"publishConfig": {
|
|
95
96
|
"access": "public"
|
|
96
97
|
},
|
|
97
|
-
"
|
|
98
|
+
"funding": [
|
|
99
|
+
"https://trpc.io/sponsor"
|
|
100
|
+
],
|
|
101
|
+
"gitHead": "7aadb24ce0bc20ae27f32bd80891ef6da5885e5e"
|
|
98
102
|
}
|
package/src/links/wsLink.ts
CHANGED
|
@@ -120,6 +120,14 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
function closeActiveSubscriptions() {
|
|
124
|
+
Object.values(pendingRequests).forEach((req) => {
|
|
125
|
+
if (req.type === 'subscription') {
|
|
126
|
+
req.callbacks.complete();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
123
131
|
function resumeSubscriptionOnReconnect(req: TRequest) {
|
|
124
132
|
if (outgoing.some((r) => r.id === req.op.id)) {
|
|
125
133
|
return;
|
|
@@ -281,6 +289,7 @@ export function createWSClient(opts: WebSocketClientOptions) {
|
|
|
281
289
|
close: () => {
|
|
282
290
|
state = 'closed';
|
|
283
291
|
onClose?.();
|
|
292
|
+
closeActiveSubscriptions();
|
|
284
293
|
closeIfNoPending(activeConnection);
|
|
285
294
|
clearTimeout(connectTimer as any);
|
|
286
295
|
connectTimer = null;
|
|
@@ -304,14 +313,6 @@ class TRPCWebSocketClosedError extends Error {
|
|
|
304
313
|
}
|
|
305
314
|
}
|
|
306
315
|
|
|
307
|
-
class TRPCSubscriptionEndedError extends Error {
|
|
308
|
-
constructor(message: string) {
|
|
309
|
-
super(message);
|
|
310
|
-
this.name = 'TRPCSubscriptionEndedError';
|
|
311
|
-
Object.setPrototypeOf(this, TRPCSubscriptionEndedError.prototype);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
316
|
export function wsLink<TRouter extends AnyRouter>(
|
|
316
317
|
opts: WebSocketLinkOptions,
|
|
317
318
|
): TRPCLink<TRouter> {
|
|
@@ -323,28 +324,15 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
323
324
|
|
|
324
325
|
const input = runtime.transformer.serialize(op.input);
|
|
325
326
|
|
|
326
|
-
let isDone = false;
|
|
327
327
|
const unsub = client.request(
|
|
328
328
|
{ type, path, input, id, context },
|
|
329
329
|
{
|
|
330
330
|
error(err) {
|
|
331
|
-
isDone = true;
|
|
332
331
|
observer.error(err as TRPCClientError<any>);
|
|
333
332
|
unsub();
|
|
334
333
|
},
|
|
335
334
|
complete() {
|
|
336
|
-
|
|
337
|
-
isDone = true;
|
|
338
|
-
observer.error(
|
|
339
|
-
TRPCClientError.from(
|
|
340
|
-
new TRPCSubscriptionEndedError(
|
|
341
|
-
'Operation ended prematurely',
|
|
342
|
-
),
|
|
343
|
-
),
|
|
344
|
-
);
|
|
345
|
-
} else {
|
|
346
|
-
observer.complete();
|
|
347
|
-
}
|
|
335
|
+
observer.complete();
|
|
348
336
|
},
|
|
349
337
|
next(message) {
|
|
350
338
|
const transformed = transformResult(message, runtime);
|
|
@@ -360,7 +348,6 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
360
348
|
if (op.type !== 'subscription') {
|
|
361
349
|
// if it isn't a subscription we don't care about next response
|
|
362
350
|
|
|
363
|
-
isDone = true;
|
|
364
351
|
unsub();
|
|
365
352
|
observer.complete();
|
|
366
353
|
}
|
|
@@ -368,7 +355,6 @@ export function wsLink<TRouter extends AnyRouter>(
|
|
|
368
355
|
},
|
|
369
356
|
);
|
|
370
357
|
return () => {
|
|
371
|
-
isDone = true;
|
|
372
358
|
unsub();
|
|
373
359
|
};
|
|
374
360
|
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import isomorphicFetch from 'isomorphic-fetch';
|
|
2
|
-
import nodeFetch from 'node-fetch';
|
|
3
|
-
import type { fetch as undiciFetch } from 'undici';
|
|
4
|
-
import { createTRPCProxyClient } from '../createTRPCClientProxy';
|
|
5
|
-
import { getFetch } from '../getFetch';
|
|
6
|
-
import { httpBatchLink } from '../links/httpBatchLink';
|
|
7
|
-
import { getAbortController } from './getAbortController';
|
|
8
|
-
import {
|
|
9
|
-
AbortControllerEsque,
|
|
10
|
-
AbortControllerInstanceEsque,
|
|
11
|
-
FetchEsque,
|
|
12
|
-
ResponseEsque,
|
|
13
|
-
} from './types';
|
|
14
|
-
|
|
15
|
-
describe('AbortController', () => {
|
|
16
|
-
test('AbortControllerEsque', () => {
|
|
17
|
-
expectTypeOf(
|
|
18
|
-
getAbortController,
|
|
19
|
-
).returns.toEqualTypeOf<AbortControllerEsque | null>();
|
|
20
|
-
|
|
21
|
-
expectTypeOf(() => {
|
|
22
|
-
const AbortController = getAbortController(undefined)!;
|
|
23
|
-
return new AbortController();
|
|
24
|
-
}).returns.toEqualTypeOf<AbortControllerInstanceEsque>();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('fetch', () => {
|
|
29
|
-
test('parameters', () => {
|
|
30
|
-
createTRPCProxyClient({
|
|
31
|
-
links: [
|
|
32
|
-
httpBatchLink({
|
|
33
|
-
url: 'YOUR_SERVER_URL',
|
|
34
|
-
fetch(url, options) {
|
|
35
|
-
return fetch(url, options);
|
|
36
|
-
},
|
|
37
|
-
}),
|
|
38
|
-
],
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('FetchEsque', () => {
|
|
43
|
-
expectTypeOf(getFetch).returns.toEqualTypeOf<FetchEsque>();
|
|
44
|
-
|
|
45
|
-
expectTypeOf(() =>
|
|
46
|
-
getFetch()('', {
|
|
47
|
-
body: '',
|
|
48
|
-
headers: Math.random() > 0.5 ? [['a', 'b']] : { a: 'b' },
|
|
49
|
-
method: 'GET',
|
|
50
|
-
signal: new AbortSignal(),
|
|
51
|
-
}),
|
|
52
|
-
).returns.toEqualTypeOf<Promise<ResponseEsque>>();
|
|
53
|
-
|
|
54
|
-
getFetch({} as unknown as typeof fetch);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('NativeFetchEsque', () => {
|
|
58
|
-
getFetch(isomorphicFetch);
|
|
59
|
-
getFetch(nodeFetch as any);
|
|
60
|
-
|
|
61
|
-
// Passing in undiciFetch directly in Node v18.7.0 gives:
|
|
62
|
-
// ReferenceError: TextEncoder is not defined
|
|
63
|
-
// 🤷
|
|
64
|
-
getFetch({} as unknown as typeof undiciFetch);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { waitFor } from '@testing-library/dom';
|
|
2
|
-
import { OperationLink } from '..';
|
|
3
|
-
import { AnyRouter } from '../../../server/src';
|
|
4
|
-
import { observable } from '../../../server/src/observable';
|
|
5
|
-
import { dedupeLink } from './dedupeLink';
|
|
6
|
-
import { createChain } from './internals/createChain';
|
|
7
|
-
|
|
8
|
-
test('dedupeLink', async () => {
|
|
9
|
-
const endingLinkTriggered = vi.fn();
|
|
10
|
-
const timerTriggered = vi.fn();
|
|
11
|
-
const links: OperationLink<AnyRouter, any, any>[] = [
|
|
12
|
-
// "dedupe link"
|
|
13
|
-
dedupeLink()(null as any),
|
|
14
|
-
({ op }) => {
|
|
15
|
-
return observable((subscribe) => {
|
|
16
|
-
endingLinkTriggered();
|
|
17
|
-
const timer = setTimeout(() => {
|
|
18
|
-
timerTriggered();
|
|
19
|
-
subscribe.next({
|
|
20
|
-
result: {
|
|
21
|
-
type: 'data',
|
|
22
|
-
data: {
|
|
23
|
-
input: op.input,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
subscribe.complete();
|
|
28
|
-
}, 1);
|
|
29
|
-
return () => clearTimeout(timer);
|
|
30
|
-
});
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
{
|
|
34
|
-
const call1 = createChain<AnyRouter, unknown, unknown>({
|
|
35
|
-
links,
|
|
36
|
-
op: {
|
|
37
|
-
type: 'query',
|
|
38
|
-
id: 1,
|
|
39
|
-
input: 'world',
|
|
40
|
-
path: 'hello',
|
|
41
|
-
context: {},
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const call2 = createChain<AnyRouter, unknown, unknown>({
|
|
46
|
-
links,
|
|
47
|
-
op: {
|
|
48
|
-
type: 'query',
|
|
49
|
-
id: 1,
|
|
50
|
-
input: 'world',
|
|
51
|
-
path: 'hello',
|
|
52
|
-
context: {},
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
const next = vi.fn();
|
|
56
|
-
call1.subscribe({ next });
|
|
57
|
-
call2.subscribe({ next });
|
|
58
|
-
|
|
59
|
-
expect(endingLinkTriggered).toHaveBeenCalledTimes(1);
|
|
60
|
-
await waitFor(() => {
|
|
61
|
-
expect(timerTriggered).toHaveBeenCalledTimes(1);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(next).toHaveBeenCalledTimes(2);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('dedupe - cancel one does not cancel the other', async () => {
|
|
69
|
-
const endingLinkTriggered = vi.fn();
|
|
70
|
-
const timerTriggered = vi.fn();
|
|
71
|
-
const links: OperationLink<AnyRouter, any, any>[] = [
|
|
72
|
-
// "dedupe link"
|
|
73
|
-
dedupeLink()(null as any),
|
|
74
|
-
({ op }) => {
|
|
75
|
-
return observable((subscribe) => {
|
|
76
|
-
endingLinkTriggered();
|
|
77
|
-
const timer = setTimeout(() => {
|
|
78
|
-
timerTriggered();
|
|
79
|
-
subscribe.next({
|
|
80
|
-
result: {
|
|
81
|
-
type: 'data',
|
|
82
|
-
data: {
|
|
83
|
-
input: op.input,
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
subscribe.complete();
|
|
88
|
-
}, 1);
|
|
89
|
-
return () => clearTimeout(timer);
|
|
90
|
-
});
|
|
91
|
-
},
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
{
|
|
95
|
-
const call1 = createChain<AnyRouter, unknown, unknown>({
|
|
96
|
-
links,
|
|
97
|
-
op: {
|
|
98
|
-
type: 'query',
|
|
99
|
-
id: 1,
|
|
100
|
-
input: 'world',
|
|
101
|
-
path: 'hello',
|
|
102
|
-
context: {},
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const call2 = createChain<AnyRouter, unknown, unknown>({
|
|
107
|
-
links,
|
|
108
|
-
op: {
|
|
109
|
-
type: 'query',
|
|
110
|
-
id: 1,
|
|
111
|
-
input: 'world',
|
|
112
|
-
path: 'hello',
|
|
113
|
-
context: {},
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
const next = vi.fn();
|
|
117
|
-
const call1$ = call1.subscribe({ next });
|
|
118
|
-
call2.subscribe({ next });
|
|
119
|
-
call1$.unsubscribe();
|
|
120
|
-
|
|
121
|
-
expect(endingLinkTriggered).toHaveBeenCalledTimes(1);
|
|
122
|
-
await waitFor(() => {
|
|
123
|
-
expect(timerTriggered).toHaveBeenCalledTimes(1);
|
|
124
|
-
|
|
125
|
-
expect(next).toHaveBeenCalledTimes(1);
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { observable } from '@trpc/server/observable';
|
|
2
|
-
import { AnyRouter } from '@trpc/server/src';
|
|
3
|
-
import { createChain } from './createChain';
|
|
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 subscription = next(op).subscribe(observer);
|
|
12
|
-
return () => {
|
|
13
|
-
subscription.unsubscribe();
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
},
|
|
17
|
-
({ op }) => {
|
|
18
|
-
return observable((observer) => {
|
|
19
|
-
observer.next({
|
|
20
|
-
context: {},
|
|
21
|
-
result: {
|
|
22
|
-
type: 'data',
|
|
23
|
-
data: {
|
|
24
|
-
input: op.input,
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
observer.complete();
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
op: {
|
|
33
|
-
type: 'query',
|
|
34
|
-
id: 1,
|
|
35
|
-
input: 'world',
|
|
36
|
-
path: 'hello',
|
|
37
|
-
context: {},
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const next = vi.fn();
|
|
42
|
-
|
|
43
|
-
result$.subscribe({ next });
|
|
44
|
-
// console.log(next.mock.calls);
|
|
45
|
-
expect(next).toHaveBeenCalledTimes(1);
|
|
46
|
-
});
|
|
47
|
-
test('multiple responses', () => {
|
|
48
|
-
const result$ = createChain<AnyRouter, unknown, unknown>({
|
|
49
|
-
links: [
|
|
50
|
-
({ next, op }) => {
|
|
51
|
-
return observable((observer) => {
|
|
52
|
-
observer.next({
|
|
53
|
-
context: {},
|
|
54
|
-
result: {
|
|
55
|
-
type: 'data',
|
|
56
|
-
data: 'from cache',
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
const subscription = next(op).subscribe(observer);
|
|
60
|
-
return () => {
|
|
61
|
-
subscription.unsubscribe();
|
|
62
|
-
};
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
({ op }) => {
|
|
66
|
-
return observable((observer) => {
|
|
67
|
-
observer.next({
|
|
68
|
-
result: {
|
|
69
|
-
type: 'data',
|
|
70
|
-
data: {
|
|
71
|
-
input: op.input,
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
observer.complete();
|
|
76
|
-
});
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
op: {
|
|
80
|
-
type: 'query',
|
|
81
|
-
id: 1,
|
|
82
|
-
input: 'world',
|
|
83
|
-
path: 'hello',
|
|
84
|
-
context: {},
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const next = vi.fn();
|
|
89
|
-
|
|
90
|
-
result$.subscribe({ next });
|
|
91
|
-
|
|
92
|
-
expect(next).toHaveBeenCalledTimes(2);
|
|
93
|
-
expect(next.mock.calls[0]).toMatchInlineSnapshot(`
|
|
94
|
-
Array [
|
|
95
|
-
Object {
|
|
96
|
-
"context": Object {},
|
|
97
|
-
"result": Object {
|
|
98
|
-
"data": "from cache",
|
|
99
|
-
"type": "data",
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
]
|
|
103
|
-
`);
|
|
104
|
-
expect(next.mock.calls[1]).toMatchInlineSnapshot(`
|
|
105
|
-
Array [
|
|
106
|
-
Object {
|
|
107
|
-
"result": Object {
|
|
108
|
-
"data": Object {
|
|
109
|
-
"input": "world",
|
|
110
|
-
},
|
|
111
|
-
"type": "data",
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
]
|
|
115
|
-
`);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { observable } from '@trpc/server/observable';
|
|
2
|
-
import { splitLink } from '../';
|
|
3
|
-
import { OperationLink, TRPCLink } from '../';
|
|
4
|
-
import { AnyRouter } from '../../../server/src';
|
|
5
|
-
import { createChain } from '../links/internals/createChain';
|
|
6
|
-
|
|
7
|
-
test('splitLink', () => {
|
|
8
|
-
const wsLinkSpy = vi.fn();
|
|
9
|
-
const wsLink: TRPCLink<any> = () => () =>
|
|
10
|
-
observable(() => {
|
|
11
|
-
wsLinkSpy();
|
|
12
|
-
});
|
|
13
|
-
const httpLinkSpy = vi.fn();
|
|
14
|
-
const httpLink: TRPCLink<any> = () => () =>
|
|
15
|
-
observable(() => {
|
|
16
|
-
httpLinkSpy();
|
|
17
|
-
});
|
|
18
|
-
const links: OperationLink<AnyRouter, any, any>[] = [
|
|
19
|
-
// "dedupe link"
|
|
20
|
-
splitLink({
|
|
21
|
-
condition(op) {
|
|
22
|
-
return op.type === 'subscription';
|
|
23
|
-
},
|
|
24
|
-
true: wsLink,
|
|
25
|
-
false: [httpLink],
|
|
26
|
-
})(null as any),
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
createChain({
|
|
30
|
-
links,
|
|
31
|
-
op: {
|
|
32
|
-
type: 'query',
|
|
33
|
-
input: null,
|
|
34
|
-
path: '.',
|
|
35
|
-
id: 0,
|
|
36
|
-
context: {},
|
|
37
|
-
},
|
|
38
|
-
}).subscribe({});
|
|
39
|
-
expect(httpLinkSpy).toHaveBeenCalledTimes(1);
|
|
40
|
-
expect(wsLinkSpy).toHaveBeenCalledTimes(0);
|
|
41
|
-
vi.resetAllMocks();
|
|
42
|
-
|
|
43
|
-
createChain({
|
|
44
|
-
links,
|
|
45
|
-
op: {
|
|
46
|
-
type: 'subscription',
|
|
47
|
-
input: null,
|
|
48
|
-
path: '.',
|
|
49
|
-
id: 0,
|
|
50
|
-
context: {},
|
|
51
|
-
},
|
|
52
|
-
}).subscribe({});
|
|
53
|
-
expect(httpLinkSpy).toHaveBeenCalledTimes(0);
|
|
54
|
-
expect(wsLinkSpy).toHaveBeenCalledTimes(1);
|
|
55
|
-
});
|