trpc-uwebsockets 0.9.4 → 0.10.0-proxy-beta.8
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.
- package/README.md +43 -63
- package/dist/index.js +18 -123
- package/dist/index.js.map +1 -1
- package/dist/requestHandler.js +86 -0
- package/dist/requestHandler.js.map +1 -0
- package/dist/utils.js +16 -17
- package/dist/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +28 -133
- package/src/requestHandler.ts +99 -0
- package/src/types.ts +30 -27
- package/src/utils.ts +26 -14
- package/test/index.spec.ts +110 -172
- package/types/index.d.ts +3 -4
- package/types/requestHandler.d.ts +3 -0
- package/types/types.d.ts +17 -19
- package/types/utils.d.ts +4 -4
package/test/index.spec.ts
CHANGED
|
@@ -1,103 +1,77 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import AbortController from 'abort-controller';
|
|
3
2
|
import fetch from 'node-fetch';
|
|
4
|
-
import {
|
|
3
|
+
import { CreateContextOptions } from '../src/types';
|
|
5
4
|
import uWs from 'uWebSockets.js';
|
|
6
5
|
import z from 'zod';
|
|
7
|
-
import * as trpc from '@trpc/server';
|
|
8
|
-
import { inferAsyncReturnType, TRPCError } from '@trpc/server';
|
|
9
6
|
import { createUWebSocketsHandler } from '../src/index';
|
|
10
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createTRPCProxyClient,
|
|
9
|
+
httpBatchLink,
|
|
10
|
+
TRPCClientError,
|
|
11
|
+
} from '@trpc/client';
|
|
12
|
+
import { inferAsyncReturnType, initTRPC, TRPCError } from '@trpc/server';
|
|
11
13
|
|
|
12
14
|
const testPort = 8799;
|
|
13
15
|
|
|
14
16
|
function makeRouter() {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
const t = initTRPC.context<Context>().create();
|
|
18
|
+
|
|
19
|
+
const router = t.router({
|
|
20
|
+
hello: t.procedure
|
|
21
|
+
.input(
|
|
22
|
+
z
|
|
23
|
+
.object({
|
|
24
|
+
who: z.string().nullish(),
|
|
25
|
+
})
|
|
26
|
+
.nullish()
|
|
27
|
+
)
|
|
28
|
+
.query(({ input, ctx }) => {
|
|
24
29
|
return {
|
|
25
30
|
text: `hello ${input?.who ?? ctx.user?.name ?? 'world'}`,
|
|
26
31
|
};
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
.query('error', {
|
|
30
|
-
async resolve() {
|
|
31
|
-
throw new TRPCError({
|
|
32
|
-
code: 'BAD_REQUEST',
|
|
33
|
-
message: 'error as expected',
|
|
34
|
-
});
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
.mutation('long-payload', {
|
|
38
|
-
input: z.object({
|
|
39
|
-
ping: z.any(),
|
|
40
32
|
}),
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
input
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
error: t.procedure.query(() => {
|
|
34
|
+
throw new TRPCError({
|
|
35
|
+
code: 'BAD_REQUEST',
|
|
36
|
+
message: 'error as expected',
|
|
37
|
+
});
|
|
38
|
+
}),
|
|
39
|
+
test: t.procedure
|
|
40
|
+
.input(
|
|
41
|
+
z.object({
|
|
42
|
+
value: z.string(),
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
.mutation(({ input, ctx }) => {
|
|
52
46
|
return {
|
|
53
47
|
originalValue: input.value,
|
|
54
48
|
user: ctx.user,
|
|
55
49
|
};
|
|
56
|
-
},
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ctx.res.setCookie('one', 'nom');
|
|
64
|
-
ctx.res.setCookie('two', 'nom nom');
|
|
65
|
-
ctx.res.setHeader('x-spooked', 'true');
|
|
66
|
-
ctx.res.setStatus(201);
|
|
67
|
-
return {
|
|
68
|
-
combined,
|
|
69
|
-
user: ctx.user,
|
|
70
|
-
};
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
50
|
+
}),
|
|
51
|
+
manualRes: t.procedure.query(({ ctx }) => {
|
|
52
|
+
ctx.res.setStatus(400);
|
|
53
|
+
ctx.res.setHeader('manual', 'header');
|
|
54
|
+
return 'status 400';
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
74
57
|
return router;
|
|
75
58
|
}
|
|
76
59
|
export type Router = ReturnType<typeof makeRouter>;
|
|
77
60
|
|
|
78
61
|
function makeContext() {
|
|
79
|
-
const createContext = ({
|
|
80
|
-
req,
|
|
81
|
-
res,
|
|
82
|
-
uWs,
|
|
83
|
-
}: UWebSocketsCreateContextOptions) => {
|
|
62
|
+
const createContext = ({ req, res }: CreateContextOptions) => {
|
|
84
63
|
const getUser = () => {
|
|
85
64
|
if (req.headers.authorization === 'meow') {
|
|
86
65
|
return {
|
|
87
66
|
name: 'KATT',
|
|
88
67
|
};
|
|
89
68
|
}
|
|
90
|
-
if (req.getCookies()?.user === 'romanzy')
|
|
91
|
-
return {
|
|
92
|
-
name: 'romanzy',
|
|
93
|
-
};
|
|
94
69
|
return null;
|
|
95
70
|
};
|
|
96
|
-
|
|
97
71
|
return {
|
|
98
72
|
req,
|
|
99
73
|
res,
|
|
100
|
-
uWs,
|
|
74
|
+
// uWs,
|
|
101
75
|
user: getUser(),
|
|
102
76
|
};
|
|
103
77
|
};
|
|
@@ -105,46 +79,25 @@ function makeContext() {
|
|
|
105
79
|
return createContext;
|
|
106
80
|
}
|
|
107
81
|
export type Context = inferAsyncReturnType<ReturnType<typeof makeContext>>;
|
|
82
|
+
|
|
83
|
+
// export type Context = inferAsyncReturnType<ReturnType<typeof makeContext>>;
|
|
108
84
|
async function startServer() {
|
|
109
85
|
const app = uWs.App();
|
|
110
86
|
|
|
111
|
-
// Handle CORS
|
|
112
|
-
app.options('/trpc/*', (res) => {
|
|
113
|
-
res.writeHeader('Access-Control-Allow-Origin', '*');
|
|
114
|
-
res.writeStatus('200 OK');
|
|
115
|
-
res.end();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
app.get('/', (res) => {
|
|
119
|
-
res.writeStatus('200 OK');
|
|
120
|
-
|
|
121
|
-
res.end();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// need to register everything on the app object,
|
|
125
|
-
// as uWebSockets does not have middleware
|
|
126
87
|
createUWebSocketsHandler(app, '/trpc', {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
88
|
+
responseMeta({ ctx, paths, type, errors }) {
|
|
89
|
+
return {
|
|
90
|
+
headers: {
|
|
91
|
+
hello: 'world',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
132
95
|
router: makeRouter(),
|
|
133
96
|
createContext: makeContext(),
|
|
134
97
|
});
|
|
135
98
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
res.end();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
app.any('/*', (res) => {
|
|
142
|
-
res.writeStatus('404 NOT FOUND');
|
|
143
|
-
res.end();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
const { socket } = await new Promise<{
|
|
147
|
-
socket: uWs.us_listen_socket;
|
|
99
|
+
let { socket } = await new Promise<{
|
|
100
|
+
socket: uWs.us_listen_socket | any;
|
|
148
101
|
}>((resolve) => {
|
|
149
102
|
app.listen('0.0.0.0', testPort, (socket) => {
|
|
150
103
|
resolve({
|
|
@@ -158,6 +111,7 @@ async function startServer() {
|
|
|
158
111
|
new Promise<void>((resolve, reject) => {
|
|
159
112
|
try {
|
|
160
113
|
uWs.us_listen_socket_close(socket);
|
|
114
|
+
socket = null;
|
|
161
115
|
resolve();
|
|
162
116
|
} catch (error) {
|
|
163
117
|
reject();
|
|
@@ -167,16 +121,28 @@ async function startServer() {
|
|
|
167
121
|
}
|
|
168
122
|
|
|
169
123
|
function makeClient(headers) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
124
|
+
const client = createTRPCProxyClient<Router>({
|
|
125
|
+
links: [
|
|
126
|
+
httpBatchLink({
|
|
127
|
+
url: `http://localhost:${testPort}/trpc`,
|
|
128
|
+
fetch: fetch as any,
|
|
129
|
+
headers,
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
176
132
|
});
|
|
133
|
+
return client;
|
|
134
|
+
// client.
|
|
135
|
+
|
|
136
|
+
// return createTRPCClient<Router>({
|
|
137
|
+
// url: `http://localhost:${testPort}/trpc`,
|
|
138
|
+
|
|
139
|
+
// AbortController: AbortController as any,
|
|
140
|
+
// fetch: fetch as any,
|
|
141
|
+
// headers,
|
|
142
|
+
// });
|
|
177
143
|
}
|
|
178
144
|
|
|
179
|
-
let t!:
|
|
145
|
+
let t!: Awaited<ReturnType<typeof startServer>>;
|
|
180
146
|
beforeEach(async () => {
|
|
181
147
|
t = await startServer();
|
|
182
148
|
});
|
|
@@ -184,12 +150,13 @@ afterEach(async () => {
|
|
|
184
150
|
await t.close();
|
|
185
151
|
});
|
|
186
152
|
|
|
187
|
-
test('simple
|
|
153
|
+
test('query simple success and error handling', async () => {
|
|
188
154
|
// t.client.runtime.headers = ()
|
|
189
155
|
const client = makeClient({});
|
|
190
156
|
|
|
157
|
+
// client.
|
|
191
158
|
expect(
|
|
192
|
-
await client.query(
|
|
159
|
+
await client.hello.query({
|
|
193
160
|
who: 'test',
|
|
194
161
|
})
|
|
195
162
|
).toMatchInlineSnapshot(`
|
|
@@ -198,16 +165,16 @@ test('simple query', async () => {
|
|
|
198
165
|
}
|
|
199
166
|
`);
|
|
200
167
|
|
|
201
|
-
expect(client.query(
|
|
168
|
+
await expect(client.error.query()).rejects.toThrowError('error as expected');
|
|
202
169
|
});
|
|
203
170
|
|
|
204
|
-
test('mutation
|
|
171
|
+
test('mutation and reading headers', async () => {
|
|
205
172
|
const client = makeClient({
|
|
206
173
|
authorization: 'meow',
|
|
207
174
|
});
|
|
208
175
|
|
|
209
176
|
expect(
|
|
210
|
-
await client.
|
|
177
|
+
await client.test.mutate({
|
|
211
178
|
value: 'lala',
|
|
212
179
|
})
|
|
213
180
|
).toMatchInlineSnapshot(`
|
|
@@ -220,67 +187,38 @@ test('mutation with header', async () => {
|
|
|
220
187
|
`);
|
|
221
188
|
});
|
|
222
189
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
cookie: 'cookie1=abc; cookie2=d.e; user=romanzy',
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
expect(await client.query('cookie-monster')).toMatchInlineSnapshot(`
|
|
230
|
-
Object {
|
|
231
|
-
"combined": "abcd.e",
|
|
232
|
-
"user": Object {
|
|
233
|
-
"name": "romanzy",
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
`);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
test('setting cookies and headers', async () => {
|
|
240
|
-
const monsterRes = await fetch(
|
|
241
|
-
`http://localhost:${testPort}/trpc/cookie-monster`
|
|
242
|
-
);
|
|
243
|
-
expect(monsterRes.status).toEqual(201);
|
|
244
|
-
expect(monsterRes.headers.get('set-cookie')).toEqual(
|
|
245
|
-
'one=nom, two=nom%20nom'
|
|
190
|
+
test('manually sets status and headers', async () => {
|
|
191
|
+
const fetcher = await fetch(
|
|
192
|
+
`http://localhost:${testPort}/trpc/manualRes?input=${encodeURI('{}')}`
|
|
246
193
|
);
|
|
247
|
-
|
|
194
|
+
const body = await fetcher.json();
|
|
195
|
+
expect(fetcher.status).toEqual(400);
|
|
196
|
+
expect(body.result.data).toEqual('status 400');
|
|
197
|
+
expect(fetcher.headers.get('hello')).toEqual('world'); // from the meta
|
|
198
|
+
expect(fetcher.headers.get('manual')).toEqual('header'); //from the result
|
|
248
199
|
});
|
|
249
200
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
`http://localhost:${testPort}/trpc/hello?input=${badInput}`
|
|
275
|
-
);
|
|
276
|
-
expect(badRes.status).toEqual(400);
|
|
277
|
-
|
|
278
|
-
const badPath = await fetch(
|
|
279
|
-
`http://localhost:${testPort}/trpc/nonexisting?input=${badInput}`
|
|
280
|
-
);
|
|
281
|
-
expect(badPath.status).toEqual(400);
|
|
282
|
-
|
|
283
|
-
const uncaught = await fetch(`http://localhost:${testPort}/badurl`);
|
|
284
|
-
|
|
285
|
-
expect(uncaught.status).toEqual(404);
|
|
286
|
-
});
|
|
201
|
+
// this needs to be tested
|
|
202
|
+
// test('abort works okay', async () => {
|
|
203
|
+
// const ac = new AbortController();
|
|
204
|
+
// const client = makeClient({});
|
|
205
|
+
|
|
206
|
+
// setTimeout(() => {
|
|
207
|
+
// ac.abort();
|
|
208
|
+
// }, 3);
|
|
209
|
+
// const res = await client.test.mutate(
|
|
210
|
+
// {
|
|
211
|
+
// value: 'haha',
|
|
212
|
+
// },
|
|
213
|
+
// {
|
|
214
|
+
// signal: ac.signal as any,
|
|
215
|
+
// }
|
|
216
|
+
// );
|
|
217
|
+
|
|
218
|
+
// expect(res).toMatchInlineSnapshot(`
|
|
219
|
+
// Object {
|
|
220
|
+
// "originalValue": "haha",
|
|
221
|
+
// "user": null,
|
|
222
|
+
// }
|
|
223
|
+
// `);
|
|
224
|
+
// });
|
package/types/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { AnyRouter } from '@trpc/server';
|
|
2
2
|
import type { TemplatedApp } from 'uWebSockets.js';
|
|
3
|
-
import {
|
|
4
|
-
export * from './types';
|
|
3
|
+
import { uHTTPHandlerOptions } from './types';
|
|
5
4
|
/**
|
|
6
5
|
* @param uWsApp uWebsockets server instance
|
|
7
|
-
* @param
|
|
6
|
+
* @param prefix The path to trpc without trailing slash (ex: "/trpc")
|
|
8
7
|
* @param opts handler options
|
|
9
8
|
*/
|
|
10
|
-
export declare function createUWebSocketsHandler<TRouter extends AnyRouter>(uWsApp: TemplatedApp,
|
|
9
|
+
export declare function createUWebSocketsHandler<TRouter extends AnyRouter>(uWsApp: TemplatedApp, prefix: string, opts: uHTTPHandlerOptions<TRouter>): void;
|
package/types/types.d.ts
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
createContext?: (opts: UWebSocketsCreateContextOptions) => Promise<inferRouterContext<TRouter>> | inferRouterContext<TRouter>;
|
|
7
|
-
onRequest?: (req: UWebSocketsRequestObject, res: UWebSocketsResponseObject) => void;
|
|
8
|
-
};
|
|
9
|
-
export declare type UWebSocketsRequestObject = {
|
|
1
|
+
import { HttpResponse } from 'uWebSockets.js';
|
|
2
|
+
import { AnyRouter } from '@trpc/server';
|
|
3
|
+
import { NodeHTTPCreateContextFnOptions, NodeHTTPCreateContextOption } from '@trpc/server/adapters/node-http';
|
|
4
|
+
import { HTTPBaseHandlerOptions } from '@trpc/server/dist/http/internals/types';
|
|
5
|
+
export declare type WrappedHTTPRequest = {
|
|
10
6
|
headers: Record<string, string>;
|
|
11
7
|
method: 'POST' | 'GET';
|
|
12
|
-
query:
|
|
13
|
-
|
|
14
|
-
getCookies: (opts?: CookieParseOptions) => Record<string, string>;
|
|
8
|
+
query: string;
|
|
9
|
+
url: string;
|
|
15
10
|
};
|
|
16
|
-
export declare type
|
|
17
|
-
setCookie(key: string, value: string, opts?: CookieSerializeOptions): void;
|
|
11
|
+
export declare type WrappedHTTPResponse = {
|
|
18
12
|
setStatus(status: number): void;
|
|
19
13
|
setHeader(key: string, value: string): void;
|
|
20
14
|
};
|
|
21
|
-
export declare type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
export declare type uHTTPHandlerOptions<TRouter extends AnyRouter> = HTTPBaseHandlerOptions<TRouter, WrappedHTTPRequest> & {
|
|
16
|
+
maxBodySize?: number;
|
|
17
|
+
} & NodeHTTPCreateContextOption<TRouter, WrappedHTTPRequest, WrappedHTTPResponse>;
|
|
18
|
+
export declare type uHTTPRequestHandlerOptions<TRouter extends AnyRouter> = {
|
|
19
|
+
req: WrappedHTTPRequest;
|
|
20
|
+
uRes: HttpResponse;
|
|
21
|
+
path: string;
|
|
22
|
+
} & uHTTPHandlerOptions<TRouter>;
|
|
23
|
+
export declare type CreateContextOptions = NodeHTTPCreateContextFnOptions<WrappedHTTPRequest, WrappedHTTPResponse>;
|
package/types/utils.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { TRPCError } from '@trpc/server';
|
|
2
1
|
import { HttpResponse } from 'uWebSockets.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export declare function
|
|
2
|
+
import { WrappedHTTPRequest, WrappedHTTPResponse } from './types';
|
|
3
|
+
import { AnyRouter, TRPCError } from '@trpc/server';
|
|
4
|
+
export declare function getPostBody<TRouter extends AnyRouter, TRequest extends WrappedHTTPRequest, TResponse extends WrappedHTTPResponse>(method: any, res: HttpResponse, maxBodySize?: number): Promise<{
|
|
6
5
|
ok: true;
|
|
7
6
|
data: unknown;
|
|
8
7
|
} | {
|
|
9
8
|
ok: false;
|
|
10
9
|
error: TRPCError;
|
|
11
10
|
}>;
|
|
11
|
+
export declare function sendResponse(res: HttpResponse, payload?: string): void;
|