@trpc/client 10.28.1 → 10.29.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.
Files changed (64) hide show
  1. package/dist/TRPCClientError.d.ts +1 -1
  2. package/dist/TRPCClientError.d.ts.map +1 -1
  3. package/dist/createTRPCClient.d.ts +1 -1
  4. package/dist/createTRPCClient.d.ts.map +1 -1
  5. package/dist/createTRPCClientProxy.d.ts +1 -1
  6. package/dist/createTRPCClientProxy.d.ts.map +1 -1
  7. package/dist/httpBatchLink-38dcf5f2.js +243 -0
  8. package/dist/httpBatchLink-e66c0674.mjs +240 -0
  9. package/dist/httpBatchLink-f35df745.js +239 -0
  10. package/dist/{httpUtils-00c8b54e.js → httpUtils-145e783b.js} +27 -23
  11. package/dist/{httpUtils-90a6bccb.js → httpUtils-21cbb35d.js} +27 -20
  12. package/dist/{httpUtils-8a5c637a.mjs → httpUtils-b2fcc20c.mjs} +26 -21
  13. package/dist/index.js +149 -4
  14. package/dist/index.mjs +151 -6
  15. package/dist/internals/TRPCUntypedClient.d.ts +1 -1
  16. package/dist/internals/TRPCUntypedClient.d.ts.map +1 -1
  17. package/dist/internals/dataLoader.d.ts +1 -1
  18. package/dist/internals/dataLoader.d.ts.map +1 -1
  19. package/dist/internals/types.d.ts +2 -0
  20. package/dist/internals/types.d.ts.map +1 -1
  21. package/dist/links/HTTPBatchLinkOptions.d.ts +20 -0
  22. package/dist/links/HTTPBatchLinkOptions.d.ts.map +1 -0
  23. package/dist/links/httpBatchLink.d.ts +1 -15
  24. package/dist/links/httpBatchLink.d.ts.map +1 -1
  25. package/dist/links/httpBatchLink.js +5 -217
  26. package/dist/links/httpBatchLink.mjs +4 -220
  27. package/dist/links/httpBatchStreamLink.d.ts +2 -0
  28. package/dist/links/httpBatchStreamLink.d.ts.map +1 -0
  29. package/dist/links/httpLink.d.ts.map +1 -1
  30. package/dist/links/httpLink.js +1 -1
  31. package/dist/links/httpLink.mjs +1 -1
  32. package/dist/links/index.d.ts +2 -0
  33. package/dist/links/index.d.ts.map +1 -1
  34. package/dist/links/internals/createHTTPBatchLink.d.ts +20 -0
  35. package/dist/links/internals/createHTTPBatchLink.d.ts.map +1 -0
  36. package/dist/links/internals/httpUtils.d.ts +3 -1
  37. package/dist/links/internals/httpUtils.d.ts.map +1 -1
  38. package/dist/links/internals/parseJSONStream.d.ts +36 -0
  39. package/dist/links/internals/parseJSONStream.d.ts.map +1 -0
  40. package/dist/links/types.d.ts +1 -1
  41. package/dist/links/types.d.ts.map +1 -1
  42. package/dist/links/wsLink.d.ts +1 -1
  43. package/dist/links/wsLink.d.ts.map +1 -1
  44. package/dist/shared/transformResult.d.ts.map +1 -1
  45. package/package.json +4 -4
  46. package/src/TRPCClientError.ts +1 -1
  47. package/src/createTRPCClient.ts +1 -1
  48. package/src/createTRPCClientProxy.ts +1 -1
  49. package/src/internals/TRPCUntypedClient.ts +2 -2
  50. package/src/internals/dataLoader.ts +21 -7
  51. package/src/internals/types.ts +1 -0
  52. package/src/links/HTTPBatchLinkOptions.ts +21 -0
  53. package/src/links/httpBatchLink.ts +44 -133
  54. package/src/links/httpBatchStreamLink.ts +47 -0
  55. package/src/links/httpFormDataLink.ts +1 -1
  56. package/src/links/httpLink.ts +2 -2
  57. package/src/links/index.ts +2 -0
  58. package/src/links/internals/createHTTPBatchLink.ts +113 -0
  59. package/src/links/internals/httpUtils.ts +33 -24
  60. package/src/links/internals/parseJSONStream.ts +165 -0
  61. package/src/links/retryLink.ts +1 -1
  62. package/src/links/types.ts +1 -1
  63. package/src/links/wsLink.ts +3 -3
  64. package/src/shared/transformResult.ts +1 -1
@@ -0,0 +1,239 @@
1
+ import { observable } from '@trpc/server/observable';
2
+ import { t as transformResult, T as TRPCClientError } from './transformResult-ae8e970c.js';
3
+ import { r as resolveHTTPLinkOptions, g as getUrl, j as jsonHttpRequester } from './httpUtils-145e783b.js';
4
+
5
+ /**
6
+ * A function that should never be called unless we messed something up.
7
+ */
8
+ const throwFatalError = () => {
9
+ throw new Error('Something went wrong. Please submit an issue at https://github.com/trpc/trpc/issues/new');
10
+ };
11
+ /**
12
+ * Dataloader that's very inspired by https://github.com/graphql/dataloader
13
+ * Less configuration, no caching, and allows you to cancel requests
14
+ * When cancelling a single fetch the whole batch will be cancelled only when _all_ items are cancelled
15
+ */
16
+ function dataLoader(batchLoader) {
17
+ let pendingItems = null;
18
+ let dispatchTimer = null;
19
+ const destroyTimerAndPendingItems = () => {
20
+ clearTimeout(dispatchTimer);
21
+ dispatchTimer = null;
22
+ pendingItems = null;
23
+ };
24
+ /**
25
+ * Iterate through the items and split them into groups based on the `batchLoader`'s validate function
26
+ */
27
+ function groupItems(items) {
28
+ const groupedItems = [[]];
29
+ let index = 0;
30
+ while (true) {
31
+ const item = items[index];
32
+ if (!item) {
33
+ // we're done
34
+ break;
35
+ }
36
+ const lastGroup = groupedItems[groupedItems.length - 1];
37
+ if (item.aborted) {
38
+ // Item was aborted before it was dispatched
39
+ item.reject?.(new Error('Aborted'));
40
+ index++;
41
+ continue;
42
+ }
43
+ const isValid = batchLoader.validate(lastGroup.concat(item).map((it) => it.key));
44
+ if (isValid) {
45
+ lastGroup.push(item);
46
+ index++;
47
+ continue;
48
+ }
49
+ if (lastGroup.length === 0) {
50
+ item.reject?.(new Error('Input is too big for a single dispatch'));
51
+ index++;
52
+ continue;
53
+ }
54
+ // Create new group, next iteration will try to add the item to that
55
+ groupedItems.push([]);
56
+ }
57
+ return groupedItems;
58
+ }
59
+ function dispatch() {
60
+ const groupedItems = groupItems(pendingItems);
61
+ destroyTimerAndPendingItems();
62
+ // Create batches for each group of items
63
+ for (const items of groupedItems) {
64
+ if (!items.length) {
65
+ continue;
66
+ }
67
+ const batch = {
68
+ items,
69
+ cancel: throwFatalError,
70
+ };
71
+ for (const item of items) {
72
+ item.batch = batch;
73
+ }
74
+ const unitResolver = (index, value) => {
75
+ const item = batch.items[index];
76
+ item.resolve?.(value);
77
+ item.batch = null;
78
+ item.reject = null;
79
+ item.resolve = null;
80
+ };
81
+ const { promise, cancel } = batchLoader.fetch(batch.items.map((_item) => _item.key), unitResolver);
82
+ batch.cancel = cancel;
83
+ promise
84
+ .then((result) => {
85
+ for (let i = 0; i < result.length; i++) {
86
+ const value = result[i];
87
+ unitResolver(i, value);
88
+ }
89
+ for (let i = 0; i < batch.items.length; i++) {
90
+ const item = batch.items[i];
91
+ item.reject?.(new Error('Missing result'));
92
+ item.batch = null;
93
+ }
94
+ })
95
+ .catch((cause) => {
96
+ for (const item of batch.items) {
97
+ item.reject?.(cause);
98
+ item.batch = null;
99
+ }
100
+ });
101
+ }
102
+ }
103
+ function load(key) {
104
+ const item = {
105
+ aborted: false,
106
+ key,
107
+ batch: null,
108
+ resolve: throwFatalError,
109
+ reject: throwFatalError,
110
+ };
111
+ const promise = new Promise((resolve, reject) => {
112
+ item.reject = reject;
113
+ item.resolve = resolve;
114
+ if (!pendingItems) {
115
+ pendingItems = [];
116
+ }
117
+ pendingItems.push(item);
118
+ });
119
+ if (!dispatchTimer) {
120
+ dispatchTimer = setTimeout(dispatch);
121
+ }
122
+ const cancel = () => {
123
+ item.aborted = true;
124
+ if (item.batch?.items.every((item) => item.aborted)) {
125
+ // All items in the batch have been cancelled
126
+ item.batch.cancel();
127
+ item.batch = null;
128
+ }
129
+ };
130
+ return { promise, cancel };
131
+ }
132
+ return {
133
+ load,
134
+ };
135
+ }
136
+
137
+ /**
138
+ * @internal
139
+ */
140
+ function createHTTPBatchLink(requester) {
141
+ return function httpBatchLink(opts) {
142
+ const resolvedOpts = resolveHTTPLinkOptions(opts);
143
+ const maxURLLength = opts.maxURLLength || Infinity;
144
+ // initialized config
145
+ return (runtime) => {
146
+ const batchLoader = (type) => {
147
+ const validate = (batchOps) => {
148
+ if (maxURLLength === Infinity) {
149
+ // escape hatch for quick calcs
150
+ return true;
151
+ }
152
+ const path = batchOps.map((op) => op.path).join(',');
153
+ const inputs = batchOps.map((op) => op.input);
154
+ const url = getUrl({
155
+ ...resolvedOpts,
156
+ runtime,
157
+ type,
158
+ path,
159
+ inputs,
160
+ });
161
+ return url.length <= maxURLLength;
162
+ };
163
+ const fetch = requester({
164
+ ...resolvedOpts,
165
+ runtime,
166
+ type,
167
+ opts,
168
+ });
169
+ return { validate, fetch };
170
+ };
171
+ const query = dataLoader(batchLoader('query'));
172
+ const mutation = dataLoader(batchLoader('mutation'));
173
+ const subscription = dataLoader(batchLoader('subscription'));
174
+ const loaders = { query, subscription, mutation };
175
+ return ({ op }) => {
176
+ return observable((observer) => {
177
+ const loader = loaders[op.type];
178
+ const { promise, cancel } = loader.load(op);
179
+ promise
180
+ .then((res) => {
181
+ const transformed = transformResult(res.json, runtime);
182
+ if (!transformed.ok) {
183
+ observer.error(TRPCClientError.from(transformed.error, {
184
+ meta: res.meta,
185
+ }));
186
+ return;
187
+ }
188
+ observer.next({
189
+ context: res.meta,
190
+ result: transformed.result,
191
+ });
192
+ observer.complete();
193
+ })
194
+ .catch((err) => observer.error(TRPCClientError.from(err)));
195
+ return () => cancel();
196
+ });
197
+ };
198
+ };
199
+ };
200
+ }
201
+
202
+ const batchRequester = (requesterOpts) => {
203
+ return (batchOps) => {
204
+ const path = batchOps.map((op) => op.path).join(',');
205
+ const inputs = batchOps.map((op) => op.input);
206
+ const { promise, cancel } = jsonHttpRequester({
207
+ ...requesterOpts,
208
+ path,
209
+ inputs,
210
+ headers() {
211
+ if (!requesterOpts.opts.headers) {
212
+ return {};
213
+ }
214
+ if (typeof requesterOpts.opts.headers === 'function') {
215
+ return requesterOpts.opts.headers({
216
+ opList: batchOps,
217
+ });
218
+ }
219
+ return requesterOpts.opts.headers;
220
+ },
221
+ });
222
+ return {
223
+ promise: promise.then((res) => {
224
+ const resJSON = Array.isArray(res.json)
225
+ ? res.json
226
+ : batchOps.map(() => res.json);
227
+ const result = resJSON.map((item) => ({
228
+ meta: res.meta,
229
+ json: item,
230
+ }));
231
+ return result;
232
+ }),
233
+ cancel,
234
+ };
235
+ };
236
+ };
237
+ const httpBatchLink = createHTTPBatchLink(batchRequester);
238
+
239
+ export { createHTTPBatchLink as c, httpBatchLink as h };
@@ -86,31 +86,35 @@ const jsonHttpRequester = (opts) => {
86
86
  getBody,
87
87
  });
88
88
  };
89
- function httpRequest(opts) {
89
+ async function fetchHTTPResponse(opts, ac) {
90
+ const url = opts.getUrl(opts);
91
+ const body = opts.getBody(opts);
90
92
  const { type } = opts;
93
+ const headers = await opts.headers();
94
+ /* istanbul ignore if -- @preserve */
95
+ if (type === 'subscription') {
96
+ throw new Error('Subscriptions should use wsLink');
97
+ }
98
+ return opts.fetch(url, {
99
+ method: METHOD[type],
100
+ signal: ac?.signal,
101
+ body: body,
102
+ headers: {
103
+ ...(opts.contentTypeHeader
104
+ ? { 'content-type': opts.contentTypeHeader }
105
+ : {}),
106
+ ...(opts.batchModeHeader
107
+ ? { 'trpc-batch-mode': opts.batchModeHeader }
108
+ : {}),
109
+ ...headers,
110
+ },
111
+ });
112
+ }
113
+ function httpRequest(opts) {
91
114
  const ac = opts.AbortController ? new opts.AbortController() : null;
115
+ const meta = {};
92
116
  const promise = new Promise((resolve, reject) => {
93
- const url = opts.getUrl(opts);
94
- const body = opts.getBody(opts);
95
- const meta = {};
96
- Promise.resolve(opts.headers())
97
- .then((headers) => {
98
- /* istanbul ignore if -- @preserve */
99
- if (type === 'subscription') {
100
- throw new Error('Subscriptions should use wsLink');
101
- }
102
- return opts.fetch(url, {
103
- method: METHOD[type],
104
- signal: ac?.signal,
105
- body: body,
106
- headers: {
107
- ...(opts.contentTypeHeader
108
- ? { 'content-type': opts.contentTypeHeader }
109
- : {}),
110
- ...headers,
111
- },
112
- });
113
- })
117
+ fetchHTTPResponse(opts, ac)
114
118
  .then((_res) => {
115
119
  meta.response = _res;
116
120
  return _res.json();
@@ -129,4 +133,4 @@ function httpRequest(opts) {
129
133
  return { promise, cancel };
130
134
  }
131
135
 
132
- export { getUrl as a, getFetch as g, httpRequest as h, jsonHttpRequester as j, resolveHTTPLinkOptions as r };
136
+ export { getBody as a, getFetch as b, fetchHTTPResponse as f, getUrl as g, httpRequest as h, jsonHttpRequester as j, resolveHTTPLinkOptions as r };
@@ -86,29 +86,34 @@ const jsonHttpRequester = (opts)=>{
86
86
  getBody
87
87
  });
88
88
  };
89
- function httpRequest(opts) {
89
+ async function fetchHTTPResponse(opts, ac) {
90
+ const url = opts.getUrl(opts);
91
+ const body = opts.getBody(opts);
90
92
  const { type } = opts;
93
+ const headers = await opts.headers();
94
+ /* istanbul ignore if -- @preserve */ if (type === 'subscription') {
95
+ throw new Error('Subscriptions should use wsLink');
96
+ }
97
+ return opts.fetch(url, {
98
+ method: METHOD[type],
99
+ signal: ac?.signal,
100
+ body: body,
101
+ headers: {
102
+ ...opts.contentTypeHeader ? {
103
+ 'content-type': opts.contentTypeHeader
104
+ } : {},
105
+ ...opts.batchModeHeader ? {
106
+ 'trpc-batch-mode': opts.batchModeHeader
107
+ } : {},
108
+ ...headers
109
+ }
110
+ });
111
+ }
112
+ function httpRequest(opts) {
91
113
  const ac = opts.AbortController ? new opts.AbortController() : null;
114
+ const meta = {};
92
115
  const promise = new Promise((resolve, reject)=>{
93
- const url = opts.getUrl(opts);
94
- const body = opts.getBody(opts);
95
- const meta = {};
96
- Promise.resolve(opts.headers()).then((headers)=>{
97
- /* istanbul ignore if -- @preserve */ if (type === 'subscription') {
98
- throw new Error('Subscriptions should use wsLink');
99
- }
100
- return opts.fetch(url, {
101
- method: METHOD[type],
102
- signal: ac?.signal,
103
- body: body,
104
- headers: {
105
- ...opts.contentTypeHeader ? {
106
- 'content-type': opts.contentTypeHeader
107
- } : {},
108
- ...headers
109
- }
110
- });
111
- }).then((_res)=>{
116
+ fetchHTTPResponse(opts, ac).then((_res)=>{
112
117
  meta.response = _res;
113
118
  return _res.json();
114
119
  }).then((json)=>{
@@ -127,6 +132,8 @@ function httpRequest(opts) {
127
132
  };
128
133
  }
129
134
 
135
+ exports.fetchHTTPResponse = fetchHTTPResponse;
136
+ exports.getBody = getBody;
130
137
  exports.getFetch = getFetch;
131
138
  exports.getUrl = getUrl;
132
139
  exports.httpRequest = httpRequest;
@@ -84,29 +84,34 @@ const jsonHttpRequester = (opts)=>{
84
84
  getBody
85
85
  });
86
86
  };
87
- function httpRequest(opts) {
87
+ async function fetchHTTPResponse(opts, ac) {
88
+ const url = opts.getUrl(opts);
89
+ const body = opts.getBody(opts);
88
90
  const { type } = opts;
91
+ const headers = await opts.headers();
92
+ /* istanbul ignore if -- @preserve */ if (type === 'subscription') {
93
+ throw new Error('Subscriptions should use wsLink');
94
+ }
95
+ return opts.fetch(url, {
96
+ method: METHOD[type],
97
+ signal: ac?.signal,
98
+ body: body,
99
+ headers: {
100
+ ...opts.contentTypeHeader ? {
101
+ 'content-type': opts.contentTypeHeader
102
+ } : {},
103
+ ...opts.batchModeHeader ? {
104
+ 'trpc-batch-mode': opts.batchModeHeader
105
+ } : {},
106
+ ...headers
107
+ }
108
+ });
109
+ }
110
+ function httpRequest(opts) {
89
111
  const ac = opts.AbortController ? new opts.AbortController() : null;
112
+ const meta = {};
90
113
  const promise = new Promise((resolve, reject)=>{
91
- const url = opts.getUrl(opts);
92
- const body = opts.getBody(opts);
93
- const meta = {};
94
- Promise.resolve(opts.headers()).then((headers)=>{
95
- /* istanbul ignore if -- @preserve */ if (type === 'subscription') {
96
- throw new Error('Subscriptions should use wsLink');
97
- }
98
- return opts.fetch(url, {
99
- method: METHOD[type],
100
- signal: ac?.signal,
101
- body: body,
102
- headers: {
103
- ...opts.contentTypeHeader ? {
104
- 'content-type': opts.contentTypeHeader
105
- } : {},
106
- ...headers
107
- }
108
- });
109
- }).then((_res)=>{
114
+ fetchHTTPResponse(opts, ac).then((_res)=>{
110
115
  meta.response = _res;
111
116
  return _res.json();
112
117
  }).then((json)=>{
@@ -125,4 +130,4 @@ function httpRequest(opts) {
125
130
  };
126
131
  }
127
132
 
128
- export { getUrl as a, getFetch as g, httpRequest as h, jsonHttpRequester as j, resolveHTTPLinkOptions as r };
133
+ export { getBody as a, getFetch as b, fetchHTTPResponse as f, getUrl as g, httpRequest as h, jsonHttpRequester as j, resolveHTTPLinkOptions as r };
package/dist/index.js CHANGED
@@ -3,11 +3,11 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var observable = require('@trpc/server/observable');
6
- var transformResult = require('./transformResult-70f95ffb.js');
7
6
  var links_splitLink = require('./splitLink-f29e84be.js');
7
+ var transformResult = require('./transformResult-70f95ffb.js');
8
8
  var shared = require('@trpc/server/shared');
9
- var httpUtils = require('./httpUtils-90a6bccb.js');
10
- var links_httpBatchLink = require('./links/httpBatchLink.js');
9
+ var httpUtils = require('./httpUtils-21cbb35d.js');
10
+ var links_httpBatchLink = require('./httpBatchLink-38dcf5f2.js');
11
11
  var links_httpLink = require('./links/httpLink.js');
12
12
  var links_loggerLink = require('./links/loggerLink.js');
13
13
  var links_wsLink = require('./links/wsLink.js');
@@ -163,6 +163,150 @@ function createTRPCProxyClient(opts) {
163
163
  return proxy;
164
164
  }
165
165
 
166
+ // Stream parsing adapted from https://www.loginradius.com/blog/engineering/guest-post/http-streaming-with-nodejs-and-fetch-api/
167
+ /**
168
+ * @internal
169
+ * @description Take a stream of bytes and call `onLine` with
170
+ * a JSON object for each line in the stream. Expected stream
171
+ * format is:
172
+ * ```json
173
+ * {"1": {...}
174
+ * ,"0": {...}
175
+ * }
176
+ * ```
177
+ */ async function parseJSONStream(opts) {
178
+ const parse = opts.parse ?? JSON.parse;
179
+ const onLine = (line)=>{
180
+ if (opts.signal?.aborted) return;
181
+ if (!line || line === '}') {
182
+ return;
183
+ }
184
+ /**
185
+ * At this point, `line` can be one of two things:
186
+ * - The first line of the stream `{"2":{...}`
187
+ * - A line in the middle of the stream `,"2":{...}`
188
+ */ const indexOfColon = line.indexOf(':');
189
+ const indexAsStr = line.substring(2, indexOfColon - 1);
190
+ const text = line.substring(indexOfColon + 1);
191
+ opts.onSingle(Number(indexAsStr), parse(text));
192
+ };
193
+ await readLines(opts.readableStream, onLine);
194
+ }
195
+ const textDecoder = new TextDecoder();
196
+ /**
197
+ * Handle transforming a stream of bytes into lines of text.
198
+ * To avoid using AsyncIterators / AsyncGenerators,
199
+ * we use a callback for each line.
200
+ *
201
+ * @param readableStream can be a NodeJS stream or a WebAPI stream
202
+ * @param onLine will be called for every line ('\n' delimited) in the stream
203
+ */ async function readLines(readableStream, onLine) {
204
+ let partOfLine = '';
205
+ const onChunk = (chunk)=>{
206
+ const chunkText = textDecoder.decode(chunk);
207
+ const chunkLines = chunkText.split('\n');
208
+ if (chunkLines.length === 1) {
209
+ partOfLine += chunkLines[0];
210
+ } else if (chunkLines.length > 1) {
211
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
212
+ onLine(partOfLine + chunkLines[0]);
213
+ for(let i = 1; i < chunkLines.length - 1; i++){
214
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length checked on line above
215
+ onLine(chunkLines[i]);
216
+ }
217
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length doesn't change, so is necessarily > 1
218
+ partOfLine = chunkLines[chunkLines.length - 1];
219
+ }
220
+ };
221
+ // we handle 2 different types of streams, this if where we figure out which one we have
222
+ if ('getReader' in readableStream) {
223
+ await readStandardChunks(readableStream, onChunk);
224
+ } else {
225
+ await readNodeChunks(readableStream, onChunk);
226
+ }
227
+ onLine(partOfLine);
228
+ }
229
+ /**
230
+ * Handle NodeJS stream
231
+ */ function readNodeChunks(stream, onChunk) {
232
+ return new Promise((resolve)=>{
233
+ stream.on('data', onChunk);
234
+ stream.on('end', resolve);
235
+ });
236
+ }
237
+ /**
238
+ * Handle WebAPI stream
239
+ */ async function readStandardChunks(stream, onChunk) {
240
+ const reader = stream.getReader();
241
+ let readResult = await reader.read();
242
+ while(!readResult.done){
243
+ onChunk(readResult.value);
244
+ readResult = await reader.read();
245
+ }
246
+ }
247
+ const streamingJsonHttpRequester = (opts, onSingle)=>{
248
+ const ac = opts.AbortController ? new opts.AbortController() : null;
249
+ const responsePromise = httpUtils.fetchHTTPResponse({
250
+ ...opts,
251
+ contentTypeHeader: 'application/json',
252
+ batchModeHeader: 'stream',
253
+ getUrl: httpUtils.getUrl,
254
+ getBody: httpUtils.getBody
255
+ }, ac);
256
+ const cancel = ()=>ac?.abort();
257
+ const promise = responsePromise.then(async (res)=>{
258
+ if (!res.body) throw new Error('Received response without body');
259
+ const meta = {
260
+ response: res
261
+ };
262
+ return parseJSONStream({
263
+ readableStream: res.body,
264
+ onSingle,
265
+ parse: (string)=>({
266
+ json: JSON.parse(string),
267
+ meta
268
+ }),
269
+ signal: ac?.signal
270
+ });
271
+ });
272
+ return {
273
+ cancel,
274
+ promise
275
+ };
276
+ };
277
+
278
+ const streamRequester = (requesterOpts)=>{
279
+ return (batchOps, unitResolver)=>{
280
+ const path = batchOps.map((op)=>op.path).join(',');
281
+ const inputs = batchOps.map((op)=>op.input);
282
+ const { cancel , promise } = streamingJsonHttpRequester({
283
+ ...requesterOpts,
284
+ path,
285
+ inputs,
286
+ headers () {
287
+ if (!requesterOpts.opts.headers) {
288
+ return {};
289
+ }
290
+ if (typeof requesterOpts.opts.headers === 'function') {
291
+ return requesterOpts.opts.headers({
292
+ opList: batchOps
293
+ });
294
+ }
295
+ return requesterOpts.opts.headers;
296
+ }
297
+ }, (index, res)=>unitResolver(index, res));
298
+ return {
299
+ /**
300
+ * return an empty array because the batchLoader expects an array of results
301
+ * but we've already called the `unitResolver` for each of them, there's
302
+ * nothing left to do here.
303
+ */ promise: promise.then(()=>[]),
304
+ cancel
305
+ };
306
+ };
307
+ };
308
+ const unstable_httpBatchStreamLink = links_httpBatchLink.createHTTPBatchLink(streamRequester);
309
+
166
310
  const getBody = (opts)=>{
167
311
  if (!('input' in opts)) {
168
312
  return undefined;
@@ -189,8 +333,8 @@ const experimental_formDataLink = links_httpLink.httpLinkFactory({
189
333
  requester: formDataRequester
190
334
  });
191
335
 
192
- exports.TRPCClientError = transformResult.TRPCClientError;
193
336
  exports.splitLink = links_splitLink.splitLink;
337
+ exports.TRPCClientError = transformResult.TRPCClientError;
194
338
  exports.getFetch = httpUtils.getFetch;
195
339
  exports.httpBatchLink = links_httpBatchLink.httpBatchLink;
196
340
  exports.httpLink = links_httpLink.httpLink;
@@ -204,3 +348,4 @@ exports.createTRPCClientProxy = createTRPCClientProxy;
204
348
  exports.createTRPCProxyClient = createTRPCProxyClient;
205
349
  exports.createTRPCUntypedClient = createTRPCUntypedClient;
206
350
  exports.experimental_formDataLink = experimental_formDataLink;
351
+ exports.unstable_httpBatchStreamLink = unstable_httpBatchStreamLink;