trpc-uwebsockets 0.10.0-proxy-beta.15 → 10.38.3-beta.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.
package/README.md CHANGED
@@ -4,18 +4,20 @@
4
4
 
5
5
  # Installation
6
6
 
7
- To use old, stable version 9
7
+ Latest stable version
8
8
 
9
9
  ```bash
10
- yarn add trpc-uwebsockets@^0.9.*
10
+ yarn add trpc-uwebsockets@latest
11
11
  ```
12
12
 
13
- To use version 10 beta run
13
+ Or install beta version with subscription support
14
14
 
15
15
  ```bash
16
- yarn add trpc-uwebsockets
16
+ yarn add trpc-uwebsockets@beta
17
17
  ```
18
18
 
19
+ ##
20
+
19
21
  # Usage
20
22
 
21
23
  Import needed packages
@@ -100,27 +102,61 @@ type CreateContextOptions = {
100
102
  query: string;
101
103
  path: string;
102
104
  };
103
- /* minimal response interface, useful for setting cookies */
105
+ /* see https://unetworking.github.io/uWebSockets.js/generated/interfaces/HttpResponse.html */
104
106
  res: {
105
- setStatus(status: number): void;
106
- setHeader(name: string, value: string): void;
107
+ writeStatus(status: RecognizedString): HttpResponse;
108
+ writeHeader(key: RecognizedString, value: RecognizedString): HttpResponse;
107
109
  };
108
110
  };
109
111
  ```
110
112
 
111
- # Testing
113
+ # Enabling subscrptions
112
114
 
113
- ```bash
114
- yarn t
115
+ Simple method: enable subscriptions when creating the main handler.
116
+
117
+ ```typescript
118
+ createUWebSocketsHandler(app, '/trpc', {
119
+ router,
120
+ createContext,
121
+ enableSubscriptions: true,
122
+ });
115
123
  ```
116
124
 
117
- or
125
+ Recommended method: enable subscriptions after registering main request handler. This allows for contexts to be evaluated differently
118
126
 
119
- ```bash
120
- yarn test:watch
127
+ ```typescript
128
+ const app = App();
129
+
130
+ createUWebSocketsHandler(app, '/trpc', {
131
+ router,
132
+ createContext: ({ req, res }) => {},
133
+ });
134
+
135
+ applyWSHandler(app, '/trpc', {
136
+ router,
137
+ createContext: ({ req, res }) => {},
138
+ });
121
139
  ```
122
140
 
123
- # Todo
141
+ ## example of subscrption client
124
142
 
125
- - [ ] Various improvements (res.tryEnd + reading multiple headers /w same key)
126
- - [ ] Subscription support with websockets
143
+ ```typescript
144
+ const host = `localhost:8080/trpc`;
145
+ const wsClient = createWSClient({ url: `ws://${host}` });
146
+ const client = createTRPCProxyClient<AppRouter>({
147
+ links: [
148
+ splitLink({
149
+ condition(op) {
150
+ return op.type === 'subscription';
151
+ },
152
+ true: wsLink({ client: wsClient }),
153
+ false: httpBatchLink({
154
+ url: `http://${host}`,
155
+ headers: headers,
156
+ AbortController,
157
+ fetch: fetch as any,
158
+ }),
159
+ }),
160
+ ],
161
+ });
162
+ ```
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyWSHandler = void 0;
4
+ const server_1 = require("@trpc/server");
5
+ const observable_1 = require("@trpc/server/observable");
6
+ // import { transformTRPCResponse } from '../shared/transformTRPCResponse';
7
+ const shared_1 = require("@trpc/server/shared");
8
+ const utils_1 = require("./utils");
9
+ /* istanbul ignore next -- @preserve */
10
+ function assertIsObject(obj) {
11
+ if (typeof obj !== 'object' || Array.isArray(obj) || !obj) {
12
+ throw new Error('Not an object');
13
+ }
14
+ }
15
+ /* istanbul ignore next -- @preserve */
16
+ function assertIsProcedureType(obj) {
17
+ if (obj !== 'query' && obj !== 'subscription' && obj !== 'mutation') {
18
+ throw new Error('Invalid procedure type');
19
+ }
20
+ }
21
+ /* istanbul ignore next -- @preserve */
22
+ function assertIsRequestId(obj) {
23
+ if (obj !== null &&
24
+ typeof obj === 'number' &&
25
+ isNaN(obj) &&
26
+ typeof obj !== 'string') {
27
+ throw new Error('Invalid request id');
28
+ }
29
+ }
30
+ /* istanbul ignore next -- @preserve */
31
+ function assertIsString(obj) {
32
+ if (typeof obj !== 'string') {
33
+ throw new Error('Invalid string');
34
+ }
35
+ }
36
+ /* istanbul ignore next -- @preserve */
37
+ function assertIsJSONRPC2OrUndefined(obj) {
38
+ if (typeof obj !== 'undefined' && obj !== '2.0') {
39
+ throw new Error('Must be JSONRPC 2.0');
40
+ }
41
+ }
42
+ function parseMessage(obj, transformer) {
43
+ assertIsObject(obj);
44
+ const { method, params, id, jsonrpc } = obj;
45
+ assertIsRequestId(id);
46
+ assertIsJSONRPC2OrUndefined(jsonrpc);
47
+ if (method === 'subscription.stop') {
48
+ return {
49
+ id,
50
+ jsonrpc,
51
+ method,
52
+ };
53
+ }
54
+ assertIsProcedureType(method);
55
+ assertIsObject(params);
56
+ const { input: rawInput, path } = params;
57
+ assertIsString(path);
58
+ const input = transformer.input.deserialize(rawInput);
59
+ return {
60
+ id,
61
+ jsonrpc,
62
+ method,
63
+ params: {
64
+ input,
65
+ path,
66
+ },
67
+ };
68
+ }
69
+ function applyWSHandler(app, prefix, opts) {
70
+ const { createContext, router } = opts;
71
+ const { transformer } = router._def._config;
72
+ // global map of maps
73
+ // const globalSubs = new Map<WebSocket, Map<number | string, Unsubscribable>>();
74
+ // const globals = new Map<WebSocket, Decoration>();
75
+ function respond(ws, untransformedJSON) {
76
+ ws.send(JSON.stringify((0, shared_1.transformTRPCResponse)(router._def._config, untransformedJSON)));
77
+ }
78
+ function stopSubscription(ws, subscription, { id, jsonrpc }) {
79
+ subscription.unsubscribe();
80
+ respond(ws, {
81
+ id,
82
+ jsonrpc,
83
+ result: {
84
+ type: 'stopped',
85
+ },
86
+ });
87
+ }
88
+ async function handleRequest(ws, msg) {
89
+ const clientSubscriptions = ws.clientSubscriptions;
90
+ const { id, jsonrpc } = msg;
91
+ /* istanbul ignore next -- @preserve */
92
+ if (id === null) {
93
+ throw new server_1.TRPCError({
94
+ code: 'BAD_REQUEST',
95
+ message: '`id` is required',
96
+ });
97
+ }
98
+ if (msg.method === 'subscription.stop') {
99
+ const sub = clientSubscriptions.get(id);
100
+ if (sub) {
101
+ stopSubscription(ws, sub, { id, jsonrpc });
102
+ }
103
+ clientSubscriptions.delete(id);
104
+ return;
105
+ }
106
+ const { path, input } = msg.params;
107
+ const type = msg.method;
108
+ try {
109
+ await ws.ctxPromise; // asserts context has been set
110
+ const result = await (0, server_1.callProcedure)({
111
+ procedures: router._def.procedures,
112
+ path,
113
+ rawInput: input,
114
+ ctx: ws.ctx,
115
+ type,
116
+ });
117
+ if (type === 'subscription') {
118
+ if (!(0, observable_1.isObservable)(result)) {
119
+ throw new server_1.TRPCError({
120
+ message: `Subscription ${path} did not return an observable`,
121
+ code: 'INTERNAL_SERVER_ERROR',
122
+ });
123
+ }
124
+ }
125
+ else {
126
+ // send the value as data if the method is not a subscription
127
+ respond(ws, {
128
+ id,
129
+ jsonrpc,
130
+ result: {
131
+ type: 'data',
132
+ data: result,
133
+ },
134
+ });
135
+ return;
136
+ }
137
+ const observable = result;
138
+ const sub = observable.subscribe({
139
+ next(data) {
140
+ respond(ws, {
141
+ id,
142
+ jsonrpc,
143
+ result: {
144
+ type: 'data',
145
+ data,
146
+ },
147
+ });
148
+ },
149
+ error(err) {
150
+ const error = (0, server_1.getTRPCErrorFromUnknown)(err);
151
+ opts.onError?.({
152
+ error,
153
+ path,
154
+ type,
155
+ ctx: ws.ctx,
156
+ req: ws.req,
157
+ input,
158
+ });
159
+ respond(ws, {
160
+ id,
161
+ jsonrpc,
162
+ error: (0, shared_1.getErrorShape)({
163
+ config: router._def._config,
164
+ error,
165
+ type,
166
+ path,
167
+ input,
168
+ ctx: ws.ctx,
169
+ }),
170
+ });
171
+ },
172
+ complete() {
173
+ respond(ws, {
174
+ id,
175
+ jsonrpc,
176
+ result: {
177
+ type: 'stopped',
178
+ },
179
+ });
180
+ },
181
+ });
182
+ /* istanbul ignore next -- @preserve */
183
+ // FIXME handle these edge cases
184
+ // if (client.readyState !== client.OPEN) {
185
+ // // if the client got disconnected whilst initializing the subscription
186
+ // // no need to send stopped message if the client is disconnected
187
+ // sub.unsubscribe();
188
+ // return;
189
+ // }
190
+ /* istanbul ignore next -- @preserve */
191
+ if (clientSubscriptions.has(id)) {
192
+ // duplicate request ids for client
193
+ stopSubscription(ws, sub, { id, jsonrpc });
194
+ throw new server_1.TRPCError({
195
+ message: `Duplicate id ${id}`,
196
+ code: 'BAD_REQUEST',
197
+ });
198
+ }
199
+ clientSubscriptions.set(id, sub);
200
+ respond(ws, {
201
+ id,
202
+ jsonrpc,
203
+ result: {
204
+ type: 'started',
205
+ },
206
+ });
207
+ }
208
+ catch (cause) /* istanbul ignore next -- @preserve */ {
209
+ // procedure threw an error
210
+ const error = (0, server_1.getTRPCErrorFromUnknown)(cause);
211
+ opts.onError?.({ error, path, type, ctx: ws.ctx, req: ws.req, input });
212
+ respond(ws, {
213
+ id,
214
+ jsonrpc,
215
+ error: (0, shared_1.getErrorShape)({
216
+ config: router._def._config,
217
+ error,
218
+ type,
219
+ path,
220
+ input,
221
+ ctx: ws.ctx,
222
+ }),
223
+ });
224
+ }
225
+ }
226
+ // this is probably bad, but its for reconnection notification
227
+ const allClients = new Set();
228
+ app.ws(prefix, {
229
+ // sendPingsAutomatically: true, // could this be enabled?
230
+ upgrade: (res, ogReq, context) => {
231
+ // console.log(
232
+ // 'An Http connection wants to become WebSocket, URL: ' +
233
+ // ogReq.getUrl() +
234
+ // '!'
235
+ // );
236
+ const wrappedReq = (0, utils_1.extractAndWrapHttpRequest)(prefix, ogReq);
237
+ const secWebSocketKey = wrappedReq.headers['sec-websocket-key'];
238
+ const secWebSocketProtocol = wrappedReq.headers['sec-websocket-protocol'];
239
+ const secWebSocketExtensions = wrappedReq.headers['sec-websocket-extensions'];
240
+ const d = {
241
+ clientSubscriptions: new Map(),
242
+ ctx: undefined,
243
+ req: wrappedReq,
244
+ ctxPromise: createContext?.({ req: wrappedReq, res }), // this cannot use RES!
245
+ };
246
+ res.upgrade(d,
247
+ /* Spell these correctly */
248
+ secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, context);
249
+ },
250
+ // @ts-expect-error Adds decoration on ws type
251
+ async open(ws) {
252
+ async function createContextAsync() {
253
+ try {
254
+ ws.ctx = await ws.ctxPromise;
255
+ }
256
+ catch (cause) {
257
+ const error = (0, server_1.getTRPCErrorFromUnknown)(cause);
258
+ opts.onError?.({
259
+ error,
260
+ path: undefined,
261
+ type: 'unknown',
262
+ ctx: ws.ctx,
263
+ req: ws.req,
264
+ input: undefined,
265
+ });
266
+ respond(ws, {
267
+ id: null,
268
+ error: (0, shared_1.getErrorShape)({
269
+ config: router._def._config,
270
+ error,
271
+ type: 'unknown',
272
+ path: undefined,
273
+ input: undefined,
274
+ ctx: ws.ctx,
275
+ }),
276
+ });
277
+ // close in next tick
278
+ // FIXME check if this is okay?
279
+ (global.setImmediate ?? global.setTimeout)(() => {
280
+ ws.close();
281
+ });
282
+ }
283
+ }
284
+ await createContextAsync();
285
+ allClients.add(ws);
286
+ },
287
+ // @ts-expect-error Adds decoration on ws type
288
+ async message(ws, rawMsg) {
289
+ try {
290
+ const stringMsg = Buffer.from(rawMsg).toString();
291
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
292
+ const msgJSON = JSON.parse(stringMsg);
293
+ // TODO pre-optimization? why does it always sends empty arrays?
294
+ // if (Array.isArray(msgJSON) && msgJSON.length == 0)
295
+ // return;
296
+ const msgs = Array.isArray(msgJSON) ? msgJSON : [msgJSON];
297
+ const promises = msgs
298
+ .map((raw) => parseMessage(raw, transformer))
299
+ .map((value) => handleRequest(ws, value));
300
+ await Promise.all(promises);
301
+ }
302
+ catch (cause) {
303
+ const error = new server_1.TRPCError({
304
+ code: 'PARSE_ERROR',
305
+ cause,
306
+ });
307
+ respond(ws, {
308
+ id: null,
309
+ error: (0, shared_1.getErrorShape)({
310
+ config: router._def._config,
311
+ error,
312
+ type: 'unknown',
313
+ path: undefined,
314
+ input: undefined,
315
+ ctx: undefined,
316
+ }),
317
+ });
318
+ }
319
+ },
320
+ // @ts-expect-error Adds decoration on ws type
321
+ close(ws) {
322
+ for (const sub of ws.clientSubscriptions.values()) {
323
+ sub.unsubscribe();
324
+ }
325
+ ws.clientSubscriptions.clear();
326
+ allClients.delete(ws);
327
+ },
328
+ });
329
+ return {
330
+ broadcastReconnectNotification: () => {
331
+ const response = {
332
+ id: null,
333
+ method: 'reconnect',
334
+ };
335
+ const data = JSON.stringify(response);
336
+ allClients.forEach((v) => {
337
+ v.send(data);
338
+ });
339
+ // for (const client of wss.clients) {
340
+ // if (client.readyState === 1 /* ws.OPEN */) {
341
+ // client.send(data);
342
+ // }
343
+ // }
344
+ },
345
+ };
346
+ }
347
+ exports.applyWSHandler = applyWSHandler;
348
+ //# sourceMappingURL=applyWsHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyWsHandler.js","sourceRoot":"","sources":["../src/applyWsHandler.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -16,40 +16,35 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.createUWebSocketsHandler = void 0;
18
18
  const requestHandler_1 = require("./requestHandler");
19
+ const utils_1 = require("./utils");
20
+ const applyWsHandler_1 = require("./applyWsHandler");
19
21
  __exportStar(require("./types"), exports);
22
+ __exportStar(require("./applyWsHandler"), exports);
20
23
  /**
21
24
  * @param uWsApp uWebsockets server instance
22
25
  * @param prefix The path to trpc without trailing slash (ex: "/trpc")
23
26
  * @param opts handler options
24
27
  */
25
- function createUWebSocketsHandler(uWsApp, prefix, opts) {
26
- const prefixTrimLength = prefix.length + 1; // remove /* from url
28
+ function createUWebSocketsHandler(uWsApp, prefix, opts
29
+ // opts: uHTTPHandlerOptions<TRouter, TRequest, TResponse>
30
+ ) {
31
+ // const prefixTrimLength = prefix.length + 1; // remove /* from url
27
32
  const handler = (res, req) => {
28
- const method = req.getMethod().toUpperCase();
29
- const url = req.getUrl().substring(prefixTrimLength);
30
- const query = req.getQuery();
31
- const headers = {};
32
- req.forEach((key, value) => {
33
- // TODO handle headers with the same key, potential issue
34
- headers[key] = value;
35
- });
36
- // new request object needs to be created, because socket
37
- // can only be accessed synchronously, after await it cannot be accessed
38
- const wrappedReq = {
39
- headers,
40
- method,
41
- query,
42
- url,
43
- };
33
+ const wrappedReq = (0, utils_1.extractAndWrapHttpRequest)(prefix, req);
44
34
  (0, requestHandler_1.uWsHTTPRequestHandler)({
45
35
  req: wrappedReq,
46
- uRes: res,
47
- path: url,
36
+ res: res,
37
+ path: wrappedReq.url,
48
38
  ...opts,
49
39
  });
50
40
  };
51
41
  uWsApp.get(prefix + '/*', handler);
52
42
  uWsApp.post(prefix + '/*', handler);
43
+ if (opts.enableSubscriptions) {
44
+ // do something
45
+ (0, applyWsHandler_1.applyWSHandler)(uWsApp, prefix, opts);
46
+ // uWsApp.ws(prefix + '/*', behavior)
47
+ }
53
48
  }
54
49
  exports.createUWebSocketsHandler = createUWebSocketsHandler;
55
50
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAIA,qDAAyD;AAQzD,0CAAwB;AAExB;;;;GAIG;AACH,SAAgB,wBAAwB,CACtC,MAAoB,EACpB,MAAc,EACd,IAAkC;IAElC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,qBAAqB;IAEjE,MAAM,OAAO,GAAG,CAAC,GAAiB,EAAE,GAAgB,EAAE,EAAE;QACtD,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,EAAE,CAAC,WAAW,EAAoB,CAAC;QAC/D,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAE7B,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACzB,yDAAyD;YACzD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,yDAAyD;QACzD,wEAAwE;QACxE,MAAM,UAAU,GAAuB;YACrC,OAAO;YACP,MAAM;YACN,KAAK;YACL,GAAG;SACJ,CAAC;QAEF,IAAA,sCAAqB,EAAC;YACpB,GAAG,EAAE,UAAU;YACf,IAAI,EAAE,GAAG;YACT,IAAI,EAAE,GAAG;YACT,GAAG,IAAI;SACR,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AApCD,4DAoCC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAGA,qDAAyD;AAGzD,mCAAoD;AACpD,qDAAkD;AAElD,0CAAwB;AACxB,mDAAiC;AAEjC;;;;GAIG;AACH,SAAgB,wBAAwB,CACtC,MAAoB,EACpB,MAAc,EACd,IAAoE;AACpE,0DAA0D;;IAE1D,oEAAoE;IAEpE,MAAM,OAAO,GAAG,CAAC,GAAiB,EAAE,GAAgB,EAAE,EAAE;QACtD,MAAM,UAAU,GAAG,IAAA,iCAAyB,EAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE1D,IAAA,sCAAqB,EAAC;YACpB,GAAG,EAAE,UAAU;YACf,GAAG,EAAE,GAAG;YACR,IAAI,EAAE,UAAU,CAAC,GAAG;YACpB,GAAG,IAAI;SACR,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAEpC,IAAI,IAAI,CAAC,mBAAmB,EAAE;QAC5B,eAAe;QAEf,IAAA,+BAAc,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAErC,qCAAqC;KACtC;AACH,CAAC;AA5BD,4DA4BC"}
@@ -1,85 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uWsHTTPRequestHandler = void 0;
4
- const server_1 = require("@trpc/server");
5
4
  const utils_1 = require("./utils");
5
+ const http_1 = require("@trpc/server/http");
6
6
  async function uWsHTTPRequestHandler(opts) {
7
- const resOverride = {
8
- headers: [],
9
- status: 0,
10
- };
11
- const wrappedRes = {
12
- setStatus: (status) => {
13
- resOverride.status = status;
14
- },
15
- setHeader: (name, value) => {
16
- resOverride.headers.push({ name, value });
17
- // resOverride.headers.set(key, value);
18
- },
19
- };
20
- const createContext = async function _createContext() {
21
- return await opts.createContext?.({
22
- req: opts.req,
23
- res: wrappedRes,
7
+ const handleViaMiddleware = opts.middleware ?? ((_req, _res, next) => next());
8
+ return handleViaMiddleware(opts.req, opts.res, async (err) => {
9
+ if (err)
10
+ throw err;
11
+ const createContext = async () => {
12
+ return await opts.createContext?.(opts); // TODO type this up
13
+ };
14
+ // this may not be needed
15
+ const query = new URLSearchParams(opts.req.query);
16
+ const { res, req } = opts;
17
+ let aborted = false;
18
+ res.onAborted(() => {
19
+ // console.log('request was aborted');
20
+ aborted = true;
24
21
  });
25
- };
26
- const { path, router, uRes, req } = opts;
27
- let aborted = false;
28
- uRes.onAborted(() => {
29
- // console.log('request was aborted');
30
- aborted = true;
31
- });
32
- const bodyResult = await (0, utils_1.getPostBody)(req.method, uRes, opts.maxBodySize);
33
- const query = new URLSearchParams(opts.req.query);
34
- const requestObj = {
35
- method: opts.req.method,
36
- headers: opts.req.headers,
37
- query,
38
- body: bodyResult.ok ? bodyResult.data : undefined,
39
- };
40
- const result = await (0, server_1.resolveHTTPResponse)({
41
- batching: opts.batching,
42
- responseMeta: opts.responseMeta,
43
- path,
44
- createContext,
45
- router,
46
- req: requestObj,
47
- error: bodyResult.ok ? null : bodyResult.error,
48
- onError(o) {
49
- opts?.onError?.({
50
- ...o,
51
- req: opts.req,
52
- });
53
- },
54
- });
55
- if (aborted) {
56
- // TODO check this behavior
57
- return;
58
- }
59
- uRes.cork(() => {
60
- // if ('status' in result && (!res.statusCode || res.statusCode === 200)) {
61
- if (resOverride.status > 0) {
62
- uRes.writeStatus(resOverride.status.toString()); // TODO convert code to actual message
63
- }
64
- if ('status' in result) {
65
- uRes.writeStatus(result.status.toString());
66
- }
67
- //send our manual headers
68
- resOverride.headers.forEach((h) => {
69
- uRes.writeHeader(h.name, h.value);
70
- });
71
- for (const [key, value] of Object.entries(result.headers ?? {})) {
72
- if (typeof value === 'undefined') {
73
- continue;
74
- }
75
- if (Array.isArray(value))
76
- value.forEach((v) => {
77
- uRes.writeHeader(key, v);
22
+ const bodyResult = await (0, utils_1.getPostBody)(req.method, res, opts.maxBodySize);
23
+ const reqObj = {
24
+ method: opts.req.method,
25
+ headers: opts.req.headers,
26
+ query,
27
+ body: bodyResult.ok ? bodyResult.data : undefined,
28
+ };
29
+ const result = await (0, http_1.resolveHTTPResponse)({
30
+ batching: opts.batching,
31
+ responseMeta: opts.responseMeta,
32
+ path: opts.path,
33
+ createContext,
34
+ router: opts.router,
35
+ req: reqObj,
36
+ error: bodyResult.ok ? null : bodyResult.error,
37
+ preprocessedBody: false,
38
+ onError(o) {
39
+ opts?.onError?.({
40
+ ...o,
41
+ req: opts.req,
78
42
  });
79
- else
80
- uRes.writeHeader(key, value);
43
+ },
44
+ });
45
+ if (aborted) {
46
+ // TODO check this behavior
47
+ return;
81
48
  }
82
- (0, utils_1.sendResponse)(uRes, result.body);
49
+ res.cork(() => {
50
+ res.writeStatus(result.status.toString()); // is this okay?
51
+ // oldschool way of writing headers
52
+ for (const [key, value] of Object.entries(result.headers ?? {})) {
53
+ if (typeof value === 'undefined') {
54
+ continue;
55
+ }
56
+ if (Array.isArray(value))
57
+ value.forEach((v) => {
58
+ res.writeHeader(key, v);
59
+ });
60
+ else
61
+ res.writeHeader(key, value);
62
+ }
63
+ (0, utils_1.sendResponse)(res, result.body);
64
+ });
83
65
  });
84
66
  }
85
67
  exports.uWsHTTPRequestHandler = uWsHTTPRequestHandler;