@trpc/client 11.0.0-rc.772 → 11.0.0-rc.781

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 (51) hide show
  1. package/dist/bundle-analysis.json +132 -39
  2. package/dist/index.js +3 -2
  3. package/dist/index.mjs +2 -1
  4. package/dist/links/wsLink/createWsClient.d.ts +6 -0
  5. package/dist/links/wsLink/createWsClient.d.ts.map +1 -0
  6. package/dist/links/wsLink/createWsClient.js +9 -0
  7. package/dist/links/wsLink/createWsClient.mjs +7 -0
  8. package/dist/links/wsLink/wsClient/options.d.ts +79 -0
  9. package/dist/links/wsLink/wsClient/options.d.ts.map +1 -0
  10. package/dist/links/wsLink/wsClient/options.js +22 -0
  11. package/dist/links/wsLink/wsClient/options.mjs +18 -0
  12. package/dist/links/wsLink/wsClient/requestManager.d.ts +102 -0
  13. package/dist/links/wsLink/wsClient/requestManager.d.ts.map +1 -0
  14. package/dist/links/wsLink/wsClient/requestManager.js +138 -0
  15. package/dist/links/wsLink/wsClient/requestManager.mjs +136 -0
  16. package/dist/links/wsLink/wsClient/utils.d.ts +38 -0
  17. package/dist/links/wsLink/wsClient/utils.d.ts.map +1 -0
  18. package/dist/links/wsLink/wsClient/utils.js +94 -0
  19. package/dist/links/wsLink/wsClient/utils.mjs +88 -0
  20. package/dist/links/wsLink/wsClient/wsClient.d.ts +85 -0
  21. package/dist/links/wsLink/wsClient/wsClient.d.ts.map +1 -0
  22. package/dist/links/wsLink/wsClient/wsClient.js +331 -0
  23. package/dist/links/wsLink/wsClient/wsClient.mjs +329 -0
  24. package/dist/links/wsLink/wsClient/wsConnection.d.ts +79 -0
  25. package/dist/links/wsLink/wsClient/wsConnection.d.ts.map +1 -0
  26. package/dist/links/wsLink/wsClient/wsConnection.js +181 -0
  27. package/dist/links/wsLink/wsClient/wsConnection.mjs +178 -0
  28. package/dist/links/wsLink/wsLink.d.ts +11 -0
  29. package/dist/links/wsLink/wsLink.d.ts.map +1 -0
  30. package/dist/links/wsLink/wsLink.js +35 -0
  31. package/dist/links/wsLink/wsLink.mjs +32 -0
  32. package/dist/links.d.ts +1 -1
  33. package/dist/links.d.ts.map +1 -1
  34. package/links/wsLink/wsLink/index.d.ts +1 -0
  35. package/links/wsLink/wsLink/index.js +1 -0
  36. package/package.json +8 -8
  37. package/src/links/wsLink/createWsClient.ts +10 -0
  38. package/src/links/wsLink/wsClient/options.ts +91 -0
  39. package/src/links/wsLink/wsClient/requestManager.ts +174 -0
  40. package/src/links/wsLink/wsClient/utils.ts +94 -0
  41. package/src/links/wsLink/wsClient/wsClient.ts +441 -0
  42. package/src/links/wsLink/wsClient/wsConnection.ts +230 -0
  43. package/src/links/wsLink/wsLink.ts +55 -0
  44. package/src/links.ts +1 -1
  45. package/dist/links/wsLink.d.ts +0 -125
  46. package/dist/links/wsLink.d.ts.map +0 -1
  47. package/dist/links/wsLink.js +0 -498
  48. package/dist/links/wsLink.mjs +0 -495
  49. package/links/wsLink/index.d.ts +0 -1
  50. package/links/wsLink/index.js +0 -1
  51. package/src/links/wsLink.ts +0 -737
@@ -1,495 +0,0 @@
1
- import { behaviorSubject, observable } from '@trpc/server/observable';
2
- import { transformResult } from '@trpc/server/unstable-core-do-not-import';
3
- import { TRPCClientError } from '../TRPCClientError.mjs';
4
- import { getTransformer } from '../internals/transformer.mjs';
5
- import { resultOf } from './internals/urlWithConnectionParams.mjs';
6
-
7
- const run = (fn)=>fn();
8
- const exponentialBackoff = (attemptIndex)=>attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);
9
- const lazyDefaults = {
10
- enabled: false,
11
- closeMs: 0
12
- };
13
- /**
14
- * @see https://trpc.io/docs/v11/client/links/wsLink
15
- * @deprecated
16
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
17
- * See https://github.com/trpc/trpc/issues/6109
18
- */ function createWSClient(opts) {
19
- const { WebSocket: WebSocketImpl = WebSocket, retryDelayMs: retryDelayFn = exponentialBackoff } = opts;
20
- const lazyOpts = {
21
- ...lazyDefaults,
22
- ...opts.lazy
23
- };
24
- /* istanbul ignore next -- @preserve */ if (!WebSocketImpl) {
25
- throw new Error("No WebSocket implementation found - you probably don't want to use this on the server, but if you do you need to pass a `WebSocket`-ponyfill");
26
- }
27
- /**
28
- * outgoing messages buffer whilst not open
29
- */ let outgoing = [];
30
- const pendingRequests = Object.create(null);
31
- let connectAttempt = 0;
32
- let connectTimer = undefined;
33
- let connectionIndex = 0;
34
- let lazyDisconnectTimer = undefined;
35
- let activeConnection = lazyOpts.enabled ? null : createConnection();
36
- const initState = activeConnection ? {
37
- type: 'state',
38
- state: 'connecting',
39
- error: null
40
- } : {
41
- type: 'state',
42
- state: 'idle',
43
- error: null
44
- };
45
- const connectionState = behaviorSubject(initState);
46
- /**
47
- * tries to send the list of messages
48
- */ function dispatch() {
49
- if (!activeConnection) {
50
- reconnect(null);
51
- return;
52
- }
53
- // using a timeout to batch messages
54
- setTimeout(()=>{
55
- if (activeConnection?.state !== 'open') {
56
- return;
57
- }
58
- for (const pending of Object.values(pendingRequests)){
59
- if (!pending.connection) {
60
- pending.connection = activeConnection;
61
- }
62
- }
63
- if (outgoing.length === 1) {
64
- // single send
65
- activeConnection.ws.send(JSON.stringify(outgoing.pop()));
66
- } else {
67
- // batch send
68
- activeConnection.ws.send(JSON.stringify(outgoing));
69
- }
70
- // clear
71
- outgoing = [];
72
- startLazyDisconnectTimer();
73
- });
74
- }
75
- function tryReconnect(cause) {
76
- if (!!connectTimer) {
77
- return;
78
- }
79
- const timeout = retryDelayFn(connectAttempt++);
80
- reconnectInMs(timeout, cause);
81
- }
82
- function hasPendingRequests(conn) {
83
- const requests = Object.values(pendingRequests);
84
- if (!conn) {
85
- return requests.length > 0;
86
- }
87
- return requests.some((req)=>req.connection === conn);
88
- }
89
- function reconnect(cause) {
90
- if (lazyOpts.enabled && !hasPendingRequests()) {
91
- // Skip reconnecting if there aren't pending requests and we're in lazy mode
92
- return;
93
- }
94
- const oldConnection = activeConnection;
95
- activeConnection = createConnection();
96
- if (oldConnection) {
97
- closeIfNoPending(oldConnection);
98
- }
99
- const currentState = connectionState.get();
100
- if (currentState.state !== 'connecting') {
101
- connectionState.next({
102
- type: 'state',
103
- state: 'connecting',
104
- error: cause ? TRPCClientError.from(cause) : null
105
- });
106
- }
107
- }
108
- function reconnectInMs(ms, cause) {
109
- if (connectTimer) {
110
- return;
111
- }
112
- connectTimer = setTimeout(()=>{
113
- reconnect(cause);
114
- }, ms);
115
- }
116
- function closeIfNoPending(conn) {
117
- // disconnect as soon as there are are no pending requests
118
- if (!hasPendingRequests(conn)) {
119
- conn.ws?.close();
120
- }
121
- }
122
- function resumeSubscriptionOnReconnect(req) {
123
- if (outgoing.some((r)=>r.id === req.op.id)) {
124
- return;
125
- }
126
- request({
127
- op: req.op,
128
- callbacks: req.callbacks,
129
- lastEventId: req.lastEventId
130
- });
131
- }
132
- const startLazyDisconnectTimer = ()=>{
133
- if (!lazyOpts.enabled) {
134
- return;
135
- }
136
- clearTimeout(lazyDisconnectTimer);
137
- lazyDisconnectTimer = setTimeout(()=>{
138
- if (!activeConnection) {
139
- return;
140
- }
141
- if (!hasPendingRequests()) {
142
- activeConnection.ws?.close();
143
- activeConnection = null;
144
- connectionState.next({
145
- type: 'state',
146
- state: 'idle',
147
- error: null
148
- });
149
- }
150
- }, lazyOpts.closeMs);
151
- };
152
- function createConnection() {
153
- let pingTimeout = undefined;
154
- let pongTimeout = undefined;
155
- const self = {
156
- id: ++connectionIndex,
157
- state: 'connecting'
158
- };
159
- clearTimeout(lazyDisconnectTimer);
160
- function destroy() {
161
- const noop = ()=>{
162
- // no-op
163
- };
164
- const { ws } = self;
165
- if (ws) {
166
- ws.onclose = noop;
167
- ws.onerror = noop;
168
- ws.onmessage = noop;
169
- ws.onopen = noop;
170
- ws.close();
171
- }
172
- self.state = 'closed';
173
- }
174
- const onCloseOrError = (cause)=>{
175
- clearTimeout(pingTimeout);
176
- clearTimeout(pongTimeout);
177
- self.state = 'closed';
178
- if (activeConnection === self) {
179
- // connection might have been replaced already
180
- tryReconnect(cause);
181
- }
182
- for (const [key, req] of Object.entries(pendingRequests)){
183
- if (req.connection !== self) {
184
- continue;
185
- }
186
- // The connection was closed either unexpectedly or because of a reconnect
187
- if (req.type === 'subscription') {
188
- // Subscriptions will resume after we've reconnected
189
- resumeSubscriptionOnReconnect(req);
190
- } else {
191
- // Queries and mutations will error if interrupted
192
- delete pendingRequests[key];
193
- req.callbacks.error?.(TRPCClientError.from(cause ?? new TRPCWebSocketClosedError()));
194
- }
195
- }
196
- };
197
- const onError = (evt)=>{
198
- onCloseOrError(new TRPCWebSocketClosedError({
199
- cause: evt
200
- }));
201
- opts.onError?.(evt);
202
- };
203
- function connect(url) {
204
- if (opts.connectionParams) {
205
- // append `?connectionParams=1` when connection params are used
206
- const prefix = url.includes('?') ? '&' : '?';
207
- url += prefix + 'connectionParams=1';
208
- }
209
- const ws = new WebSocketImpl(url);
210
- self.ws = ws;
211
- clearTimeout(connectTimer);
212
- connectTimer = undefined;
213
- ws.onopen = ()=>{
214
- async function sendConnectionParams() {
215
- if (!opts.connectionParams) {
216
- return;
217
- }
218
- const connectMsg = {
219
- method: 'connectionParams',
220
- data: await resultOf(opts.connectionParams)
221
- };
222
- ws.send(JSON.stringify(connectMsg));
223
- }
224
- function handleKeepAlive() {
225
- if (!opts.keepAlive?.enabled) {
226
- return;
227
- }
228
- const { pongTimeoutMs = 1000, intervalMs = 5000 } = opts.keepAlive;
229
- const schedulePing = ()=>{
230
- const schedulePongTimeout = ()=>{
231
- pongTimeout = setTimeout(()=>{
232
- const wasOpen = self.state === 'open';
233
- destroy();
234
- if (wasOpen) {
235
- opts.onClose?.();
236
- }
237
- }, pongTimeoutMs);
238
- };
239
- pingTimeout = setTimeout(()=>{
240
- ws.send('PING');
241
- schedulePongTimeout();
242
- }, intervalMs);
243
- };
244
- ws.addEventListener('message', ()=>{
245
- clearTimeout(pingTimeout);
246
- clearTimeout(pongTimeout);
247
- schedulePing();
248
- });
249
- schedulePing();
250
- }
251
- run(async ()=>{
252
- /* istanbul ignore next -- @preserve */ if (activeConnection?.ws !== ws) {
253
- return;
254
- }
255
- handleKeepAlive();
256
- await sendConnectionParams();
257
- connectAttempt = 0;
258
- self.state = 'open';
259
- // Update connection state
260
- connectionState.next({
261
- type: 'state',
262
- state: 'pending',
263
- error: null
264
- });
265
- opts.onOpen?.();
266
- dispatch();
267
- }).catch((cause)=>{
268
- ws.close(// "Status codes in the range 3000-3999 are reserved for use by libraries, frameworks, and applications"
269
- 3000);
270
- onCloseOrError(new TRPCWebSocketClosedError({
271
- message: 'Initialization error',
272
- cause
273
- }));
274
- });
275
- };
276
- ws.onerror = onError;
277
- const handleIncomingRequest = (req)=>{
278
- if (self !== activeConnection) {
279
- return;
280
- }
281
- if (req.method === 'reconnect') {
282
- reconnect(new TRPCWebSocketClosedError({
283
- message: 'Server requested reconnect'
284
- }));
285
- // notify subscribers
286
- for (const pendingReq of Object.values(pendingRequests)){
287
- if (pendingReq.type === 'subscription') {
288
- resumeSubscriptionOnReconnect(pendingReq);
289
- }
290
- }
291
- }
292
- };
293
- const handleIncomingResponse = (data)=>{
294
- const req = data.id !== null && pendingRequests[data.id];
295
- if (!req) {
296
- // do something?
297
- return;
298
- }
299
- req.callbacks.next?.(data);
300
- if (self === activeConnection && req.connection !== activeConnection) {
301
- // gracefully replace old connection with a new connection
302
- req.connection = self;
303
- }
304
- if (req.connection !== self) {
305
- // the connection has been replaced
306
- return;
307
- }
308
- if ('result' in data && data.result.type === 'data' && typeof data.result.id === 'string') {
309
- req.lastEventId = data.result.id;
310
- }
311
- if ('result' in data && data.result.type === 'stopped' && activeConnection === self) {
312
- req.callbacks.complete();
313
- }
314
- };
315
- ws.onmessage = (event)=>{
316
- const { data } = event;
317
- if (data === 'PONG') {
318
- return;
319
- }
320
- if (data === 'PING') {
321
- ws.send('PONG');
322
- return;
323
- }
324
- startLazyDisconnectTimer();
325
- const msg = JSON.parse(data);
326
- if ('method' in msg) {
327
- handleIncomingRequest(msg);
328
- } else {
329
- handleIncomingResponse(msg);
330
- }
331
- if (self !== activeConnection) {
332
- // when receiving a message, we close old connection that has no pending requests
333
- closeIfNoPending(self);
334
- }
335
- };
336
- ws.onclose = (event)=>{
337
- const wasOpen = self.state === 'open';
338
- destroy();
339
- onCloseOrError(new TRPCWebSocketClosedError({
340
- cause: event
341
- }));
342
- if (wasOpen) {
343
- opts.onClose?.(event);
344
- }
345
- };
346
- }
347
- Promise.resolve(resultOf(opts.url)).then(connect).catch(()=>{
348
- onCloseOrError(new Error('Failed to resolve url'));
349
- });
350
- return self;
351
- }
352
- function request(opts) {
353
- const { op, callbacks, lastEventId } = opts;
354
- const { type, input, path, id } = op;
355
- const envelope = {
356
- id,
357
- method: type,
358
- params: {
359
- input,
360
- path,
361
- lastEventId
362
- }
363
- };
364
- pendingRequests[id] = {
365
- connection: null,
366
- type,
367
- callbacks,
368
- op,
369
- lastEventId
370
- };
371
- // enqueue message
372
- outgoing.push(envelope);
373
- dispatch();
374
- return ()=>{
375
- const callbacks = pendingRequests[id]?.callbacks;
376
- delete pendingRequests[id];
377
- outgoing = outgoing.filter((msg)=>msg.id !== id);
378
- callbacks?.complete?.();
379
- if (activeConnection?.state === 'open' && op.type === 'subscription') {
380
- outgoing.push({
381
- id,
382
- method: 'subscription.stop'
383
- });
384
- dispatch();
385
- }
386
- startLazyDisconnectTimer();
387
- };
388
- }
389
- return {
390
- close: ()=>{
391
- connectAttempt = 0;
392
- for (const req of Object.values(pendingRequests)){
393
- if (req.type === 'subscription') {
394
- req.callbacks.complete();
395
- } else if (!req.connection) {
396
- // close pending requests that aren't attached to a connection yet
397
- req.callbacks.error(TRPCClientError.from(new TRPCWebSocketClosedError({
398
- message: 'Closed before connection was established'
399
- })));
400
- }
401
- }
402
- if (activeConnection) {
403
- closeIfNoPending(activeConnection);
404
- }
405
- clearTimeout(connectTimer);
406
- connectTimer = undefined;
407
- activeConnection = null;
408
- },
409
- request,
410
- get connection () {
411
- return activeConnection;
412
- },
413
- /**
414
- * Reconnect to the WebSocket server
415
- */ reconnect,
416
- connectionState: connectionState
417
- };
418
- }
419
- class TRPCWebSocketClosedError extends Error {
420
- constructor(opts){
421
- super(opts?.message ?? 'WebSocket closed', // eslint-disable-next-line @typescript-eslint/ban-ts-comment
422
- // @ts-ignore https://github.com/tc39/proposal-error-cause
423
- {
424
- cause: opts?.cause
425
- });
426
- this.name = 'TRPCWebSocketClosedError';
427
- Object.setPrototypeOf(this, TRPCWebSocketClosedError.prototype);
428
- }
429
- }
430
- /**
431
- * @see https://trpc.io/docs/v11/client/links/wsLink
432
- * @deprecated
433
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
434
- * See https://github.com/trpc/trpc/issues/6109
435
- */ function wsLink(opts) {
436
- const transformer = getTransformer(opts.transformer);
437
- return ()=>{
438
- const { client } = opts;
439
- return ({ op })=>{
440
- return observable((observer)=>{
441
- const { type, path, id, context } = op;
442
- const input = transformer.input.serialize(op.input);
443
- const connState = type === 'subscription' ? client.connectionState.subscribe({
444
- next (result) {
445
- observer.next({
446
- result,
447
- context
448
- });
449
- }
450
- }) : null;
451
- const unsubscribeRequest = client.request({
452
- op: {
453
- type,
454
- path,
455
- input,
456
- id,
457
- context,
458
- signal: null
459
- },
460
- callbacks: {
461
- error (err) {
462
- observer.error(err);
463
- unsubscribeRequest();
464
- },
465
- complete () {
466
- observer.complete();
467
- },
468
- next (event) {
469
- const transformed = transformResult(event, transformer.output);
470
- if (!transformed.ok) {
471
- observer.error(TRPCClientError.from(transformed.error));
472
- return;
473
- }
474
- observer.next({
475
- result: transformed.result
476
- });
477
- if (op.type !== 'subscription') {
478
- // if it isn't a subscription we don't care about next response
479
- unsubscribeRequest();
480
- observer.complete();
481
- }
482
- }
483
- },
484
- lastEventId: undefined
485
- });
486
- return ()=>{
487
- unsubscribeRequest();
488
- connState?.unsubscribe();
489
- };
490
- });
491
- };
492
- };
493
- }
494
-
495
- export { createWSClient, wsLink };
@@ -1 +0,0 @@
1
- export * from '../../dist/links/wsLink';
@@ -1 +0,0 @@
1
- module.exports = require('../../dist/links/wsLink');