@trpc/server 11.0.0-rc.566 → 11.0.0-rc.567

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 (29) hide show
  1. package/dist/adapters/express.js +4 -4
  2. package/dist/adapters/express.mjs +4 -4
  3. package/dist/adapters/next.d.ts.map +1 -1
  4. package/dist/adapters/next.js +26 -32
  5. package/dist/adapters/next.mjs +27 -33
  6. package/dist/adapters/node-http/incomingMessageToRequest.d.ts.map +1 -1
  7. package/dist/adapters/node-http/incomingMessageToRequest.js +2 -1
  8. package/dist/adapters/node-http/incomingMessageToRequest.mjs +2 -1
  9. package/dist/adapters/node-http/index.js +1 -0
  10. package/dist/adapters/node-http/index.mjs +1 -1
  11. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts +5 -1
  12. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  13. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +91 -56
  14. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +91 -57
  15. package/dist/adapters/standalone.d.ts +5 -2
  16. package/dist/adapters/standalone.d.ts.map +1 -1
  17. package/dist/adapters/standalone.js +25 -14
  18. package/dist/adapters/standalone.mjs +26 -15
  19. package/dist/bundle-analysis.json +113 -108
  20. package/dist/unstable-core-do-not-import/http/toURL.d.ts.map +1 -1
  21. package/dist/unstable-core-do-not-import/http/toURL.js +12 -2
  22. package/dist/unstable-core-do-not-import/http/toURL.mjs +12 -2
  23. package/package.json +2 -2
  24. package/src/adapters/express.ts +4 -4
  25. package/src/adapters/next.ts +33 -35
  26. package/src/adapters/node-http/incomingMessageToRequest.ts +2 -1
  27. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +109 -62
  28. package/src/adapters/standalone.ts +32 -16
  29. package/src/unstable-core-do-not-import/http/toURL.ts +14 -4
@@ -23,14 +23,14 @@ export type CreateExpressContextOptions = NodeHTTPCreateContextFnOptions<
23
23
  export function createExpressMiddleware<TRouter extends AnyRouter>(
24
24
  opts: NodeHTTPHandlerOptions<TRouter, express.Request, express.Response>,
25
25
  ): express.Handler {
26
- return async (req, res) => {
27
- const endpoint = req.path.slice(1);
26
+ return (req, res) => {
27
+ const path = req.path.slice(1);
28
28
 
29
- await nodeHTTPRequestHandler({
29
+ nodeHTTPRequestHandler({
30
30
  ...(opts as any),
31
31
  req,
32
32
  res,
33
- path: endpoint,
33
+ path,
34
34
  });
35
35
  };
36
36
  }
@@ -11,12 +11,14 @@ import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
11
11
  // @trpc/server
12
12
  import type { AnyRouter } from '../@trpc/server';
13
13
  // @trpc/server
14
- import { getErrorShape, TRPCError } from '../@trpc/server';
14
+ import { TRPCError } from '../@trpc/server';
15
+ // eslint-disable-next-line no-restricted-imports
16
+ import { run } from '../unstable-core-do-not-import';
15
17
  import type {
16
18
  NodeHTTPCreateContextFnOptions,
17
19
  NodeHTTPHandlerOptions,
18
20
  } from './node-http';
19
- import { nodeHTTPRequestHandler } from './node-http';
21
+ import { internal_exceptionHandler, nodeHTTPRequestHandler } from './node-http';
20
22
 
21
23
  export type CreateNextContextOptions = NodeHTTPCreateContextFnOptions<
22
24
  NextApiRequest,
@@ -28,47 +30,43 @@ export type CreateNextContextOptions = NodeHTTPCreateContextFnOptions<
28
30
  */
29
31
  export type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
30
32
 
33
+ type SyncNextApiHandler = (req: NextApiRequest, res: NextApiResponse) => void;
34
+
31
35
  export function createNextApiHandler<TRouter extends AnyRouter>(
32
36
  opts: NodeHTTPHandlerOptions<TRouter, NextApiRequest, NextApiResponse>,
33
37
  ): NextApiHandler {
34
- return async (req, res) => {
35
- function getPath(): string | null {
36
- if (typeof req.query['trpc'] === 'string') {
37
- return req.query['trpc'];
38
- }
39
- if (Array.isArray(req.query['trpc'])) {
40
- return req.query['trpc'].join('/');
41
- }
42
- return null;
43
- }
44
- const path = getPath();
45
-
46
- if (path === null) {
47
- const error = getErrorShape({
48
- config: opts.router._def._config,
49
- error: new TRPCError({
38
+ let path = '';
39
+ const handler: SyncNextApiHandler = (req, res) => {
40
+ try {
41
+ path = run(() => {
42
+ if (typeof req.query['trpc'] === 'string') {
43
+ return req.query['trpc'];
44
+ }
45
+ if (Array.isArray(req.query['trpc'])) {
46
+ return req.query['trpc'].join('/');
47
+ }
48
+ throw new TRPCError({
50
49
  message:
51
50
  'Query "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?',
52
51
  code: 'INTERNAL_SERVER_ERROR',
53
- }),
54
- type: 'unknown',
55
- ctx: undefined,
56
- path: undefined,
57
- input: undefined,
52
+ });
58
53
  });
59
- res.statusCode = 500;
60
- res.json({
61
- id: -1,
62
- error,
54
+
55
+ nodeHTTPRequestHandler({
56
+ ...(opts as any),
57
+ req,
58
+ res,
59
+ path,
63
60
  });
64
- return;
61
+ } catch (cause) {
62
+ internal_exceptionHandler({
63
+ req,
64
+ res,
65
+ path,
66
+ ...opts,
67
+ })(cause);
65
68
  }
66
-
67
- await nodeHTTPRequestHandler({
68
- ...(opts as any),
69
- req,
70
- res,
71
- path,
72
- });
73
69
  };
70
+
71
+ return handler;
74
72
  }
@@ -1,5 +1,6 @@
1
1
  import type * as http from 'http';
2
2
  import { TRPCError } from '../../@trpc/server';
3
+ import { toURL } from '../../http';
3
4
 
4
5
  export interface IncomingMessageWithBody extends http.IncomingMessage {
5
6
  /**
@@ -67,7 +68,7 @@ export function incomingMessageToRequest(
67
68
  ): Request {
68
69
  const ac = new AbortController();
69
70
  const headers = new Headers(req.headers as any);
70
- const url = `http://${headers.get('host')}${req.url}`;
71
+ const url = toURL(`http://${headers.get('host')}${req.url}`);
71
72
  req.once('aborted', () => {
72
73
  ac.abort();
73
74
  });
@@ -10,9 +10,15 @@
10
10
 
11
11
  // @trpc/server
12
12
 
13
- import { getTRPCErrorFromUnknown, type AnyRouter } from '../../@trpc/server';
13
+ import {
14
+ getTRPCErrorFromUnknown,
15
+ transformTRPCResponse,
16
+ type AnyRouter,
17
+ } from '../../@trpc/server';
14
18
  import type { ResolveHTTPRequestOptionsContextFn } from '../../@trpc/server/http';
15
19
  import { resolveResponse } from '../../@trpc/server/http';
20
+ // eslint-disable-next-line no-restricted-imports
21
+ import { getErrorShape, run } from '../../unstable-core-do-not-import';
16
22
  import { incomingMessageToRequest } from './incomingMessageToRequest';
17
23
  import type {
18
24
  NodeHTTPRequest,
@@ -20,84 +26,125 @@ import type {
20
26
  NodeHTTPResponse,
21
27
  } from './types';
22
28
 
23
- export async function nodeHTTPRequestHandler<
29
+ /**
30
+ * @internal
31
+ */
32
+ export function internal_exceptionHandler<
24
33
  TRouter extends AnyRouter,
25
34
  TRequest extends NodeHTTPRequest,
26
35
  TResponse extends NodeHTTPResponse,
27
36
  >(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {
28
- const handleViaMiddleware = opts.middleware ?? ((_req, _res, next) => next());
37
+ return (cause: unknown) => {
38
+ const { res, req } = opts;
39
+ const error = getTRPCErrorFromUnknown(cause);
29
40
 
30
- return handleViaMiddleware(opts.req, opts.res, async (err) => {
31
- const req = incomingMessageToRequest(opts.req, {
32
- maxBodySize: opts.maxBodySize ?? null,
41
+ const shape = getErrorShape({
42
+ config: opts.router._def._config,
43
+ error,
44
+ type: 'unknown',
45
+ path: undefined,
46
+ input: undefined,
47
+ ctx: undefined,
33
48
  });
34
49
 
35
- // Build tRPC dependencies
36
- const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (
37
- innerOpts,
38
- ) => {
39
- return await opts.createContext?.({
40
- ...opts,
41
- ...innerOpts,
42
- });
43
- };
44
-
45
- const response = await resolveResponse({
46
- ...opts,
50
+ opts.onError?.({
47
51
  req,
48
- error: err ? getTRPCErrorFromUnknown(err) : null,
49
- createContext,
50
- onError(o) {
51
- opts?.onError?.({
52
- ...o,
53
- req: opts.req,
54
- });
55
- },
52
+ error,
53
+ type: 'unknown',
54
+ path: undefined,
55
+ input: undefined,
56
+ ctx: undefined,
57
+ });
58
+
59
+ const transformed = transformTRPCResponse(opts.router._def._config, {
60
+ error: shape,
56
61
  });
57
62
 
58
- const { res } = opts;
59
- if (res.statusCode === 200) {
60
- // if the status code is set, we assume that it's been manually overridden
61
- res.statusCode = response.status;
62
- }
63
- for (const [key, value] of response.headers) {
64
- res.setHeader(key, value);
65
- }
66
- if (response.body) {
67
- const reader = response.body.getReader();
68
- const onAbort = () => {
69
- // cancelling the reader will cause the whole stream to be cancelled
70
- reader.cancel().catch(() => {
71
- // console.error('reader.cancel() error', err);
63
+ res.statusCode = shape.data.httpStatus;
64
+ res.end(JSON.stringify(transformed));
65
+ };
66
+ }
67
+
68
+ export function nodeHTTPRequestHandler<
69
+ TRouter extends AnyRouter,
70
+ TRequest extends NodeHTTPRequest,
71
+ TResponse extends NodeHTTPResponse,
72
+ >(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {
73
+ const handleViaMiddleware = opts.middleware ?? ((_req, _res, next) => next());
74
+
75
+ return handleViaMiddleware(opts.req, opts.res, (err: unknown) => {
76
+ run(async () => {
77
+ const req = incomingMessageToRequest(opts.req, {
78
+ maxBodySize: opts.maxBodySize ?? null,
79
+ });
80
+
81
+ // Build tRPC dependencies
82
+ const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (
83
+ innerOpts,
84
+ ) => {
85
+ return await opts.createContext?.({
86
+ ...opts,
87
+ ...innerOpts,
72
88
  });
73
89
  };
74
- req.signal.addEventListener('abort', onAbort, {
75
- once: true,
76
- });
77
90
 
78
- while (true) {
79
- const { done, value } = await reader.read();
91
+ const response = await resolveResponse({
92
+ ...opts,
93
+ req,
94
+ error: err ? getTRPCErrorFromUnknown(err) : null,
95
+ createContext,
96
+ onError(o) {
97
+ opts?.onError?.({
98
+ ...o,
99
+ req: opts.req,
100
+ });
101
+ },
102
+ });
80
103
 
81
- if (done) {
82
- break;
83
- }
84
- if (!res.writable) {
85
- break;
86
- }
87
- if (res.write(value) === false) {
88
- await new Promise<void>((resolve) => {
89
- res.once('drain', resolve);
104
+ const { res } = opts;
105
+ if (res.statusCode === 200) {
106
+ // if the status code is set, we assume that it's been manually overridden
107
+ res.statusCode = response.status;
108
+ }
109
+ for (const [key, value] of response.headers) {
110
+ res.setHeader(key, value);
111
+ }
112
+ if (response.body) {
113
+ const reader = response.body.getReader();
114
+ const onAbort = () => {
115
+ // cancelling the reader will cause the whole stream to be cancelled
116
+ reader.cancel().catch(() => {
117
+ // console.error('reader.cancel() error', err);
90
118
  });
91
- }
119
+ };
120
+ req.signal.addEventListener('abort', onAbort, {
121
+ once: true,
122
+ });
123
+
124
+ while (true) {
125
+ const { done, value } = await reader.read();
92
126
 
93
- // useful for debugging chunked responses:
94
- // console.log('wrote', Buffer.from(value).toString());
127
+ if (done) {
128
+ break;
129
+ }
130
+ if (!res.writable) {
131
+ break;
132
+ }
133
+ if (res.write(value) === false) {
134
+ await new Promise<void>((resolve) => {
135
+ res.once('drain', resolve);
136
+ });
137
+ }
95
138
 
96
- // IMPORTANT - flush the response buffer, otherwise the client will not receive the data until `.end()`
97
- res.flush?.();
139
+ // useful for debugging chunked responses:
140
+ // console.log('wrote', Buffer.from(value).toString());
141
+
142
+ // IMPORTANT - flush the response buffer, otherwise the client will not receive the data until `.end()`
143
+ res.flush?.();
144
+ }
145
+ req.signal.removeEventListener('abort', onAbort);
98
146
  }
99
- req.signal.removeEventListener('abort', onAbort);
100
- }
101
- res.end();
147
+ res.end();
148
+ }).catch(internal_exceptionHandler(opts));
102
149
  });
103
150
  }
@@ -10,13 +10,13 @@
10
10
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
11
11
  import http from 'http';
12
12
  // @trpc/server
13
- import type { AnyRouter } from '../@trpc/server';
13
+ import { type AnyRouter } from '../@trpc/server';
14
14
  import { toURL } from '../@trpc/server/http';
15
15
  import type {
16
16
  NodeHTTPCreateContextFnOptions,
17
17
  NodeHTTPHandlerOptions,
18
18
  } from './node-http';
19
- import { nodeHTTPRequestHandler } from './node-http';
19
+ import { internal_exceptionHandler, nodeHTTPRequestHandler } from './node-http';
20
20
 
21
21
  export type CreateHTTPHandlerOptions<TRouter extends AnyRouter> =
22
22
  NodeHTTPHandlerOptions<TRouter, http.IncomingMessage, http.ServerResponse>;
@@ -26,28 +26,44 @@ export type CreateHTTPContextOptions = NodeHTTPCreateContextFnOptions<
26
26
  http.ServerResponse
27
27
  >;
28
28
 
29
+ /**
30
+ * @internal
31
+ */
29
32
  export function createHTTPHandler<TRouter extends AnyRouter>(
30
33
  opts: CreateHTTPHandlerOptions<TRouter>,
31
- ) {
32
- return async (req: http.IncomingMessage, res: http.ServerResponse) => {
33
- const url = toURL(req.url!);
34
+ ): http.RequestListener {
35
+ return (req, res) => {
36
+ let path = '';
37
+ try {
38
+ const url = toURL(req.url!);
34
39
 
35
- // get procedure path and remove the leading slash
36
- // /procedure -> procedure
37
- const path = url.pathname.slice(1);
40
+ // get procedure path and remove the leading slash
41
+ // /procedure -> procedure
42
+ path = url.pathname.slice(1);
38
43
 
39
- await nodeHTTPRequestHandler({
40
- ...(opts as any),
41
- req,
42
- res,
43
- path,
44
- });
44
+ nodeHTTPRequestHandler({
45
+ ...(opts as any),
46
+ req,
47
+ res,
48
+ path,
49
+ });
50
+ } catch (cause) {
51
+ internal_exceptionHandler<
52
+ TRouter,
53
+ http.IncomingMessage,
54
+ http.ServerResponse
55
+ >({
56
+ req,
57
+ res,
58
+ path,
59
+ ...opts,
60
+ })(cause);
61
+ }
45
62
  };
46
63
  }
47
64
 
48
65
  export function createHTTPServer<TRouter extends AnyRouter>(
49
66
  opts: CreateHTTPHandlerOptions<TRouter>,
50
67
  ) {
51
- const handler = createHTTPHandler(opts);
52
- return http.createServer(handler);
68
+ return http.createServer(createHTTPHandler(opts));
53
69
  }
@@ -1,7 +1,17 @@
1
+ import { TRPCError } from '../error/TRPCError';
2
+
1
3
  export function toURL(urlOrPathname: string): URL {
2
- const url = urlOrPathname.startsWith('/')
3
- ? `http://127.0.0.1${urlOrPathname}`
4
- : urlOrPathname;
4
+ try {
5
+ const url = urlOrPathname.startsWith('/')
6
+ ? `http://127.0.0.1${urlOrPathname}`
7
+ : urlOrPathname;
5
8
 
6
- return new URL(url);
9
+ return new URL(url);
10
+ } catch (cause) {
11
+ throw new TRPCError({
12
+ code: 'BAD_REQUEST',
13
+ message: 'Invalid URL',
14
+ cause,
15
+ });
16
+ }
7
17
  }