@trpc/server 11.0.0-rc.608 → 11.0.0-rc.621

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 (36) hide show
  1. package/dist/@trpc/server/http.d.ts +1 -1
  2. package/dist/@trpc/server/http.d.ts.map +1 -1
  3. package/dist/adapters/fetch/fetchRequestHandler.js +1 -2
  4. package/dist/adapters/fetch/fetchRequestHandler.mjs +1 -2
  5. package/dist/adapters/node-http/incomingMessageToRequest.d.ts +1 -0
  6. package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
  7. package/dist/adapters/node-http/incomingMessageToRequest.js +79 -40
  8. package/dist/adapters/node-http/incomingMessageToRequest.mjs +79 -41
  9. package/dist/adapters/node-http/index.js +1 -0
  10. package/dist/adapters/node-http/index.mjs +1 -1
  11. package/dist/adapters/standalone.d.ts.map +1 -1
  12. package/dist/adapters/standalone.js +2 -2
  13. package/dist/adapters/standalone.mjs +2 -2
  14. package/dist/adapters/ws.d.ts +1 -1
  15. package/dist/adapters/ws.d.ts.map +1 -1
  16. package/dist/adapters/ws.js +2 -2
  17. package/dist/adapters/ws.mjs +2 -2
  18. package/dist/bundle-analysis.json +86 -100
  19. package/dist/http.js +0 -2
  20. package/dist/http.mjs +0 -1
  21. package/dist/unstable-core-do-not-import.d.ts +0 -1
  22. package/dist/unstable-core-do-not-import.d.ts.map +1 -1
  23. package/dist/unstable-core-do-not-import.js +0 -2
  24. package/dist/unstable-core-do-not-import.mjs +0 -1
  25. package/package.json +2 -2
  26. package/src/@trpc/server/http.ts +0 -1
  27. package/src/adapters/fetch/fetchRequestHandler.ts +2 -2
  28. package/src/adapters/node-http/incomingMessageToRequest.ts +106 -41
  29. package/src/adapters/standalone.ts +7 -4
  30. package/src/adapters/ws.ts +3 -3
  31. package/src/unstable-core-do-not-import.ts +0 -1
  32. package/dist/unstable-core-do-not-import/http/toURL.d.ts +0 -2
  33. package/dist/unstable-core-do-not-import/http/toURL.d.ts.map +0 -1
  34. package/dist/unstable-core-do-not-import/http/toURL.js +0 -18
  35. package/dist/unstable-core-do-not-import/http/toURL.mjs +0 -16
  36. package/src/unstable-core-do-not-import/http/toURL.ts +0 -17
@@ -1,6 +1,5 @@
1
1
  import type * as http from 'http';
2
2
  import { TRPCError } from '../../@trpc/server';
3
- import { toURL } from '../../http';
4
3
 
5
4
  export interface IncomingMessageWithBody extends http.IncomingMessage {
6
5
  /**
@@ -8,52 +7,112 @@ export interface IncomingMessageWithBody extends http.IncomingMessage {
8
7
  */
9
8
  body?: unknown;
10
9
  }
11
- /**
12
- * Convert an incoming message to a body stream with a max size
13
- */
14
- function incomingMessageToBodyStream(
15
- req: IncomingMessageWithBody,
16
- opts: { maxBodySize: number | null },
17
- ) {
18
- type Value = Buffer | Uint8Array | string | null;
10
+
11
+ function createBody(
12
+ req: http.IncomingMessage,
13
+ opts: {
14
+ /**
15
+ * Max body size in bytes. If the body is larger than this, the request will be aborted
16
+ */
17
+ maxBodySize: number | null;
18
+ },
19
+ ): RequestInit['body'] {
20
+ // Some adapters will pre-parse the body and add it to the request object
21
+ if ('body' in req) {
22
+ // If the body is already a string, return it directly
23
+ if (typeof req.body === 'string') {
24
+ return req.body;
25
+ }
26
+ // If body exists but isn't a string, stringify it as JSON
27
+ else if (req.body !== undefined) {
28
+ return JSON.stringify(req.body);
29
+ }
30
+ // If body property exists but is undefined, return undefined
31
+ return undefined;
32
+ }
19
33
  let size = 0;
20
- const maxBodySize = opts.maxBodySize;
21
34
  let hasClosed = false;
22
35
 
23
- const stream = new ReadableStream<Value>({
36
+ return new ReadableStream({
24
37
  start(controller) {
25
- req.on('data', (chunk) => {
38
+ const onData = (chunk: Buffer) => {
26
39
  size += chunk.length;
27
- if (maxBodySize != null && size > maxBodySize) {
28
- controller.error(
29
- new TRPCError({
30
- code: 'PAYLOAD_TOO_LARGE',
31
- }),
40
+ if (!opts.maxBodySize || size <= opts.maxBodySize) {
41
+ controller.enqueue(
42
+ new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength),
32
43
  );
33
- // an error is thrown if we try to close the controller after
34
- // erroring, so track the closure
35
- hasClosed = true;
36
44
  return;
37
45
  }
38
- controller.enqueue(chunk);
39
- });
40
- req.once('end', () => {
46
+ controller.error(
47
+ new TRPCError({
48
+ code: 'PAYLOAD_TOO_LARGE',
49
+ }),
50
+ );
51
+ hasClosed = true;
52
+ req.off('data', onData);
53
+ req.off('end', onEnd);
54
+ };
55
+
56
+ const onEnd = () => {
41
57
  if (hasClosed) {
42
58
  return;
43
59
  }
44
60
  hasClosed = true;
61
+ req.off('data', onData);
62
+ req.off('end', onEnd);
45
63
  controller.close();
46
- });
64
+ };
65
+
66
+ req.on('data', onData);
67
+ req.on('end', onEnd);
47
68
  },
48
69
  cancel() {
49
70
  req.destroy();
50
71
  },
51
72
  });
73
+ }
74
+ export function createURL(req: http.IncomingMessage): URL {
75
+ try {
76
+ const protocol =
77
+ req.socket && 'encrypted' in req.socket && req.socket.encrypted
78
+ ? 'https:'
79
+ : 'http:';
80
+
81
+ const host = req.headers.host ?? 'localhost';
52
82
 
53
- return stream;
83
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84
+ return new URL(req.url!, `${protocol}//${host}`);
85
+ } catch (cause) {
86
+ throw new TRPCError({
87
+ code: 'BAD_REQUEST',
88
+ message: 'Invalid URL',
89
+ cause,
90
+ });
91
+ }
92
+ }
93
+
94
+ function createHeaders(incoming: http.IncomingHttpHeaders): Headers {
95
+ const headers = new Headers();
96
+
97
+ for (const key in incoming) {
98
+ const value = incoming[key];
99
+ if (typeof key === 'string' && key.startsWith(':')) {
100
+ // Skip HTTP/2 pseudo-headers
101
+ continue;
102
+ }
103
+
104
+ if (Array.isArray(value)) {
105
+ for (const item of value) {
106
+ headers.append(key, item);
107
+ }
108
+ } else if (value != null) {
109
+ headers.append(key, value);
110
+ }
111
+ }
112
+
113
+ return headers;
54
114
  }
55
115
 
56
- const bodyMethods = ['POST', 'PUT', 'PATCH'];
57
116
  /**
58
117
  * Convert an [`IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
59
118
  */
@@ -67,29 +126,35 @@ export function incomingMessageToRequest(
67
126
  },
68
127
  ): Request {
69
128
  const ac = new AbortController();
70
- const headers = new Headers(req.headers as any);
71
- const url = toURL(`http://${headers.get('host')}${req.url}`);
72
- req.once('aborted', () => {
129
+
130
+ const onAbort = () => {
73
131
  ac.abort();
74
- });
132
+ req.off('aborted', onAbort);
133
+ req.off('close', onAbort);
134
+ };
135
+ req.once('aborted', onAbort);
136
+ req.socket?.once('close', onAbort);
137
+
138
+ // Get host from either regular header or HTTP/2 pseudo-header
139
+ const url = createURL(req);
75
140
 
76
141
  const init: RequestInit = {
77
- headers,
142
+ headers: createHeaders(req.headers),
78
143
  method: req.method,
79
144
  signal: ac.signal,
80
- // @ts-expect-error this is fine
81
- duplex: 'half',
82
145
  };
83
146
 
84
- if (req.method && bodyMethods.includes(req.method)) {
85
- if (!('body' in req)) {
86
- init.body = incomingMessageToBodyStream(req, opts);
87
- } else if (typeof req.body === 'string') {
88
- init.body = req.body;
89
- } else if (req.body !== undefined) {
90
- init.body = JSON.stringify(req.body);
91
- }
147
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
148
+ init.body = createBody(req, opts);
149
+
150
+ // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec.
151
+ // However, this property is not defined in the TypeScript types for RequestInit, so we have
152
+ // to cast it here in order to set it without a type error.
153
+ // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex
154
+ // @ts-expect-error this is fine
155
+ init.duplex = 'half';
92
156
  }
157
+
93
158
  const request = new Request(url, init);
94
159
 
95
160
  return request;
@@ -7,18 +7,21 @@
7
7
  * import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
8
8
  * ```
9
9
  */
10
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
10
+
11
11
  import http from 'http';
12
12
  // @trpc/server
13
13
  import { type AnyRouter } from '../@trpc/server';
14
- import { toURL } from '../@trpc/server/http';
15
14
  // eslint-disable-next-line no-restricted-imports
16
15
  import { run } from '../unstable-core-do-not-import';
17
16
  import type {
18
17
  NodeHTTPCreateContextFnOptions,
19
18
  NodeHTTPHandlerOptions,
20
19
  } from './node-http';
21
- import { internal_exceptionHandler, nodeHTTPRequestHandler } from './node-http';
20
+ import {
21
+ createURL,
22
+ internal_exceptionHandler,
23
+ nodeHTTPRequestHandler,
24
+ } from './node-http';
22
25
 
23
26
  export type CreateHTTPHandlerOptions<TRouter extends AnyRouter> =
24
27
  NodeHTTPHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;
@@ -37,7 +40,7 @@ export function createHTTPHandler<TRouter extends AnyRouter>(
37
40
  return (req, res) => {
38
41
  let path = '';
39
42
  run(async () => {
40
- const url = toURL(req.url!);
43
+ const url = createURL(req);
41
44
 
42
45
  // get procedure path and remove the leading slash
43
46
  // /procedure -> procedure
@@ -13,7 +13,7 @@ import {
13
13
  TRPCError,
14
14
  } from '../@trpc/server';
15
15
  import type { TRPCRequestInfo } from '../@trpc/server/http';
16
- import { toURL, type BaseHandlerOptions } from '../@trpc/server/http';
16
+ import { type BaseHandlerOptions } from '../@trpc/server/http';
17
17
  import { parseTRPCMessage } from '../@trpc/server/rpc';
18
18
  // @trpc/server/rpc
19
19
  import type {
@@ -34,7 +34,7 @@ import {
34
34
  type MaybePromise,
35
35
  } from '../unstable-core-do-not-import';
36
36
  import { Unpromise } from '../vendor/unpromise';
37
- import type { NodeHTTPCreateContextFnOptions } from './node-http';
37
+ import { createURL, type NodeHTTPCreateContextFnOptions } from './node-http';
38
38
 
39
39
  /**
40
40
  * Importing ws causes a build error
@@ -178,7 +178,7 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
178
178
  * - if connection params are expected, they will be created once received
179
179
  */
180
180
  let ctxPromise =
181
- toURL(req.url ?? '').searchParams.get('connectionParams') === '1'
181
+ createURL(req).searchParams.get('connectionParams') === '1'
182
182
  ? unsetContextPromiseSymbol
183
183
  : createCtxPromise(() => null);
184
184
 
@@ -22,7 +22,6 @@ export * from './unstable-core-do-not-import/http/formDataToObject';
22
22
  export * from './unstable-core-do-not-import/http/getHTTPStatusCode';
23
23
  export * from './unstable-core-do-not-import/http/parseConnectionParams';
24
24
  export * from './unstable-core-do-not-import/http/resolveResponse';
25
- export * from './unstable-core-do-not-import/http/toURL';
26
25
  export * from './unstable-core-do-not-import/http/types';
27
26
  export * from './unstable-core-do-not-import/initTRPC';
28
27
  export * from './unstable-core-do-not-import/middleware';
@@ -1,2 +0,0 @@
1
- export declare function toURL(urlOrPathname: string): URL;
2
- //# sourceMappingURL=toURL.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"toURL.d.ts","sourceRoot":"","sources":["../../../src/unstable-core-do-not-import/http/toURL.ts"],"names":[],"mappings":"AAEA,wBAAgB,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAchD"}
@@ -1,18 +0,0 @@
1
- 'use strict';
2
-
3
- var TRPCError = require('../error/TRPCError.js');
4
-
5
- function toURL(urlOrPathname) {
6
- try {
7
- const url = urlOrPathname.startsWith('/') ? `http://127.0.0.1${urlOrPathname}` : urlOrPathname;
8
- return new URL(url);
9
- } catch (cause) {
10
- throw new TRPCError.TRPCError({
11
- code: 'BAD_REQUEST',
12
- message: 'Invalid URL',
13
- cause
14
- });
15
- }
16
- }
17
-
18
- exports.toURL = toURL;
@@ -1,16 +0,0 @@
1
- import { TRPCError } from '../error/TRPCError.mjs';
2
-
3
- function toURL(urlOrPathname) {
4
- try {
5
- const url = urlOrPathname.startsWith('/') ? `http://127.0.0.1${urlOrPathname}` : urlOrPathname;
6
- return new URL(url);
7
- } catch (cause) {
8
- throw new TRPCError({
9
- code: 'BAD_REQUEST',
10
- message: 'Invalid URL',
11
- cause
12
- });
13
- }
14
- }
15
-
16
- export { toURL };
@@ -1,17 +0,0 @@
1
- import { TRPCError } from '../error/TRPCError';
2
-
3
- export function toURL(urlOrPathname: string): URL {
4
- try {
5
- const url = urlOrPathname.startsWith('/')
6
- ? `http://127.0.0.1${urlOrPathname}`
7
- : urlOrPathname;
8
-
9
- return new URL(url);
10
- } catch (cause) {
11
- throw new TRPCError({
12
- code: 'BAD_REQUEST',
13
- message: 'Invalid URL',
14
- cause,
15
- });
16
- }
17
- }