@trpc/client 11.0.0-rc.413 → 11.0.0-rc.419

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 (46) hide show
  1. package/dist/bundle-analysis.json +55 -40
  2. package/dist/createTRPCClient.d.ts.map +1 -1
  3. package/dist/createTRPCClient.js +9 -9
  4. package/dist/createTRPCClient.mjs +10 -10
  5. package/dist/index.js +2 -0
  6. package/dist/index.mjs +1 -0
  7. package/dist/internals/TRPCUntypedClient.d.ts +3 -1
  8. package/dist/internals/TRPCUntypedClient.d.ts.map +1 -1
  9. package/dist/internals/TRPCUntypedClient.js +3 -1
  10. package/dist/internals/TRPCUntypedClient.mjs +3 -1
  11. package/dist/internals/dataLoader.d.ts.map +1 -1
  12. package/dist/links/httpBatchLink.d.ts.map +1 -1
  13. package/dist/links/httpBatchLink.js +3 -2
  14. package/dist/links/httpBatchLink.mjs +3 -2
  15. package/dist/links/httpBatchStreamLink.d.ts.map +1 -1
  16. package/dist/links/httpBatchStreamLink.js +5 -6
  17. package/dist/links/httpBatchStreamLink.mjs +5 -6
  18. package/dist/links/httpLink.d.ts +2 -2
  19. package/dist/links/httpLink.d.ts.map +1 -1
  20. package/dist/links/httpLink.js +3 -0
  21. package/dist/links/httpLink.mjs +3 -0
  22. package/dist/links/httpSubscriptionLink.d.ts +19 -0
  23. package/dist/links/httpSubscriptionLink.d.ts.map +1 -0
  24. package/dist/links/httpSubscriptionLink.js +87 -0
  25. package/dist/links/httpSubscriptionLink.mjs +85 -0
  26. package/dist/links/internals/contentTypes.d.ts +3 -3
  27. package/dist/links/internals/contentTypes.d.ts.map +1 -1
  28. package/dist/links/internals/httpUtils.d.ts +4 -4
  29. package/dist/links/internals/httpUtils.d.ts.map +1 -1
  30. package/dist/links/internals/httpUtils.js +6 -7
  31. package/dist/links/internals/httpUtils.mjs +6 -7
  32. package/dist/links/loggerLink.d.ts +0 -1
  33. package/dist/links/loggerLink.d.ts.map +1 -1
  34. package/dist/links/wsLink.d.ts +4 -4
  35. package/dist/links/wsLink.d.ts.map +1 -1
  36. package/dist/links.d.ts +1 -0
  37. package/dist/links.d.ts.map +1 -1
  38. package/package.json +4 -4
  39. package/src/createTRPCClient.ts +11 -8
  40. package/src/internals/TRPCUntypedClient.ts +4 -2
  41. package/src/links/httpBatchLink.ts +8 -5
  42. package/src/links/httpBatchStreamLink.ts +10 -7
  43. package/src/links/httpLink.ts +8 -2
  44. package/src/links/httpSubscriptionLink.ts +121 -0
  45. package/src/links/internals/httpUtils.ts +10 -11
  46. package/src/links.ts +1 -0
@@ -0,0 +1,121 @@
1
+ import { observable } from '@trpc/server/observable';
2
+ import type {
3
+ AnyClientTypes,
4
+ inferClientTypes,
5
+ InferrableClientTypes,
6
+ MaybePromise,
7
+ SSEvent,
8
+ } from '@trpc/server/unstable-core-do-not-import';
9
+ import {
10
+ run,
11
+ sseStreamConsumer,
12
+ } from '@trpc/server/unstable-core-do-not-import';
13
+ import { TRPCClientError } from '../TRPCClientError';
14
+ import { getTransformer, type TransformerOptions } from '../unstable-internals';
15
+ import { getUrl } from './internals/httpUtils';
16
+ import type { TRPCLink } from './types';
17
+
18
+ type HTTPSubscriptionLinkOptions<TRoot extends AnyClientTypes> = {
19
+ /**
20
+ * The URL to connect to (can be a function that returns a URL)
21
+ */
22
+ url: string | (() => MaybePromise<string>);
23
+ /**
24
+ * EventSource options
25
+ */
26
+ eventSourceOptions?: EventSourceInit;
27
+ } & TransformerOptions<TRoot>;
28
+
29
+ /**
30
+ * Get the result of a value or function that returns a value
31
+ */
32
+ const resultOf = <T>(value: T | (() => T)): T => {
33
+ return typeof value === 'function' ? (value as () => T)() : value;
34
+ };
35
+
36
+ /**
37
+ * @see https://trpc.io/docs/client/links/httpSubscriptionLink
38
+ */
39
+ export function unstable_httpSubscriptionLink<
40
+ TInferrable extends InferrableClientTypes,
41
+ >(
42
+ opts: HTTPSubscriptionLinkOptions<inferClientTypes<TInferrable>>,
43
+ ): TRPCLink<TInferrable> {
44
+ const transformer = getTransformer(opts.transformer);
45
+ return () => {
46
+ return ({ op }) => {
47
+ return observable((observer) => {
48
+ const { type, path, input } = op;
49
+ /* istanbul ignore if -- @preserve */
50
+ if (type !== 'subscription') {
51
+ throw new Error('httpSubscriptionLink only supports subscriptions');
52
+ }
53
+
54
+ let eventSource: EventSource | null = null;
55
+ let unsubscribed = false;
56
+
57
+ run(async () => {
58
+ const url = getUrl({
59
+ transformer,
60
+ url: await resultOf(opts.url),
61
+ input,
62
+ path,
63
+ type,
64
+ AbortController: null,
65
+ });
66
+
67
+ /* istanbul ignore if -- @preserve */
68
+ if (unsubscribed) {
69
+ // already unsubscribed - rare race condition
70
+ return;
71
+ }
72
+ eventSource = new EventSource(url, opts.eventSourceOptions);
73
+ const onStarted = () => {
74
+ observer.next({
75
+ result: {
76
+ type: 'started',
77
+ },
78
+ context: {
79
+ eventSource,
80
+ },
81
+ });
82
+
83
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84
+ eventSource!.removeEventListener('open', onStarted);
85
+ };
86
+ // console.log('starting', new Date());
87
+ eventSource.addEventListener('open', onStarted);
88
+ const iterable = sseStreamConsumer<SSEvent>({
89
+ from: eventSource,
90
+ deserialize: transformer.input.deserialize,
91
+ });
92
+
93
+ for await (const chunk of iterable) {
94
+ // if the `sse({})`-helper is used, we always have an `id` field
95
+ const data = 'id' in chunk ? chunk : chunk.data;
96
+ observer.next({
97
+ result: {
98
+ data,
99
+ },
100
+ });
101
+ }
102
+
103
+ observer.next({
104
+ result: {
105
+ type: 'stopped',
106
+ },
107
+ });
108
+ observer.complete();
109
+ }).catch((error) => {
110
+ observer.error(TRPCClientError.from(error));
111
+ });
112
+
113
+ return () => {
114
+ observer.complete();
115
+ eventSource?.close();
116
+ unsubscribed = true;
117
+ };
118
+ });
119
+ };
120
+ };
121
+ }
@@ -1,7 +1,8 @@
1
1
  import type {
2
- AnyRootTypes,
2
+ AnyClientTypes,
3
3
  CombinedDataTransformer,
4
4
  ProcedureType,
5
+ TRPCAcceptHeader,
5
6
  TRPCResponse,
6
7
  } from '@trpc/server/unstable-core-do-not-import';
7
8
  import { getFetch } from '../../getFetch';
@@ -22,7 +23,7 @@ import type { HTTPHeaders, PromiseAndCancel } from '../types';
22
23
  * @internal
23
24
  */
24
25
  export type HTTPLinkBaseOptions<
25
- TRoot extends Pick<AnyRootTypes, 'transformer'>,
26
+ TRoot extends Pick<AnyClientTypes, 'transformer'>,
26
27
  > = {
27
28
  url: string | URL;
28
29
  /**
@@ -50,10 +51,10 @@ export interface ResolvedHTTPLinkOptions {
50
51
  }
51
52
 
52
53
  export function resolveHTTPLinkOptions(
53
- opts: HTTPLinkBaseOptions<AnyRootTypes>,
54
+ opts: HTTPLinkBaseOptions<AnyClientTypes>,
54
55
  ): ResolvedHTTPLinkOptions {
55
56
  return {
56
- url: opts.url.toString().replace(/\/$/, ''), // Remove any trailing slashes
57
+ url: opts.url.toString(),
57
58
  fetch: opts.fetch,
58
59
  AbortController: getAbortController(opts.AbortController),
59
60
  transformer: getTransformer(opts.transformer),
@@ -74,6 +75,7 @@ function arrayToDict(array: unknown[]) {
74
75
  const METHOD = {
75
76
  query: 'GET',
76
77
  mutation: 'POST',
78
+ subscription: 'PATCH',
77
79
  } as const;
78
80
 
79
81
  export interface HTTPResult {
@@ -106,19 +108,20 @@ type GetUrl = (opts: HTTPBaseRequestOptions) => string;
106
108
  type GetBody = (opts: HTTPBaseRequestOptions) => RequestInitEsque['body'];
107
109
 
108
110
  export type ContentOptions = {
109
- trpcAcceptHeader?: 'application/jsonl';
111
+ trpcAcceptHeader?: TRPCAcceptHeader;
110
112
  contentTypeHeader?: string;
111
113
  getUrl: GetUrl;
112
114
  getBody: GetBody;
113
115
  };
114
116
 
115
117
  export const getUrl: GetUrl = (opts) => {
116
- let url = opts.url + '/' + opts.path;
118
+ const base = opts.url.replace(/\/$/, ''); // Remove any trailing slashes
119
+ let url = base + '/' + opts.path;
117
120
  const queryParts: string[] = [];
118
121
  if ('inputs' in opts) {
119
122
  queryParts.push('batch=1');
120
123
  }
121
- if (opts.type === 'query') {
124
+ if (opts.type === 'query' || opts.type === 'subscription') {
122
125
  const input = getInput(opts);
123
126
  if (input !== undefined && opts.methodOverride !== 'POST') {
124
127
  queryParts.push(`input=${encodeURIComponent(JSON.stringify(input))}`);
@@ -172,10 +175,6 @@ export async function fetchHTTPResponse(
172
175
  }
173
176
  return heads;
174
177
  })();
175
- /* istanbul ignore if -- @preserve */
176
- if (type === 'subscription') {
177
- throw new Error('Subscriptions should use wsLink');
178
- }
179
178
  const headers = {
180
179
  ...(opts.contentTypeHeader
181
180
  ? { 'content-type': opts.contentTypeHeader }
package/src/links.ts CHANGED
@@ -7,6 +7,7 @@ export * from './links/httpLink';
7
7
  export * from './links/loggerLink';
8
8
  export * from './links/splitLink';
9
9
  export * from './links/wsLink';
10
+ export * from './links/httpSubscriptionLink';
10
11
 
11
12
  // These are not public (yet) as we get this functionality from tanstack query
12
13
  // export * from './links/internals/retryLink';