trpc-uwebsockets 0.9.3 → 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 +32 -38
- package/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +28 -133
- package/src/requestHandler.ts +99 -0
- package/src/types.ts +30 -27
- package/src/utils.ts +42 -31
- package/test/index.spec.ts +109 -148
- 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,93 +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
|
-
resolve() {
|
|
31
|
-
throw new TRPCError({
|
|
32
|
-
code: 'BAD_REQUEST',
|
|
33
|
-
message: 'error as expected',
|
|
34
|
-
});
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
.mutation('test', {
|
|
38
|
-
input: z.object({
|
|
39
|
-
value: z.string(),
|
|
40
32
|
}),
|
|
41
|
-
|
|
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 }) => {
|
|
42
46
|
return {
|
|
43
47
|
originalValue: input.value,
|
|
44
48
|
user: ctx.user,
|
|
45
49
|
};
|
|
46
|
-
},
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
ctx.res.setCookie('one', 'nom');
|
|
54
|
-
ctx.res.setCookie('two', 'nom nom');
|
|
55
|
-
ctx.res.setHeader('x-spooked', 'true');
|
|
56
|
-
ctx.res.setStatus(201);
|
|
57
|
-
return {
|
|
58
|
-
combined,
|
|
59
|
-
user: ctx.user,
|
|
60
|
-
};
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
50
|
+
}),
|
|
51
|
+
manualRes: t.procedure.query(({ ctx }) => {
|
|
52
|
+
ctx.res.setStatus(400);
|
|
53
|
+
ctx.res.setHeader('manual', 'header');
|
|
54
|
+
return 'status 400';
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
64
57
|
return router;
|
|
65
58
|
}
|
|
66
59
|
export type Router = ReturnType<typeof makeRouter>;
|
|
67
60
|
|
|
68
61
|
function makeContext() {
|
|
69
|
-
const createContext = ({
|
|
70
|
-
req,
|
|
71
|
-
res,
|
|
72
|
-
uWs,
|
|
73
|
-
}: UWebSocketsCreateContextOptions) => {
|
|
62
|
+
const createContext = ({ req, res }: CreateContextOptions) => {
|
|
74
63
|
const getUser = () => {
|
|
75
64
|
if (req.headers.authorization === 'meow') {
|
|
76
65
|
return {
|
|
77
66
|
name: 'KATT',
|
|
78
67
|
};
|
|
79
68
|
}
|
|
80
|
-
if (req.getCookies()?.user === 'romanzy')
|
|
81
|
-
return {
|
|
82
|
-
name: 'romanzy',
|
|
83
|
-
};
|
|
84
69
|
return null;
|
|
85
70
|
};
|
|
86
|
-
|
|
87
71
|
return {
|
|
88
72
|
req,
|
|
89
73
|
res,
|
|
90
|
-
uWs,
|
|
74
|
+
// uWs,
|
|
91
75
|
user: getUser(),
|
|
92
76
|
};
|
|
93
77
|
};
|
|
@@ -95,46 +79,25 @@ function makeContext() {
|
|
|
95
79
|
return createContext;
|
|
96
80
|
}
|
|
97
81
|
export type Context = inferAsyncReturnType<ReturnType<typeof makeContext>>;
|
|
82
|
+
|
|
83
|
+
// export type Context = inferAsyncReturnType<ReturnType<typeof makeContext>>;
|
|
98
84
|
async function startServer() {
|
|
99
85
|
const app = uWs.App();
|
|
100
86
|
|
|
101
|
-
// Handle CORS
|
|
102
|
-
app.options('/trpc/*', (res) => {
|
|
103
|
-
res.writeHeader('Access-Control-Allow-Origin', '*');
|
|
104
|
-
res.writeStatus('200 OK');
|
|
105
|
-
res.end();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
app.get('/', (res) => {
|
|
109
|
-
res.writeStatus('200 OK');
|
|
110
|
-
|
|
111
|
-
res.end();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// need to register everything on the app object,
|
|
115
|
-
// as uWebSockets does not have middleware
|
|
116
87
|
createUWebSocketsHandler(app, '/trpc', {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
88
|
+
responseMeta({ ctx, paths, type, errors }) {
|
|
89
|
+
return {
|
|
90
|
+
headers: {
|
|
91
|
+
hello: 'world',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
121
94
|
},
|
|
122
95
|
router: makeRouter(),
|
|
123
96
|
createContext: makeContext(),
|
|
124
97
|
});
|
|
125
98
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
res.end();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
app.any('/*', (res) => {
|
|
132
|
-
res.writeStatus('404 NOT FOUND');
|
|
133
|
-
res.end();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const { socket } = await new Promise<{
|
|
137
|
-
socket: uWs.us_listen_socket;
|
|
99
|
+
let { socket } = await new Promise<{
|
|
100
|
+
socket: uWs.us_listen_socket | any;
|
|
138
101
|
}>((resolve) => {
|
|
139
102
|
app.listen('0.0.0.0', testPort, (socket) => {
|
|
140
103
|
resolve({
|
|
@@ -148,6 +111,7 @@ async function startServer() {
|
|
|
148
111
|
new Promise<void>((resolve, reject) => {
|
|
149
112
|
try {
|
|
150
113
|
uWs.us_listen_socket_close(socket);
|
|
114
|
+
socket = null;
|
|
151
115
|
resolve();
|
|
152
116
|
} catch (error) {
|
|
153
117
|
reject();
|
|
@@ -157,16 +121,28 @@ async function startServer() {
|
|
|
157
121
|
}
|
|
158
122
|
|
|
159
123
|
function makeClient(headers) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
124
|
+
const client = createTRPCProxyClient<Router>({
|
|
125
|
+
links: [
|
|
126
|
+
httpBatchLink({
|
|
127
|
+
url: `http://localhost:${testPort}/trpc`,
|
|
128
|
+
fetch: fetch as any,
|
|
129
|
+
headers,
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
166
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
|
+
// });
|
|
167
143
|
}
|
|
168
144
|
|
|
169
|
-
let t!:
|
|
145
|
+
let t!: Awaited<ReturnType<typeof startServer>>;
|
|
170
146
|
beforeEach(async () => {
|
|
171
147
|
t = await startServer();
|
|
172
148
|
});
|
|
@@ -174,12 +150,13 @@ afterEach(async () => {
|
|
|
174
150
|
await t.close();
|
|
175
151
|
});
|
|
176
152
|
|
|
177
|
-
test('simple
|
|
153
|
+
test('query simple success and error handling', async () => {
|
|
178
154
|
// t.client.runtime.headers = ()
|
|
179
155
|
const client = makeClient({});
|
|
180
156
|
|
|
157
|
+
// client.
|
|
181
158
|
expect(
|
|
182
|
-
await client.query(
|
|
159
|
+
await client.hello.query({
|
|
183
160
|
who: 'test',
|
|
184
161
|
})
|
|
185
162
|
).toMatchInlineSnapshot(`
|
|
@@ -188,16 +165,16 @@ test('simple query', async () => {
|
|
|
188
165
|
}
|
|
189
166
|
`);
|
|
190
167
|
|
|
191
|
-
expect(client.query(
|
|
168
|
+
await expect(client.error.query()).rejects.toThrowError('error as expected');
|
|
192
169
|
});
|
|
193
170
|
|
|
194
|
-
test('mutation
|
|
171
|
+
test('mutation and reading headers', async () => {
|
|
195
172
|
const client = makeClient({
|
|
196
173
|
authorization: 'meow',
|
|
197
174
|
});
|
|
198
175
|
|
|
199
176
|
expect(
|
|
200
|
-
await client.
|
|
177
|
+
await client.test.mutate({
|
|
201
178
|
value: 'lala',
|
|
202
179
|
})
|
|
203
180
|
).toMatchInlineSnapshot(`
|
|
@@ -210,54 +187,38 @@ test('mutation with header', async () => {
|
|
|
210
187
|
`);
|
|
211
188
|
});
|
|
212
189
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
cookie: 'cookie1=abc; cookie2=d.e; user=romanzy',
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
expect(await client.query('cookie-monster')).toMatchInlineSnapshot(`
|
|
220
|
-
Object {
|
|
221
|
-
"combined": "abcd.e",
|
|
222
|
-
"user": Object {
|
|
223
|
-
"name": "romanzy",
|
|
224
|
-
},
|
|
225
|
-
}
|
|
226
|
-
`);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
test('setting cookies and headers', async () => {
|
|
230
|
-
const monsterRes = await fetch(
|
|
231
|
-
`http://localhost:${testPort}/trpc/cookie-monster`
|
|
232
|
-
);
|
|
233
|
-
expect(monsterRes.status).toEqual(201);
|
|
234
|
-
expect(monsterRes.headers.get('set-cookie')).toEqual(
|
|
235
|
-
'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('{}')}`
|
|
236
193
|
);
|
|
237
|
-
|
|
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
|
|
238
199
|
});
|
|
239
200
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
});
|
|
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;
|