@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
@@ -0,0 +1,55 @@
1
+ import { observable } from '@trpc/server/observable';
2
+ import type {
3
+ AnyRouter,
4
+ inferClientTypes,
5
+ } from '@trpc/server/unstable-core-do-not-import';
6
+ import type { TransformerOptions } from '../../unstable-internals';
7
+ import { getTransformer } from '../../unstable-internals';
8
+ import type { TRPCLink } from '../types';
9
+ import type {
10
+ TRPCWebSocketClient,
11
+ WebSocketClientOptions,
12
+ } from './createWsClient';
13
+ import { createWSClient } from './createWsClient';
14
+
15
+ export type WebSocketLinkOptions<TRouter extends AnyRouter> = {
16
+ client: TRPCWebSocketClient;
17
+ } & TransformerOptions<inferClientTypes<TRouter>>;
18
+
19
+ export function wsLink<TRouter extends AnyRouter>(
20
+ opts: WebSocketLinkOptions<TRouter>,
21
+ ): TRPCLink<TRouter> {
22
+ const { client } = opts;
23
+ const transformer = getTransformer(opts.transformer);
24
+ return () => {
25
+ return ({ op }) => {
26
+ return observable((observer) => {
27
+ const connStateSubscription =
28
+ op.type === 'subscription'
29
+ ? client.connectionState.subscribe({
30
+ next(result) {
31
+ observer.next({
32
+ result,
33
+ context: op.context,
34
+ });
35
+ },
36
+ })
37
+ : null;
38
+
39
+ const requestSubscription = client
40
+ .request({
41
+ op,
42
+ transformer,
43
+ })
44
+ .subscribe(observer);
45
+
46
+ return () => {
47
+ requestSubscription.unsubscribe();
48
+ connStateSubscription?.unsubscribe();
49
+ };
50
+ });
51
+ };
52
+ };
53
+ }
54
+
55
+ export { TRPCWebSocketClient, WebSocketClientOptions, createWSClient };
package/src/links.ts CHANGED
@@ -6,7 +6,7 @@ export * from './links/httpBatchStreamLink';
6
6
  export * from './links/httpLink';
7
7
  export * from './links/loggerLink';
8
8
  export * from './links/splitLink';
9
- export * from './links/wsLink';
9
+ export * from './links/wsLink/wsLink';
10
10
  export * from './links/httpSubscriptionLink';
11
11
  export * from './links/retryLink';
12
12
 
@@ -1,125 +0,0 @@
1
- import type { Observer, UnsubscribeFn } from '@trpc/server/observable';
2
- import type { AnyRouter, inferClientTypes, inferRouterError, TRPCResponseMessage } from '@trpc/server/unstable-core-do-not-import';
3
- import { TRPCClientError } from '../TRPCClientError';
4
- import type { TransformerOptions } from '../unstable-internals';
5
- import type { TRPCConnectionState } from './internals/subscriptions';
6
- import { type UrlOptionsWithConnectionParams } from './internals/urlWithConnectionParams';
7
- import type { Operation, TRPCLink } from './types';
8
- type WSCallbackResult<TRouter extends AnyRouter, TOutput> = TRPCResponseMessage<TOutput, inferRouterError<TRouter>>;
9
- type WSCallbackObserver<TRouter extends AnyRouter, TOutput> = Observer<WSCallbackResult<TRouter, TOutput>, TRPCClientError<TRouter>>;
10
- declare const exponentialBackoff: (attemptIndex: number) => number;
11
- export interface WebSocketClientOptions extends UrlOptionsWithConnectionParams {
12
- /**
13
- * Ponyfill which WebSocket implementation to use
14
- */
15
- WebSocket?: typeof WebSocket;
16
- /**
17
- * The number of milliseconds before a reconnect is attempted.
18
- * @default {@link exponentialBackoff}
19
- */
20
- retryDelayMs?: typeof exponentialBackoff;
21
- /**
22
- * Triggered when a WebSocket connection is established
23
- */
24
- onOpen?: () => void;
25
- /**
26
- * Triggered when a WebSocket connection encounters an error
27
- */
28
- onError?: (evt?: Event) => void;
29
- /**
30
- * Triggered when a WebSocket connection is closed
31
- */
32
- onClose?: (cause?: {
33
- code?: number;
34
- }) => void;
35
- /**
36
- * Lazy mode will close the WebSocket automatically after a period of inactivity (no messages sent or received and no pending requests)
37
- */
38
- lazy?: {
39
- /**
40
- * Enable lazy mode
41
- * @default false
42
- */
43
- enabled: boolean;
44
- /**
45
- * Close the WebSocket after this many milliseconds
46
- * @default 0
47
- */
48
- closeMs: number;
49
- };
50
- /**
51
- * Send ping messages to the server and kill the connection if no pong message is returned
52
- */
53
- keepAlive?: {
54
- /**
55
- * @default false
56
- */
57
- enabled: boolean;
58
- /**
59
- * Send a ping message every this many milliseconds
60
- * @default 5_000
61
- */
62
- intervalMs?: number;
63
- /**
64
- * Close the WebSocket after this many milliseconds if the server does not respond
65
- * @default 1_000
66
- */
67
- pongTimeoutMs?: number;
68
- };
69
- }
70
- /**
71
- * @see https://trpc.io/docs/v11/client/links/wsLink
72
- * @deprecated
73
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
74
- * See https://github.com/trpc/trpc/issues/6109
75
- */
76
- export declare function createWSClient(opts: WebSocketClientOptions): {
77
- close: () => void;
78
- request: (opts: {
79
- op: Operation;
80
- callbacks: WSCallbackObserver<AnyRouter, unknown>;
81
- lastEventId: string | undefined;
82
- }) => UnsubscribeFn;
83
- readonly connection: ({
84
- id: number;
85
- } & ({
86
- state: "open";
87
- ws: WebSocket;
88
- } | {
89
- state: "closed";
90
- ws: WebSocket;
91
- } | {
92
- state: "connecting";
93
- ws?: WebSocket;
94
- })) | null;
95
- /**
96
- * Reconnect to the WebSocket server
97
- */
98
- reconnect: (cause: Error | null) => void;
99
- connectionState: import("@trpc/server/observable").BehaviorSubject<TRPCConnectionState<TRPCClientError<AnyRouter>>>;
100
- };
101
- /**
102
- * @see https://trpc.io/docs/v11/client/links/wsLink
103
- * @deprecated
104
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
105
- * See https://github.com/trpc/trpc/issues/6109
106
- */
107
- export type TRPCWebSocketClient = ReturnType<typeof createWSClient>;
108
- /**
109
- * @see https://trpc.io/docs/v11/client/links/wsLink
110
- * @deprecated
111
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
112
- * See https://github.com/trpc/trpc/issues/6109
113
- */
114
- export type WebSocketLinkOptions<TRouter extends AnyRouter> = {
115
- client: TRPCWebSocketClient;
116
- } & TransformerOptions<inferClientTypes<TRouter>>;
117
- /**
118
- * @see https://trpc.io/docs/v11/client/links/wsLink
119
- * @deprecated
120
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
121
- * See https://github.com/trpc/trpc/issues/6109
122
- */
123
- export declare function wsLink<TRouter extends AnyRouter>(opts: WebSocketLinkOptions<TRouter>): TRPCLink<TRouter>;
124
- export {};
125
- //# sourceMappingURL=wsLink.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wsLink.d.ts","sourceRoot":"","sources":["../../src/links/wsLink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGvE,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAMhB,mBAAmB,EACpB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInD,KAAK,gBAAgB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,mBAAmB,CAC7E,OAAO,EACP,gBAAgB,CAAC,OAAO,CAAC,CAC1B,CAAC;AAEF,KAAK,kBAAkB,CAAC,OAAO,SAAS,SAAS,EAAE,OAAO,IAAI,QAAQ,CACpE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAClC,eAAe,CAAC,OAAO,CAAC,CACzB,CAAC;AAEF,QAAA,MAAM,kBAAkB,iBAAkB,MAAM,WACoB,CAAC;AAErE,MAAM,WAAW,sBAAuB,SAAQ,8BAA8B;IAC5E;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,kBAAkB,CAAC;IACzC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAChC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9C;;OAEG;IACH,IAAI,CAAC,EAAE;QACL;;;WAGG;QACH,OAAO,EAAE,OAAO,CAAC;QACjB;;;WAGG;QACH,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF;;OAEG;IACH,SAAS,CAAC,EAAE;QACV;;WAEG;QACH,OAAO,EAAE,OAAO,CAAC;QACjB;;;WAGG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB;;;WAGG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAQD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,sBAAsB;;oBA2blC;QACrB,EAAE,EAAE,SAAS,CAAC;QACd,SAAS,yCAAa;QACtB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;KACjC,KAAG,aAAa;;YA9YX,MAAM;;eAGC,MAAM;YACT,SAAS;;eAGN,QAAQ;YACX,SAAS;;eAGN,YAAY;aACd,SAAS;;IA0clB;;OAEG;uBA1YqB,KAAK,GAAG,IAAI;;EA8YvC;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,SAAS,IAAI;IAC5D,MAAM,EAAE,mBAAmB,CAAC;CAC7B,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAgBlD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,OAAO,SAAS,SAAS,EAC9C,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAClC,QAAQ,CAAC,OAAO,CAAC,CA2DnB"}
@@ -1,498 +0,0 @@
1
- 'use strict';
2
-
3
- var observable = require('@trpc/server/observable');
4
- var unstableCoreDoNotImport = require('@trpc/server/unstable-core-do-not-import');
5
- var TRPCClientError = require('../TRPCClientError.js');
6
- var transformer = require('../internals/transformer.js');
7
- var urlWithConnectionParams = require('./internals/urlWithConnectionParams.js');
8
-
9
- const run = (fn)=>fn();
10
- const exponentialBackoff = (attemptIndex)=>attemptIndex === 0 ? 0 : Math.min(1000 * 2 ** attemptIndex, 30000);
11
- const lazyDefaults = {
12
- enabled: false,
13
- closeMs: 0
14
- };
15
- /**
16
- * @see https://trpc.io/docs/v11/client/links/wsLink
17
- * @deprecated
18
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
19
- * See https://github.com/trpc/trpc/issues/6109
20
- */ function createWSClient(opts) {
21
- const { WebSocket: WebSocketImpl = WebSocket, retryDelayMs: retryDelayFn = exponentialBackoff } = opts;
22
- const lazyOpts = {
23
- ...lazyDefaults,
24
- ...opts.lazy
25
- };
26
- /* istanbul ignore next -- @preserve */ if (!WebSocketImpl) {
27
- 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");
28
- }
29
- /**
30
- * outgoing messages buffer whilst not open
31
- */ let outgoing = [];
32
- const pendingRequests = Object.create(null);
33
- let connectAttempt = 0;
34
- let connectTimer = undefined;
35
- let connectionIndex = 0;
36
- let lazyDisconnectTimer = undefined;
37
- let activeConnection = lazyOpts.enabled ? null : createConnection();
38
- const initState = activeConnection ? {
39
- type: 'state',
40
- state: 'connecting',
41
- error: null
42
- } : {
43
- type: 'state',
44
- state: 'idle',
45
- error: null
46
- };
47
- const connectionState = observable.behaviorSubject(initState);
48
- /**
49
- * tries to send the list of messages
50
- */ function dispatch() {
51
- if (!activeConnection) {
52
- reconnect(null);
53
- return;
54
- }
55
- // using a timeout to batch messages
56
- setTimeout(()=>{
57
- if (activeConnection?.state !== 'open') {
58
- return;
59
- }
60
- for (const pending of Object.values(pendingRequests)){
61
- if (!pending.connection) {
62
- pending.connection = activeConnection;
63
- }
64
- }
65
- if (outgoing.length === 1) {
66
- // single send
67
- activeConnection.ws.send(JSON.stringify(outgoing.pop()));
68
- } else {
69
- // batch send
70
- activeConnection.ws.send(JSON.stringify(outgoing));
71
- }
72
- // clear
73
- outgoing = [];
74
- startLazyDisconnectTimer();
75
- });
76
- }
77
- function tryReconnect(cause) {
78
- if (!!connectTimer) {
79
- return;
80
- }
81
- const timeout = retryDelayFn(connectAttempt++);
82
- reconnectInMs(timeout, cause);
83
- }
84
- function hasPendingRequests(conn) {
85
- const requests = Object.values(pendingRequests);
86
- if (!conn) {
87
- return requests.length > 0;
88
- }
89
- return requests.some((req)=>req.connection === conn);
90
- }
91
- function reconnect(cause) {
92
- if (lazyOpts.enabled && !hasPendingRequests()) {
93
- // Skip reconnecting if there aren't pending requests and we're in lazy mode
94
- return;
95
- }
96
- const oldConnection = activeConnection;
97
- activeConnection = createConnection();
98
- if (oldConnection) {
99
- closeIfNoPending(oldConnection);
100
- }
101
- const currentState = connectionState.get();
102
- if (currentState.state !== 'connecting') {
103
- connectionState.next({
104
- type: 'state',
105
- state: 'connecting',
106
- error: cause ? TRPCClientError.TRPCClientError.from(cause) : null
107
- });
108
- }
109
- }
110
- function reconnectInMs(ms, cause) {
111
- if (connectTimer) {
112
- return;
113
- }
114
- connectTimer = setTimeout(()=>{
115
- reconnect(cause);
116
- }, ms);
117
- }
118
- function closeIfNoPending(conn) {
119
- // disconnect as soon as there are are no pending requests
120
- if (!hasPendingRequests(conn)) {
121
- conn.ws?.close();
122
- }
123
- }
124
- function resumeSubscriptionOnReconnect(req) {
125
- if (outgoing.some((r)=>r.id === req.op.id)) {
126
- return;
127
- }
128
- request({
129
- op: req.op,
130
- callbacks: req.callbacks,
131
- lastEventId: req.lastEventId
132
- });
133
- }
134
- const startLazyDisconnectTimer = ()=>{
135
- if (!lazyOpts.enabled) {
136
- return;
137
- }
138
- clearTimeout(lazyDisconnectTimer);
139
- lazyDisconnectTimer = setTimeout(()=>{
140
- if (!activeConnection) {
141
- return;
142
- }
143
- if (!hasPendingRequests()) {
144
- activeConnection.ws?.close();
145
- activeConnection = null;
146
- connectionState.next({
147
- type: 'state',
148
- state: 'idle',
149
- error: null
150
- });
151
- }
152
- }, lazyOpts.closeMs);
153
- };
154
- function createConnection() {
155
- let pingTimeout = undefined;
156
- let pongTimeout = undefined;
157
- const self = {
158
- id: ++connectionIndex,
159
- state: 'connecting'
160
- };
161
- clearTimeout(lazyDisconnectTimer);
162
- function destroy() {
163
- const noop = ()=>{
164
- // no-op
165
- };
166
- const { ws } = self;
167
- if (ws) {
168
- ws.onclose = noop;
169
- ws.onerror = noop;
170
- ws.onmessage = noop;
171
- ws.onopen = noop;
172
- ws.close();
173
- }
174
- self.state = 'closed';
175
- }
176
- const onCloseOrError = (cause)=>{
177
- clearTimeout(pingTimeout);
178
- clearTimeout(pongTimeout);
179
- self.state = 'closed';
180
- if (activeConnection === self) {
181
- // connection might have been replaced already
182
- tryReconnect(cause);
183
- }
184
- for (const [key, req] of Object.entries(pendingRequests)){
185
- if (req.connection !== self) {
186
- continue;
187
- }
188
- // The connection was closed either unexpectedly or because of a reconnect
189
- if (req.type === 'subscription') {
190
- // Subscriptions will resume after we've reconnected
191
- resumeSubscriptionOnReconnect(req);
192
- } else {
193
- // Queries and mutations will error if interrupted
194
- delete pendingRequests[key];
195
- req.callbacks.error?.(TRPCClientError.TRPCClientError.from(cause ?? new TRPCWebSocketClosedError()));
196
- }
197
- }
198
- };
199
- const onError = (evt)=>{
200
- onCloseOrError(new TRPCWebSocketClosedError({
201
- cause: evt
202
- }));
203
- opts.onError?.(evt);
204
- };
205
- function connect(url) {
206
- if (opts.connectionParams) {
207
- // append `?connectionParams=1` when connection params are used
208
- const prefix = url.includes('?') ? '&' : '?';
209
- url += prefix + 'connectionParams=1';
210
- }
211
- const ws = new WebSocketImpl(url);
212
- self.ws = ws;
213
- clearTimeout(connectTimer);
214
- connectTimer = undefined;
215
- ws.onopen = ()=>{
216
- async function sendConnectionParams() {
217
- if (!opts.connectionParams) {
218
- return;
219
- }
220
- const connectMsg = {
221
- method: 'connectionParams',
222
- data: await urlWithConnectionParams.resultOf(opts.connectionParams)
223
- };
224
- ws.send(JSON.stringify(connectMsg));
225
- }
226
- function handleKeepAlive() {
227
- if (!opts.keepAlive?.enabled) {
228
- return;
229
- }
230
- const { pongTimeoutMs = 1000, intervalMs = 5000 } = opts.keepAlive;
231
- const schedulePing = ()=>{
232
- const schedulePongTimeout = ()=>{
233
- pongTimeout = setTimeout(()=>{
234
- const wasOpen = self.state === 'open';
235
- destroy();
236
- if (wasOpen) {
237
- opts.onClose?.();
238
- }
239
- }, pongTimeoutMs);
240
- };
241
- pingTimeout = setTimeout(()=>{
242
- ws.send('PING');
243
- schedulePongTimeout();
244
- }, intervalMs);
245
- };
246
- ws.addEventListener('message', ()=>{
247
- clearTimeout(pingTimeout);
248
- clearTimeout(pongTimeout);
249
- schedulePing();
250
- });
251
- schedulePing();
252
- }
253
- run(async ()=>{
254
- /* istanbul ignore next -- @preserve */ if (activeConnection?.ws !== ws) {
255
- return;
256
- }
257
- handleKeepAlive();
258
- await sendConnectionParams();
259
- connectAttempt = 0;
260
- self.state = 'open';
261
- // Update connection state
262
- connectionState.next({
263
- type: 'state',
264
- state: 'pending',
265
- error: null
266
- });
267
- opts.onOpen?.();
268
- dispatch();
269
- }).catch((cause)=>{
270
- ws.close(// "Status codes in the range 3000-3999 are reserved for use by libraries, frameworks, and applications"
271
- 3000);
272
- onCloseOrError(new TRPCWebSocketClosedError({
273
- message: 'Initialization error',
274
- cause
275
- }));
276
- });
277
- };
278
- ws.onerror = onError;
279
- const handleIncomingRequest = (req)=>{
280
- if (self !== activeConnection) {
281
- return;
282
- }
283
- if (req.method === 'reconnect') {
284
- reconnect(new TRPCWebSocketClosedError({
285
- message: 'Server requested reconnect'
286
- }));
287
- // notify subscribers
288
- for (const pendingReq of Object.values(pendingRequests)){
289
- if (pendingReq.type === 'subscription') {
290
- resumeSubscriptionOnReconnect(pendingReq);
291
- }
292
- }
293
- }
294
- };
295
- const handleIncomingResponse = (data)=>{
296
- const req = data.id !== null && pendingRequests[data.id];
297
- if (!req) {
298
- // do something?
299
- return;
300
- }
301
- req.callbacks.next?.(data);
302
- if (self === activeConnection && req.connection !== activeConnection) {
303
- // gracefully replace old connection with a new connection
304
- req.connection = self;
305
- }
306
- if (req.connection !== self) {
307
- // the connection has been replaced
308
- return;
309
- }
310
- if ('result' in data && data.result.type === 'data' && typeof data.result.id === 'string') {
311
- req.lastEventId = data.result.id;
312
- }
313
- if ('result' in data && data.result.type === 'stopped' && activeConnection === self) {
314
- req.callbacks.complete();
315
- }
316
- };
317
- ws.onmessage = (event)=>{
318
- const { data } = event;
319
- if (data === 'PONG') {
320
- return;
321
- }
322
- if (data === 'PING') {
323
- ws.send('PONG');
324
- return;
325
- }
326
- startLazyDisconnectTimer();
327
- const msg = JSON.parse(data);
328
- if ('method' in msg) {
329
- handleIncomingRequest(msg);
330
- } else {
331
- handleIncomingResponse(msg);
332
- }
333
- if (self !== activeConnection) {
334
- // when receiving a message, we close old connection that has no pending requests
335
- closeIfNoPending(self);
336
- }
337
- };
338
- ws.onclose = (event)=>{
339
- const wasOpen = self.state === 'open';
340
- destroy();
341
- onCloseOrError(new TRPCWebSocketClosedError({
342
- cause: event
343
- }));
344
- if (wasOpen) {
345
- opts.onClose?.(event);
346
- }
347
- };
348
- }
349
- Promise.resolve(urlWithConnectionParams.resultOf(opts.url)).then(connect).catch(()=>{
350
- onCloseOrError(new Error('Failed to resolve url'));
351
- });
352
- return self;
353
- }
354
- function request(opts) {
355
- const { op, callbacks, lastEventId } = opts;
356
- const { type, input, path, id } = op;
357
- const envelope = {
358
- id,
359
- method: type,
360
- params: {
361
- input,
362
- path,
363
- lastEventId
364
- }
365
- };
366
- pendingRequests[id] = {
367
- connection: null,
368
- type,
369
- callbacks,
370
- op,
371
- lastEventId
372
- };
373
- // enqueue message
374
- outgoing.push(envelope);
375
- dispatch();
376
- return ()=>{
377
- const callbacks = pendingRequests[id]?.callbacks;
378
- delete pendingRequests[id];
379
- outgoing = outgoing.filter((msg)=>msg.id !== id);
380
- callbacks?.complete?.();
381
- if (activeConnection?.state === 'open' && op.type === 'subscription') {
382
- outgoing.push({
383
- id,
384
- method: 'subscription.stop'
385
- });
386
- dispatch();
387
- }
388
- startLazyDisconnectTimer();
389
- };
390
- }
391
- return {
392
- close: ()=>{
393
- connectAttempt = 0;
394
- for (const req of Object.values(pendingRequests)){
395
- if (req.type === 'subscription') {
396
- req.callbacks.complete();
397
- } else if (!req.connection) {
398
- // close pending requests that aren't attached to a connection yet
399
- req.callbacks.error(TRPCClientError.TRPCClientError.from(new TRPCWebSocketClosedError({
400
- message: 'Closed before connection was established'
401
- })));
402
- }
403
- }
404
- if (activeConnection) {
405
- closeIfNoPending(activeConnection);
406
- }
407
- clearTimeout(connectTimer);
408
- connectTimer = undefined;
409
- activeConnection = null;
410
- },
411
- request,
412
- get connection () {
413
- return activeConnection;
414
- },
415
- /**
416
- * Reconnect to the WebSocket server
417
- */ reconnect,
418
- connectionState: connectionState
419
- };
420
- }
421
- class TRPCWebSocketClosedError extends Error {
422
- constructor(opts){
423
- super(opts?.message ?? 'WebSocket closed', // eslint-disable-next-line @typescript-eslint/ban-ts-comment
424
- // @ts-ignore https://github.com/tc39/proposal-error-cause
425
- {
426
- cause: opts?.cause
427
- });
428
- this.name = 'TRPCWebSocketClosedError';
429
- Object.setPrototypeOf(this, TRPCWebSocketClosedError.prototype);
430
- }
431
- }
432
- /**
433
- * @see https://trpc.io/docs/v11/client/links/wsLink
434
- * @deprecated
435
- * 🙋‍♂️ **Contributors needed** to continue supporting WebSockets!
436
- * See https://github.com/trpc/trpc/issues/6109
437
- */ function wsLink(opts) {
438
- const transformer$1 = transformer.getTransformer(opts.transformer);
439
- return ()=>{
440
- const { client } = opts;
441
- return ({ op })=>{
442
- return observable.observable((observer)=>{
443
- const { type, path, id, context } = op;
444
- const input = transformer$1.input.serialize(op.input);
445
- const connState = type === 'subscription' ? client.connectionState.subscribe({
446
- next (result) {
447
- observer.next({
448
- result,
449
- context
450
- });
451
- }
452
- }) : null;
453
- const unsubscribeRequest = client.request({
454
- op: {
455
- type,
456
- path,
457
- input,
458
- id,
459
- context,
460
- signal: null
461
- },
462
- callbacks: {
463
- error (err) {
464
- observer.error(err);
465
- unsubscribeRequest();
466
- },
467
- complete () {
468
- observer.complete();
469
- },
470
- next (event) {
471
- const transformed = unstableCoreDoNotImport.transformResult(event, transformer$1.output);
472
- if (!transformed.ok) {
473
- observer.error(TRPCClientError.TRPCClientError.from(transformed.error));
474
- return;
475
- }
476
- observer.next({
477
- result: transformed.result
478
- });
479
- if (op.type !== 'subscription') {
480
- // if it isn't a subscription we don't care about next response
481
- unsubscribeRequest();
482
- observer.complete();
483
- }
484
- }
485
- },
486
- lastEventId: undefined
487
- });
488
- return ()=>{
489
- unsubscribeRequest();
490
- connState?.unsubscribe();
491
- };
492
- });
493
- };
494
- };
495
- }
496
-
497
- exports.createWSClient = createWSClient;
498
- exports.wsLink = wsLink;