@trpc/server 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 (97) hide show
  1. package/dist/adapters/aws-lambda/index.d.ts +1 -1
  2. package/dist/adapters/aws-lambda/index.d.ts.map +1 -1
  3. package/dist/adapters/aws-lambda/index.js +1 -1
  4. package/dist/adapters/aws-lambda/index.mjs +1 -1
  5. package/dist/adapters/aws-lambda/utils.d.ts +1 -1
  6. package/dist/adapters/aws-lambda/utils.d.ts.map +1 -1
  7. package/dist/adapters/express.d.ts.map +1 -1
  8. package/dist/adapters/express.js +5 -4
  9. package/dist/adapters/express.mjs +5 -4
  10. package/dist/adapters/fastify/fastifyRequestHandler.d.ts +1 -1
  11. package/dist/adapters/fastify/fastifyRequestHandler.d.ts.map +1 -1
  12. package/dist/adapters/fastify/index.js +50 -15
  13. package/dist/adapters/fastify/index.mjs +50 -15
  14. package/dist/adapters/fetch/fetchRequestHandler.d.ts.map +1 -1
  15. package/dist/adapters/fetch/index.js +66 -20
  16. package/dist/adapters/fetch/index.mjs +66 -20
  17. package/dist/adapters/lambda/index.d.ts +1 -1
  18. package/dist/adapters/lambda/index.d.ts.map +1 -1
  19. package/dist/adapters/next.d.ts.map +1 -1
  20. package/dist/adapters/next.js +3 -2
  21. package/dist/adapters/next.mjs +3 -2
  22. package/dist/adapters/node-http/content-type/form-data/index.js +19 -46
  23. package/dist/adapters/node-http/content-type/form-data/index.mjs +19 -40
  24. package/dist/adapters/node-http/content-type/form-data/streamSlice.d.ts +7 -3
  25. package/dist/adapters/node-http/content-type/form-data/streamSlice.d.ts.map +1 -1
  26. package/dist/adapters/node-http/index.js +5 -4
  27. package/dist/adapters/node-http/index.mjs +5 -4
  28. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  29. package/dist/adapters/standalone.js +5 -4
  30. package/dist/adapters/standalone.mjs +5 -4
  31. package/dist/batchStreamFormatter-2c1405a1.js +31 -0
  32. package/dist/batchStreamFormatter-93cdcdd4.js +32 -0
  33. package/dist/batchStreamFormatter-fc1ffb26.mjs +30 -0
  34. package/dist/core/initTRPC.d.ts.map +1 -1
  35. package/dist/core/internals/mergeRouters.d.ts.map +1 -1
  36. package/dist/core/internals/procedureBuilder.d.ts +1 -1
  37. package/dist/core/internals/procedureBuilder.d.ts.map +1 -1
  38. package/dist/core/router.d.ts +1 -1
  39. package/dist/core/router.d.ts.map +1 -1
  40. package/dist/deprecated/interop.d.ts.map +1 -1
  41. package/dist/deprecated/router.d.ts +3 -3
  42. package/dist/deprecated/router.d.ts.map +1 -1
  43. package/dist/error/formatter.d.ts +1 -1
  44. package/dist/error/formatter.d.ts.map +1 -1
  45. package/dist/http/batchStreamFormatter.d.ts +24 -0
  46. package/dist/http/batchStreamFormatter.d.ts.map +1 -0
  47. package/dist/http/getHTTPStatusCode.d.ts +1 -1
  48. package/dist/http/getHTTPStatusCode.d.ts.map +1 -1
  49. package/dist/http/index.d.ts +1 -0
  50. package/dist/http/index.d.ts.map +1 -1
  51. package/dist/http/index.js +3 -1
  52. package/dist/http/index.mjs +2 -1
  53. package/dist/http/internals/types.d.ts +8 -1
  54. package/dist/http/internals/types.d.ts.map +1 -1
  55. package/dist/http/resolveHTTPResponse.d.ts +36 -2
  56. package/dist/http/resolveHTTPResponse.d.ts.map +1 -1
  57. package/dist/internals/types.d.ts +1 -1
  58. package/dist/internals/types.d.ts.map +1 -1
  59. package/dist/{nodeHTTPRequestHandler-bd47641d.js → nodeHTTPRequestHandler-53b9fc2d.js} +44 -12
  60. package/dist/{nodeHTTPRequestHandler-0f979796.mjs → nodeHTTPRequestHandler-bdad2781.mjs} +42 -13
  61. package/dist/{nodeHTTPRequestHandler-5bbf93f1.js → nodeHTTPRequestHandler-e637755a.js} +42 -13
  62. package/dist/resolveHTTPResponse-2bdf2cd7.js +256 -0
  63. package/dist/resolveHTTPResponse-2c307e04.js +284 -0
  64. package/dist/resolveHTTPResponse-a9be3d96.mjs +282 -0
  65. package/dist/subscription.d.ts.map +1 -1
  66. package/package.json +4 -4
  67. package/src/adapters/aws-lambda/index.ts +2 -2
  68. package/src/adapters/aws-lambda/utils.ts +1 -1
  69. package/src/adapters/express.ts +1 -1
  70. package/src/adapters/fastify/fastifyRequestHandler.ts +64 -17
  71. package/src/adapters/fastify/fastifyTRPCPlugin.ts +1 -1
  72. package/src/adapters/fetch/fetchRequestHandler.ts +72 -25
  73. package/src/adapters/lambda/index.ts +1 -1
  74. package/src/adapters/next.ts +1 -1
  75. package/src/adapters/node-http/content-type/form-data/fileUploadHandler.ts +1 -1
  76. package/src/adapters/node-http/content-type/form-data/index.ts +1 -1
  77. package/src/adapters/node-http/content-type/form-data/streamSlice.ts +19 -21
  78. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +51 -12
  79. package/src/adapters/ws.ts +3 -3
  80. package/src/core/initTRPC.ts +2 -2
  81. package/src/core/internals/mergeRouters.ts +1 -1
  82. package/src/core/internals/procedureBuilder.ts +5 -5
  83. package/src/core/router.ts +2 -2
  84. package/src/deprecated/internals/procedure.ts +2 -2
  85. package/src/deprecated/interop.ts +1 -1
  86. package/src/deprecated/router.ts +6 -6
  87. package/src/error/formatter.ts +1 -1
  88. package/src/http/batchStreamFormatter.ts +29 -0
  89. package/src/http/getHTTPStatusCode.ts +1 -1
  90. package/src/http/index.ts +1 -0
  91. package/src/http/internals/types.ts +9 -1
  92. package/src/http/resolveHTTPResponse.ts +337 -126
  93. package/src/internals/types.ts +1 -1
  94. package/src/subscription.ts +1 -1
  95. package/dist/resolveHTTPResponse-4e576698.mjs +0 -174
  96. package/dist/resolveHTTPResponse-9523b4e3.js +0 -176
  97. package/dist/resolveHTTPResponse-ba557d3e.js +0 -166
@@ -1,6 +1,12 @@
1
+ import { Readable } from 'node:stream';
1
2
  import { FastifyReply, FastifyRequest } from 'fastify';
2
3
  import { AnyRouter, inferRouterContext } from '../../core';
3
- import { HTTPBaseHandlerOptions, HTTPRequest } from '../../http';
4
+ import {
5
+ getBatchStreamFormatter,
6
+ HTTPBaseHandlerOptions,
7
+ HTTPRequest,
8
+ } from '../../http';
9
+ import { HTTPResponse, ResponseChunk } from '../../http/internals/types';
4
10
  import { resolveHTTPResponse } from '../../http/resolveHTTPResponse';
5
11
  import { NodeHTTPCreateContextOption } from '../node-http';
6
12
 
@@ -43,7 +49,48 @@ export async function fastifyRequestHandler<
43
49
  body: opts.req.body ?? 'null',
44
50
  };
45
51
 
46
- const result = await resolveHTTPResponse({
52
+ let resolve: (value: FastifyReply) => void;
53
+ const promise = new Promise<FastifyReply>((r) => (resolve = r));
54
+
55
+ const onHead = (head: HTTPResponse) => {
56
+ if (!opts.res.statusCode || opts.res.statusCode === 200) {
57
+ opts.res.statusCode = head.status;
58
+ }
59
+ for (const [key, value] of Object.entries(head.headers ?? {})) {
60
+ /* istanbul ignore if -- @preserve */
61
+ if (typeof value === 'undefined') {
62
+ continue;
63
+ }
64
+ void opts.res.header(key, value);
65
+ }
66
+ };
67
+
68
+ let isStream = false;
69
+ let stream: Readable;
70
+ const formatter = getBatchStreamFormatter();
71
+ const onChunk = ([index, string]: ResponseChunk) => {
72
+ if (index === -1) {
73
+ // full response, no streaming
74
+ resolve(opts.res.send(string));
75
+ return;
76
+ }
77
+ if (!isStream) {
78
+ void opts.res.header('Transfer-Encoding', 'chunked');
79
+ void opts.res.header(
80
+ 'Vary',
81
+ opts.res.hasHeader('Vary')
82
+ ? 'trpc-batch-mode, ' + opts.res.getHeader('Vary')
83
+ : 'trpc-batch-mode',
84
+ );
85
+ stream = new Readable();
86
+ stream._read = () => {}; // eslint-disable-line @typescript-eslint/no-empty-function -- https://github.com/fastify/fastify/issues/805#issuecomment-369172154
87
+ resolve(opts.res.send(stream));
88
+ isStream = true;
89
+ }
90
+ stream.push(formatter(index, string));
91
+ };
92
+
93
+ resolveHTTPResponse({
47
94
  req,
48
95
  createContext,
49
96
  path: opts.path,
@@ -53,20 +100,20 @@ export async function fastifyRequestHandler<
53
100
  onError(o) {
54
101
  opts?.onError?.({ ...o, req: opts.req });
55
102
  },
56
- });
57
-
58
- const { res } = opts;
59
-
60
- if ('status' in result && (!res.statusCode || res.statusCode === 200)) {
61
- res.statusCode = result.status;
62
- }
63
- for (const [key, value] of Object.entries(result.headers ?? {})) {
64
- /* istanbul ignore if -- @preserve */
65
- if (typeof value === 'undefined') {
66
- continue;
67
- }
103
+ onHead,
104
+ onChunk,
105
+ })
106
+ .then(() => {
107
+ if (isStream) {
108
+ stream.push(formatter.end());
109
+ stream.push(null); // https://github.com/fastify/fastify/issues/805#issuecomment-369172154
110
+ }
111
+ })
112
+ .catch(() => {
113
+ if (isStream) {
114
+ stream.push(null);
115
+ }
116
+ });
68
117
 
69
- void res.header(key, value);
70
- }
71
- await res.send(result.body);
118
+ return promise;
72
119
  }
@@ -3,7 +3,7 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
3
3
  import { FastifyHandlerOptions } from '.';
4
4
  import { AnyRouter } from '../../core';
5
5
  import { NodeHTTPCreateContextFnOptions } from '../node-http';
6
- import { WSSHandlerOptions, applyWSSHandler } from '../ws';
6
+ import { applyWSSHandler, WSSHandlerOptions } from '../ws';
7
7
  import { fastifyRequestHandler } from './fastifyRequestHandler';
8
8
 
9
9
  export interface FastifyTRPCPluginOptions<TRouter extends AnyRouter> {
@@ -1,5 +1,6 @@
1
1
  import { AnyRouter } from '../../core';
2
- import { HTTPRequest } from '../../http';
2
+ import { getBatchStreamFormatter, HTTPRequest } from '../../http';
3
+ import { HTTPResponse, ResponseChunk } from '../../http/internals/types';
3
4
  import { resolveHTTPResponse } from '../../http/resolveHTTPResponse';
4
5
  import { FetchHandlerOptions } from './types';
5
6
 
@@ -29,7 +30,61 @@ export async function fetchRequestHandler<TRouter extends AnyRouter>(
29
30
  : '',
30
31
  };
31
32
 
32
- const result = await resolveHTTPResponse({
33
+ let resolve: (value: Response) => void;
34
+ const promise = new Promise<Response>((r) => (resolve = r));
35
+ let status = 200;
36
+
37
+ const onHead = (head: HTTPResponse) => {
38
+ for (const [key, value] of Object.entries(head.headers ?? {})) {
39
+ /* istanbul ignore if -- @preserve */
40
+ if (typeof value === 'undefined') {
41
+ continue;
42
+ }
43
+ if (typeof value === 'string') {
44
+ resHeaders.set(key, value);
45
+ continue;
46
+ }
47
+ for (const v of value) {
48
+ resHeaders.append(key, v);
49
+ }
50
+ }
51
+ status = head.status;
52
+ };
53
+
54
+ let isStream = false;
55
+ let controller: ReadableStreamController<any>;
56
+ let encoder: TextEncoder;
57
+ const formatter = getBatchStreamFormatter();
58
+ const onChunk = ([index, string]: ResponseChunk) => {
59
+ if (index === -1) {
60
+ // full response, no streaming
61
+ const response = new Response(string || null, {
62
+ status,
63
+ headers: resHeaders,
64
+ });
65
+ resolve(response);
66
+ return;
67
+ }
68
+ if (!isStream) {
69
+ resHeaders.set('Transfer-Encoding', 'chunked');
70
+ resHeaders.append('Vary', 'trpc-batch-mode');
71
+ const stream = new ReadableStream({
72
+ start(c) {
73
+ controller = c;
74
+ },
75
+ });
76
+ const response = new Response(stream, {
77
+ status,
78
+ headers: resHeaders,
79
+ });
80
+ resolve(response);
81
+ encoder = new TextEncoder();
82
+ isStream = true;
83
+ }
84
+ controller.enqueue(encoder.encode(formatter(index, string)));
85
+ };
86
+
87
+ resolveHTTPResponse({
33
88
  req,
34
89
  createContext,
35
90
  path,
@@ -39,28 +94,20 @@ export async function fetchRequestHandler<TRouter extends AnyRouter>(
39
94
  onError(o) {
40
95
  opts?.onError?.({ ...o, req: opts.req });
41
96
  },
42
- });
43
-
44
- for (const [key, value] of Object.entries(result.headers ?? {})) {
45
- /* istanbul ignore if -- @preserve */
46
- if (typeof value === 'undefined') {
47
- continue;
48
- }
49
-
50
- if (typeof value === 'string') {
51
- resHeaders.set(key, value);
52
- continue;
53
- }
54
-
55
- for (const v of value) {
56
- resHeaders.append(key, v);
57
- }
58
- }
59
-
60
- const res = new Response(result.body, {
61
- status: result.status,
62
- headers: resHeaders,
63
- });
97
+ onHead,
98
+ onChunk,
99
+ })
100
+ .then(() => {
101
+ if (isStream) {
102
+ controller.enqueue(encoder.encode(formatter.end()));
103
+ controller.close();
104
+ }
105
+ })
106
+ .catch(() => {
107
+ if (isStream) {
108
+ controller.close();
109
+ }
110
+ });
64
111
 
65
- return res;
112
+ return promise;
66
113
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
- CreateAWSLambdaContextOptions,
3
2
  awsLambdaRequestHandler,
3
+ CreateAWSLambdaContextOptions,
4
4
  } from '../aws-lambda';
5
5
  import { APIGatewayEvent } from '../aws-lambda/utils';
6
6
 
@@ -6,10 +6,10 @@ import type {
6
6
  import { AnyRouter } from '../core';
7
7
  import { TRPCError } from '../error/TRPCError';
8
8
  import { getErrorShape } from '../shared/getErrorShape';
9
- import { nodeHTTPRequestHandler } from './node-http';
10
9
  import {
11
10
  NodeHTTPCreateContextFnOptions,
12
11
  NodeHTTPHandlerOptions,
12
+ nodeHTTPRequestHandler,
13
13
  } from './node-http';
14
14
 
15
15
  export type CreateNextContextOptions = NodeHTTPCreateContextFnOptions<
@@ -15,7 +15,7 @@ import { createReadStream, createWriteStream, statSync } from 'node:fs';
15
15
  import { mkdir, rm, stat as statAsync, unlink } from 'node:fs/promises';
16
16
  import { tmpdir } from 'node:os';
17
17
  import { basename, dirname, extname, resolve as resolvePath } from 'node:path';
18
- import { Readable, finished } from 'node:stream';
18
+ import { finished, Readable } from 'node:stream';
19
19
  import { promisify } from 'node:util';
20
20
  import { streamSlice } from './streamSlice';
21
21
  import { MaxPartSizeExceededError, UploadHandler } from './uploadHandler';
@@ -8,9 +8,9 @@
8
8
  /**
9
9
  * @see https://github.com/remix-run/remix/blob/0bcb4a304dd2f08f6032c3bf0c3aa7eb5b976901/packages/remix-server-runtime/formData.ts
10
10
  */
11
+ import { Readable } from 'node:stream';
11
12
  import { CombinedDataTransformer } from '@trpc/server/transformer';
12
13
  import { streamMultipart } from '@web3-storage/multipart-parser';
13
- import { Readable } from 'node:stream';
14
14
  import { createNodeHTTPContentTypeHandler } from '../../internals/contentType';
15
15
  import { NodeHTTPRequest } from '../../types';
16
16
  import { UploadHandler, UploadHandlerPart } from './uploadHandler';
@@ -1,28 +1,24 @@
1
1
  import { Transform, TransformCallback } from 'node:stream';
2
2
 
3
3
  class SliceStream extends Transform {
4
- #start: number;
5
- #end: number;
6
- #offset = 0;
7
- #emitUp = false;
8
- #emitDown = false;
4
+ private indexOffset = 0;
5
+ private emitUp = false;
6
+ private emitDown = false;
9
7
 
10
- constructor(start = 0, end = Infinity) {
8
+ constructor(private startIndex = 0, private endIndex = Infinity) {
11
9
  super();
12
- this.#start = start;
13
- this.#end = end;
14
10
  }
15
11
 
16
12
  _transform(chunk: any, _: BufferEncoding, done: TransformCallback): void {
17
- this.#offset += chunk.length;
13
+ this.indexOffset += chunk.length;
18
14
 
19
- if (!this.#emitUp && this.#offset >= this.#start) {
20
- this.#emitUp = true;
21
- const start = chunk.length - (this.#offset - this.#start);
15
+ if (!this.emitUp && this.indexOffset >= this.startIndex) {
16
+ this.emitUp = true;
17
+ const start = chunk.length - (this.indexOffset - this.startIndex);
22
18
 
23
- if (this.#offset > this.#end) {
24
- const end = chunk.length - (this.#offset - this.#end);
25
- this.#emitDown = true;
19
+ if (this.indexOffset > this.endIndex) {
20
+ const end = chunk.length - (this.indexOffset - this.endIndex);
21
+ this.emitDown = true;
26
22
  this.push(chunk.slice(start, end));
27
23
  } else {
28
24
  this.push(chunk.slice(start, chunk.length));
@@ -31,10 +27,12 @@ class SliceStream extends Transform {
31
27
  return done();
32
28
  }
33
29
 
34
- if (this.#emitUp && !this.#emitDown) {
35
- if (this.#offset >= this.#end) {
36
- this.#emitDown = true;
37
- this.push(chunk.slice(0, chunk.length - (this.#offset - this.#end)));
30
+ if (this.emitUp && !this.emitDown) {
31
+ if (this.indexOffset >= this.endIndex) {
32
+ this.emitDown = true;
33
+ this.push(
34
+ chunk.slice(0, chunk.length - (this.indexOffset - this.endIndex)),
35
+ );
38
36
  } else {
39
37
  this.push(chunk);
40
38
  }
@@ -46,6 +44,6 @@ class SliceStream extends Transform {
46
44
  }
47
45
  }
48
46
 
49
- export function streamSlice(start = 0, end = Infinity): SliceStream {
50
- return new SliceStream(start, end);
47
+ export function streamSlice(startIndex = 0, endIndex = Infinity): SliceStream {
48
+ return new SliceStream(startIndex, endIndex);
51
49
  }
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
2
  import { AnyRouter } from '../../core';
3
3
  import { inferRouterContext } from '../../core/types';
4
- import { HTTPRequest } from '../../http';
4
+ import { getBatchStreamFormatter, HTTPRequest } from '../../http';
5
+ import { HTTPResponse, ResponseChunk } from '../../http/internals/types';
5
6
  import { resolveHTTPResponse } from '../../http/resolveHTTPResponse';
6
7
  import { nodeHTTPJSONContentTypeHandler } from './content-type/json';
7
8
  import { NodeHTTPContentTypeHandler } from './internals/contentType';
@@ -63,7 +64,47 @@ export async function nodeHTTPRequestHandler<
63
64
  body: bodyResult.ok ? bodyResult.data : undefined,
64
65
  };
65
66
 
66
- const result = await resolveHTTPResponse({
67
+ const onHead = (head: HTTPResponse) => {
68
+ if (
69
+ 'status' in head &&
70
+ (!opts.res.statusCode || opts.res.statusCode === 200)
71
+ ) {
72
+ opts.res.statusCode = head.status;
73
+ }
74
+ for (const [key, value] of Object.entries(head.headers ?? {})) {
75
+ /* istanbul ignore if -- @preserve */
76
+ if (typeof value === 'undefined') {
77
+ continue;
78
+ }
79
+ opts.res.setHeader(key, value);
80
+ }
81
+ };
82
+
83
+ const formatter = getBatchStreamFormatter();
84
+ let isStream = false;
85
+ const onChunk = ([index, string]: ResponseChunk) => {
86
+ if (index === -1) {
87
+ /**
88
+ * Full response, no streaming. This can happen
89
+ * - if the response is an error
90
+ * - if response is empty (HEAD request)
91
+ */
92
+ opts.res.end(string);
93
+ return;
94
+ }
95
+ if (!isStream) {
96
+ opts.res.setHeader('Transfer-Encoding', 'chunked');
97
+ const vary = opts.res.getHeader('Vary');
98
+ opts.res.setHeader(
99
+ 'Vary',
100
+ vary ? 'trpc-batch-mode, ' + vary : 'trpc-batch-mode',
101
+ );
102
+ isStream = true;
103
+ }
104
+ opts.res.write(formatter(index, string));
105
+ };
106
+
107
+ await resolveHTTPResponse({
67
108
  batching: opts.batching,
68
109
  responseMeta: opts.responseMeta,
69
110
  path: opts.path,
@@ -79,18 +120,16 @@ export async function nodeHTTPRequestHandler<
79
120
  });
80
121
  },
81
122
  contentTypeHandler,
123
+ onHead,
124
+ onChunk,
82
125
  });
83
126
 
84
- const { res } = opts;
85
- if ('status' in result && (!res.statusCode || res.statusCode === 200)) {
86
- res.statusCode = result.status;
127
+ if (!isStream) {
128
+ return opts.res;
87
129
  }
88
- for (const [key, value] of Object.entries(result.headers ?? {})) {
89
- if (typeof value === 'undefined') {
90
- continue;
91
- }
92
- res.setHeader(key, value);
93
- }
94
- res.end(result.body);
130
+
131
+ opts.res.write(formatter.end());
132
+ opts.res.end();
133
+ return opts.res;
95
134
  });
96
135
  }
@@ -2,14 +2,14 @@ import { IncomingMessage } from 'http';
2
2
  import ws from 'ws';
3
3
  import {
4
4
  AnyRouter,
5
- ProcedureType,
6
5
  callProcedure,
7
6
  inferRouterContext,
7
+ ProcedureType,
8
8
  } from '../core';
9
- import { TRPCError, getTRPCErrorFromUnknown } from '../error/TRPCError';
9
+ import { getTRPCErrorFromUnknown, TRPCError } from '../error/TRPCError';
10
10
  import { getCauseFromUnknown } from '../error/utils';
11
11
  import { BaseHandlerOptions } from '../internals/types';
12
- import { Unsubscribable, isObservable } from '../observable';
12
+ import { isObservable, Unsubscribable } from '../observable';
13
13
  import {
14
14
  JSONRPC2,
15
15
  TRPCClientOutgoingMessage,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  DefaultErrorShape,
3
+ defaultFormatter,
3
4
  ErrorFormatter,
4
5
  ErrorFormatterShape,
5
- defaultFormatter,
6
6
  } from '../error/formatter';
7
7
  import { createFlatProxy } from '../shared/createProxy';
8
8
  import {
@@ -14,10 +14,10 @@ import {
14
14
  import { FlatOverwrite, Unwrap } from '../types';
15
15
  import {
16
16
  CreateRootConfigTypes,
17
+ isServerDefault,
17
18
  RootConfig,
18
19
  RootConfigTypes,
19
20
  RuntimeConfig,
20
- isServerDefault,
21
21
  } from './internals/config';
22
22
  import { mergeRouters } from './internals/mergeRouters';
23
23
  import { createBuilder } from './internals/procedureBuilder';
@@ -3,9 +3,9 @@ import { CombinedDataTransformer, defaultTransformer } from '../../transformer';
3
3
  import {
4
4
  AnyRouter,
5
5
  AnyRouterDef,
6
+ createRouterFactory,
6
7
  Router,
7
8
  RouterDef,
8
- createRouterFactory,
9
9
  } from '../router';
10
10
  import { mergeWithoutOverrides } from './mergeWithoutOverrides';
11
11
 
@@ -1,13 +1,13 @@
1
- import { TRPCError, getTRPCErrorFromUnknown } from '../../error/TRPCError';
1
+ import { getTRPCErrorFromUnknown, TRPCError } from '../../error/TRPCError';
2
2
  import { MaybePromise, Simplify } from '../../types';
3
3
  import {
4
+ createInputMiddleware,
5
+ createOutputMiddleware,
4
6
  MiddlewareBuilder,
5
7
  MiddlewareFunction,
6
8
  MiddlewareResult,
7
- createInputMiddleware,
8
- createOutputMiddleware,
9
9
  } from '../middleware';
10
- import { Parser, inferParser } from '../parser';
10
+ import { inferParser, Parser } from '../parser';
11
11
  import {
12
12
  AnyMutationProcedure,
13
13
  AnyProcedure,
@@ -22,11 +22,11 @@ import { getParseFn } from './getParseFn';
22
22
  import { mergeWithoutOverrides } from './mergeWithoutOverrides';
23
23
  import {
24
24
  DefaultValue as FallbackValue,
25
+ middlewareMarker,
25
26
  Overwrite,
26
27
  OverwriteKnown,
27
28
  ResolveOptions,
28
29
  UnsetMarker,
29
- middlewareMarker,
30
30
  } from './utils';
31
31
 
32
32
  type CreateProcedureReturnInput<
@@ -1,5 +1,5 @@
1
- import { TRPCError } from '../error/TRPCError';
2
1
  import { DefaultErrorShape, defaultFormatter } from '../error/formatter';
2
+ import { TRPCError } from '../error/TRPCError';
3
3
  import { getHTTPStatusCodeFromError } from '../http/getHTTPStatusCode';
4
4
  import { TRPC_ERROR_CODES_BY_KEY } from '../rpc';
5
5
  import { createRecursiveProxy } from '../shared/createProxy';
@@ -15,9 +15,9 @@ import {
15
15
  ProcedureArgs,
16
16
  } from './procedure';
17
17
  import {
18
- ProcedureType,
19
18
  inferHandlerInput,
20
19
  inferProcedureOutput,
20
+ ProcedureType,
21
21
  procedureTypes,
22
22
  } from './types';
23
23
 
@@ -1,11 +1,11 @@
1
- import { TRPCError, getTRPCErrorFromUnknown } from '../../error/TRPCError';
1
+ import { getTRPCErrorFromUnknown, TRPCError } from '../../error/TRPCError';
2
2
  import { getCauseFromUnknown } from '../../error/utils';
3
3
  import { InferLast } from '../../types';
4
4
  import { ProcedureType } from '../router';
5
5
  import {
6
6
  MiddlewareFunction,
7
- MiddlewareResult,
8
7
  middlewareMarker,
8
+ MiddlewareResult,
9
9
  } from './middlewares';
10
10
 
11
11
  export type ProcedureParserZodEsque<TInput, TOutput> = {
@@ -10,10 +10,10 @@ import {
10
10
  } from '../core/middleware';
11
11
  import { Procedure } from '../core/procedure';
12
12
  import {
13
+ createRouterFactory,
13
14
  ProcedureRecord as NewProcedureRecord,
14
15
  Router as NewRouter,
15
16
  RouterDef,
16
- createRouterFactory,
17
17
  } from '../core/router';
18
18
  import {
19
19
  AnyRouter as AnyOldRouter,
@@ -1,13 +1,13 @@
1
1
  /* eslint-disable @typescript-eslint/ban-types */
2
- import { TRPCError } from '../error/TRPCError';
3
2
  import { defaultFormatter } from '../error/formatter';
3
+ import { TRPCError } from '../error/TRPCError';
4
4
  import { getHTTPStatusCodeFromError } from '../http/getHTTPStatusCode';
5
- import { Observable, inferObservableValue } from '../observable';
5
+ import { inferObservableValue, Observable } from '../observable';
6
6
  import {
7
- TRPCErrorShape,
8
- TRPC_ERROR_CODES_BY_KEY,
9
7
  TRPC_ERROR_CODE_KEY,
10
8
  TRPC_ERROR_CODE_NUMBER,
9
+ TRPC_ERROR_CODES_BY_KEY,
10
+ TRPCErrorShape,
11
11
  } from '../rpc';
12
12
  import {
13
13
  CombinedDataTransformer,
@@ -18,14 +18,14 @@ import {
18
18
  import { FlatOverwrite, ThenArg } from '../types';
19
19
  import { MiddlewareFunction } from './internals/middlewares';
20
20
  import {
21
+ createProcedure,
21
22
  CreateProcedureOptions,
22
23
  CreateProcedureWithInput,
23
24
  CreateProcedureWithInputOutputParser,
24
25
  CreateProcedureWithoutInput,
26
+ inferProcedureFromOptions,
25
27
  Procedure,
26
28
  ProcedureCallOptions,
27
- createProcedure,
28
- inferProcedureFromOptions,
29
29
  } from './internals/procedure';
30
30
  import { MigrateRouter, migrateRouter } from './interop';
31
31
 
@@ -1,8 +1,8 @@
1
1
  import { ProcedureType } from '../core';
2
2
  import {
3
- TRPCErrorShape,
4
3
  TRPC_ERROR_CODE_KEY,
5
4
  TRPC_ERROR_CODE_NUMBER,
5
+ TRPCErrorShape,
6
6
  } from '../rpc';
7
7
  import { TRPCError } from './TRPCError';
8
8
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Format a batch response as a line-delimited JSON stream
3
+ * that the `unstable_httpBatchStreamLink` can parse:
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const formatter = getBatchStreamFormatter();
8
+ * res.send(formatter(1, 'response #2'));
9
+ * res.send(formatter(0, 'response #1'));
10
+ * res.send(formatter.end());
11
+ * ```
12
+ *
13
+ * Expected format:
14
+ * ```json
15
+ * {"1":"response #2"
16
+ * ,"0":"response #1"
17
+ * }
18
+ * ```
19
+ */
20
+ export function getBatchStreamFormatter() {
21
+ let first = true;
22
+ function format(index: number, string: string) {
23
+ const prefix = first ? '{' : ',';
24
+ first = false;
25
+ return `${prefix}"${index}":${string}\n`;
26
+ }
27
+ format.end = () => '}';
28
+ return format;
29
+ }
@@ -1,6 +1,6 @@
1
1
  import { TRPCError } from '../error/TRPCError';
2
2
  import { invert } from '../internals/invert';
3
- import { TRPCResponse, TRPC_ERROR_CODES_BY_KEY } from '../rpc';
3
+ import { TRPC_ERROR_CODES_BY_KEY, TRPCResponse } from '../rpc';
4
4
 
5
5
  export const TRPC_ERROR_CODES_BY_NUMBER = invert(TRPC_ERROR_CODES_BY_KEY);
6
6
  type ValueOf<TType> = TType[keyof TType];
package/src/http/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './getHTTPStatusCode';
2
2
  export * from './resolveHTTPResponse';
3
3
  export * from './types';
4
+ export * from './batchStreamFormatter';
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  AnyRouter,
3
- ProcedureType,
4
3
  inferRouterContext,
5
4
  inferRouterError,
5
+ ProcedureType,
6
6
  } from '../../core';
7
7
  import { TRPCError } from '../../error/TRPCError';
8
8
  import { TRPCResponse } from '../../rpc';
@@ -17,6 +17,8 @@ export interface HTTPResponse {
17
17
  body?: string;
18
18
  }
19
19
 
20
+ export type ResponseChunk = [procedureIndex: number, responseBody: string];
21
+
20
22
  /**
21
23
  * @internal
22
24
  */
@@ -29,4 +31,10 @@ export type ResponseMetaFn<TRouter extends AnyRouter> = (opts: {
29
31
  paths?: string[];
30
32
  type: ProcedureType | 'unknown';
31
33
  errors: TRPCError[];
34
+ /**
35
+ * `true` if the `ResponseMeta` are being
36
+ * generated without knowing the response data
37
+ * (e.g. for streaming requests).
38
+ */
39
+ eagerGeneration: boolean;
32
40
  }) => ResponseMeta;