trpc-uwebsockets 0.8.1 → 0.9.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
@@ -8,12 +8,6 @@
8
8
  yarn add trpc-uwebsockets
9
9
  ```
10
10
 
11
- or
12
-
13
- ```bash
14
- yarn add trpc-uwebsockets
15
- ```
16
-
17
11
  # Usage
18
12
 
19
13
  Import needed packages
@@ -21,6 +15,7 @@ Import needed packages
21
15
  ```typescript
22
16
  import { App } from 'uWebSockets.js';
23
17
  import * as trpc from '@trpc/server';
18
+ import z from 'zod';
24
19
  ```
25
20
 
26
21
  Define tRPC context and router
@@ -32,12 +27,11 @@ type Context = {
32
27
  } | null;
33
28
  };
34
29
 
35
- const createContext: any = (opts: UWebSocketsContextOptions): Context => {
30
+ const createContext = (opts: UWebSocketsContextOptions): Context => {
36
31
  const getUser = () => {
37
- if (opts.req.headers.authorization === 'meow') {
38
- return {
39
- name: 'KATT',
40
- };
32
+ if (opts?.req.headers.authorization) {
33
+ // const user = await decodeJwtToken(req.headers.authorization.split(' ')[1])
34
+ // return user;
41
35
  }
42
36
  return null;
43
37
  };
@@ -61,7 +55,7 @@ const router = trpc.router<Context>().query('hello', {
61
55
  });
62
56
  ```
63
57
 
64
- Initialize uWebsockets server and attach tTRP router
58
+ Initialize uWebsockets server and attach tRPC router
65
59
 
66
60
  ```typescript
67
61
  const app = App();
@@ -72,10 +66,35 @@ createUWebSocketsHandler(app, '/trpc', {
72
66
  });
73
67
 
74
68
  app.listen('0.0.0.0', 8000, () => {
75
- console.log('server listening on http://localhost:8000');
69
+ console.log('Server listening on http://localhost:8000');
76
70
  });
77
71
  ```
78
72
 
73
+ # API
74
+
75
+ Create context options
76
+
77
+ ```typescript
78
+ type UWebSocketsCreateContextOptions = {
79
+ /* read-only request information */
80
+ req: {
81
+ headers: Record<string, string>;
82
+ method: 'POST' | 'GET';
83
+ query: URLSearchParams;
84
+ path: string;
85
+ getCookies: (opts?: CookieParseOptions) => Record<string, string>;
86
+ };
87
+ /* */
88
+ res: {
89
+ setStatus(status: number): void;
90
+ setHeader(key: string, value: string): void;
91
+ setCookie(key: string, value: string, opts?: CookieSerializeOptions): void;
92
+ };
93
+ /* instance of the uWebSockets server */
94
+ uWs: TemplatedApp;
95
+ };
96
+ ```
97
+
79
98
  # Testing
80
99
 
81
100
  ```bash
@@ -87,3 +106,8 @@ or
87
106
  ```bash
88
107
  yarn test:watch
89
108
  ```
109
+
110
+ # Todo
111
+
112
+ - [ ] Expose batching options (like express adapter)
113
+ - [ ] Implement onError handling (like express adapter)
package/dist/index.js CHANGED
@@ -13,16 +13,20 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
13
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
16
19
  Object.defineProperty(exports, "__esModule", { value: true });
17
20
  exports.createUWebSocketsHandler = void 0;
18
21
  const server_1 = require("@trpc/server");
19
22
  const utils_1 = require("./utils");
23
+ const cookie_1 = __importDefault(require("cookie"));
20
24
  __exportStar(require("./types"), exports);
21
25
  /**
22
26
  *
23
27
  * @param uWsApp uWebsockets server instance
24
28
  * @param pathPrefix The path to endpoint without trailing slash (ex: "/trpc")
25
- * @param opts router and createContext functions
29
+ * @param opts router and createContext options
26
30
  */
27
31
  function createUWebSocketsHandler(uWsApp, pathPrefix, opts) {
28
32
  const prefixTrimLength = pathPrefix.length + 1; // remove /* from url
@@ -42,63 +46,94 @@ function createUWebSocketsHandler(uWsApp, pathPrefix, opts) {
42
46
  });
43
47
  // new request object needs to be created, because socket
44
48
  // can only be accessed synchronously, after await it cannot be accessed
45
- const requestObj = {
49
+ const request = {
46
50
  headers,
47
51
  method,
48
52
  query,
49
53
  path,
54
+ getCookies: (0, utils_1.getCookieFn)(headers),
55
+ };
56
+ const resOverride = {
57
+ headers: new Map(),
58
+ cookies: [],
59
+ status: 0,
60
+ };
61
+ const response = {
62
+ setCookie: (name, value, opts) => {
63
+ const serialized = cookie_1.default.serialize(name, value, opts); //.substring(12); //remove the "Set-Cookie: "
64
+ resOverride.cookies.push(serialized);
65
+ },
66
+ setStatus: (status) => {
67
+ resOverride.status = status;
68
+ },
69
+ setHeader: (key, value) => {
70
+ resOverride.headers.set(key, value);
71
+ },
50
72
  };
51
73
  const bodyResult = await (0, utils_1.readPostBody)(method, res);
52
74
  // req is no longer available!
53
75
  const createContext = async function _() {
54
76
  //res could be proxied here
55
77
  return await opts.createContext?.({
56
- // res,
57
- req: requestObj,
78
+ uWs: uWsApp,
79
+ req: request,
80
+ res: response,
58
81
  });
59
82
  };
60
- const fakeReqObject = {
83
+ const internalReqObj = {
61
84
  method,
62
85
  headers,
63
86
  query,
64
87
  body: bodyResult.ok ? bodyResult.data : undefined,
65
88
  };
66
89
  // TODO batching, onError options need implementation.
67
- // responseMeta is not applicable?
68
90
  const result = await (0, server_1.resolveHTTPResponse)({
69
91
  path,
70
92
  createContext,
71
93
  router: opts.router,
72
- req: fakeReqObject,
94
+ req: internalReqObj,
73
95
  error: bodyResult.ok ? null : bodyResult.error,
74
96
  });
75
- if ('status' in result) {
76
- res.writeStatus(result.status.toString()); //temp
77
- }
78
- else {
79
- // assume something went bad, should never happen?
80
- // there is no way to know from res object that something was send to the socket
81
- // can proxy it to detect res calls during createContext and resolve
82
- // then will need to exit from here
83
- res.cork(() => {
84
- res.writeStatus('500 INTERNAL SERVER ERROR');
85
- res.end();
86
- });
87
- return;
88
- }
89
- for (const [key, value] of Object.entries(result.headers ?? {})) {
90
- if (typeof value === 'undefined') {
91
- continue;
97
+ // user returned already, do nothing
98
+ res.cork(() => {
99
+ if (resOverride.status != 0) {
100
+ res.writeStatus(resOverride.status.toString());
92
101
  }
93
- // FIX not sure why it could be an array. This code path is not tested
94
- if (Array.isArray(value))
95
- value.forEach((header) => {
96
- res.writeHeader(key, header);
97
- });
98
- else
102
+ else if ('status' in result) {
103
+ res.writeStatus(result.status.toString());
104
+ }
105
+ else {
106
+ // assume something went bad, should never happen?
107
+ throw new Error('No status to send');
108
+ // res.writeStatus('500 INTERNAL SERVER ERROR');
109
+ // res.end();
110
+ // return;
111
+ }
112
+ //send all cookies
113
+ resOverride.cookies.forEach((value) => {
114
+ res.writeHeader('Set-Cookie', value);
115
+ });
116
+ resOverride.headers.forEach((value, key) => {
99
117
  res.writeHeader(key, value);
100
- }
101
- res.cork(() => {
118
+ });
119
+ for (const [key, value] of Object.entries(result.headers ?? {})) {
120
+ if (typeof value === 'undefined') {
121
+ continue;
122
+ }
123
+ // make sure to never override user defined headers
124
+ // cookies are an exception
125
+ if (resOverride.headers.has(key))
126
+ continue;
127
+ // not sure why it could be an array. This code path is not tested
128
+ // maybe its duplicates for the same key? like multiple "Set-Cookie"
129
+ if (Array.isArray(value))
130
+ value.forEach((header) => {
131
+ res.writeHeader(key, header);
132
+ });
133
+ else
134
+ res.writeHeader(key, value);
135
+ }
136
+ //now send user headers
102
137
  if (result.body)
103
138
  res.write(result.body);
104
139
  res.end();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/utils.js CHANGED
@@ -1,7 +1,20 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readPostBody = void 0;
6
+ exports.readPostBody = exports.getCookieFn = void 0;
4
7
  const server_1 = require("@trpc/server");
8
+ const cookie_1 = __importDefault(require("cookie"));
9
+ /*
10
+ cookie: 'cookie1=abc; cookie2=d.e'
11
+ */
12
+ const getCookieFn = (headers) => (opts) => {
13
+ if (!('cookie' in headers))
14
+ return {};
15
+ return cookie_1.default.parse(headers.cookie, opts);
16
+ };
17
+ exports.getCookieFn = getCookieFn;
5
18
  function readPostBody(method, res) {
6
19
  return new Promise((resolve) => {
7
20
  if (method == 'GET') {
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,yCAAyC;AAGzC,SAAgB,YAAY,CAAC,MAAc,EAAE,GAAiB;IAC5D,OAAO,IAAI,OAAO,CAEhB,CAAC,OAAO,EAAE,EAAE;QACZ,IAAI,MAAM,IAAI,KAAK,EAAE;YACnB,yBAAyB;YACzB,OAAO,CAAC;gBACN,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,SAAS;aAChB,CAAC,CAAC;SACJ;QAED,IAAI,MAAc,CAAC;QACnB,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAE9B,IAAI,MAAM,EAAE;gBACV,IAAI,MAAM,EAAE;oBACV,sCAAsC;oBACtC,OAAO,CAAC;wBACN,EAAE,EAAE,IAAI;wBACR,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,kBAAkB;qBAC5C,CAAC,CAAC;iBACJ;qBAAM;oBACL,mCAAmC;oBACnC,OAAO,CAAC;wBACN,EAAE,EAAE,IAAI;wBACR,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE;qBACvB,CAAC,CAAC;iBACJ;aACF;iBAAM;gBACL,IAAI,MAAM,EAAE;oBACV,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;iBACzC;qBAAM;oBACL,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;iBACjC;aACF;QACH,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,OAAO,CAAC;gBACN,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,IAAI,kBAAS,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;aACxD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AA9CD,oCA8CC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAyC;AAEzC,oDAAoD;AACpD;;EAEE;AAEK,MAAM,WAAW,GACtB,CAAC,OAA+B,EAAE,EAAE,CAAC,CAAC,IAAyB,EAAE,EAAE;IACjE,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,OAAO,gBAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC,CAAC;AALS,QAAA,WAAW,eAKpB;AAEJ,SAAgB,YAAY,CAAC,MAAc,EAAE,GAAiB;IAC5D,OAAO,IAAI,OAAO,CAEhB,CAAC,OAAO,EAAE,EAAE;QACZ,IAAI,MAAM,IAAI,KAAK,EAAE;YACnB,yBAAyB;YACzB,OAAO,CAAC;gBACN,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,SAAS;aAChB,CAAC,CAAC;SACJ;QAED,IAAI,MAAc,CAAC;QACnB,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAE9B,IAAI,MAAM,EAAE;gBACV,IAAI,MAAM,EAAE;oBACV,sCAAsC;oBACtC,OAAO,CAAC;wBACN,EAAE,EAAE,IAAI;wBACR,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,kBAAkB;qBAC5C,CAAC,CAAC;iBACJ;qBAAM;oBACL,mCAAmC;oBACnC,OAAO,CAAC;wBACN,EAAE,EAAE,IAAI;wBACR,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE;qBACvB,CAAC,CAAC;iBACJ;aACF;iBAAM;gBACL,IAAI,MAAM,EAAE;oBACV,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;iBACzC;qBAAM;oBACL,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;iBACjC;aACF;QACH,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,OAAO,CAAC;gBACN,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,IAAI,kBAAS,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;aACxD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AA9CD,oCA8CC"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "trpc-uwebsockets",
4
- "description": "uWebSockets adapter for tRPC",
5
- "version": "0.8.1",
4
+ "description": "tRPC adapter for uWebSockets.js server",
5
+ "version": "0.9.0",
6
6
  "main": "./dist/index.js",
7
7
  "type": "commonjs",
8
8
  "types": "./types/index.d.ts",
@@ -25,28 +25,30 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@trpc/server": "^9.26.2",
28
+ "cookie": "^0.5.0",
28
29
  "uWebSockets.js": "uNetworking/uWebSockets.js#v20.10.0"
29
30
  },
30
31
  "devDependencies": {
31
- "@types/jest": "^28.1.6",
32
- "babel-jest": "^28.1.3",
33
- "jest": "^28.1.3",
34
- "node-fetch": "2",
35
32
  "@rollup/plugin-typescript": "^8.3.3",
36
33
  "@swc/core": "^1.2.215",
37
34
  "@swc/jest": "^0.2.21",
38
35
  "@trpc/client": "^9.26.2",
36
+ "@types/cookie": "^0.5.1",
37
+ "@types/jest": "^28.1.6",
39
38
  "@types/node": "^18.0.6",
40
39
  "@types/webrtc": "^0.0.32",
41
40
  "@typescript-eslint/eslint-plugin": "^5.30.6",
42
41
  "@typescript-eslint/parser": "^5.30.6",
43
42
  "abort-controller": "^3.0.0",
43
+ "babel-jest": "^28.1.3",
44
44
  "eslint": "^8.19.0",
45
45
  "eslint-config-prettier": "^8.5.0",
46
46
  "eslint-plugin-jest": "^26.6.0",
47
47
  "eslint-plugin-prettier": "^4.2.1",
48
+ "jest": "^28.1.3",
48
49
  "jest-environment-jsdom": "^27.1.0",
49
50
  "jest-environment-node": "^28.1.3",
51
+ "node-fetch": "2",
50
52
  "prettier": "^2.7.1",
51
53
  "rimraf": "^3.0.2",
52
54
  "rollup": "^2.77.0",
@@ -63,7 +65,11 @@
63
65
  "test"
64
66
  ],
65
67
  "license": "MIT",
66
- "keywords": [],
68
+ "keywords": [
69
+ "trpc",
70
+ "uwebsockets",
71
+ "trpc adapter"
72
+ ],
67
73
  "repository": {
68
74
  "type": "git",
69
75
  "url": "https://github.com/romanzy-1612/trpc-uwebsockets.git"
package/src/index.ts CHANGED
@@ -4,28 +4,36 @@ import {
4
4
  resolveHTTPResponse,
5
5
  } from '@trpc/server';
6
6
  import { HTTPRequest } from '@trpc/server/dist/declarations/src/http/internals/types';
7
- import type * as uWs from 'uWebSockets.js';
7
+ import { CookieParseOptions, CookieSerializeOptions } from 'cookie';
8
+ import type {
9
+ HttpRequest,
10
+ HttpResponse,
11
+ RecognizedString,
12
+ TemplatedApp,
13
+ } from 'uWebSockets.js';
8
14
  import {
9
- UWebSocketsRegisterEndpointOptions,
15
+ UWebSocketsRegisterEndpointOptions as UWebSocketsCreateHandlerOptions,
10
16
  UWebSocketsRequestObject,
17
+ UWebSocketsResponseObject,
11
18
  } from './types';
12
- import { readPostBody } from './utils';
19
+ import { getCookieFn, readPostBody } from './utils';
20
+ import cookie from 'cookie';
13
21
  export * from './types';
14
22
 
15
23
  /**
16
24
  *
17
25
  * @param uWsApp uWebsockets server instance
18
26
  * @param pathPrefix The path to endpoint without trailing slash (ex: "/trpc")
19
- * @param opts router and createContext functions
27
+ * @param opts router and createContext options
20
28
  */
21
29
  export function createUWebSocketsHandler<TRouter extends AnyRouter>(
22
- uWsApp: uWs.TemplatedApp,
30
+ uWsApp: TemplatedApp,
23
31
  pathPrefix: string,
24
- opts: UWebSocketsRegisterEndpointOptions<TRouter>
32
+ opts: UWebSocketsCreateHandlerOptions<TRouter>
25
33
  ) {
26
34
  const prefixTrimLength = pathPrefix.length + 1; // remove /* from url
27
35
 
28
- const handler = async (res: uWs.HttpResponse, req: uWs.HttpRequest) => {
36
+ const handler = async (res: HttpResponse, req: HttpRequest) => {
29
37
  const method = req.getMethod().toUpperCase();
30
38
  if (method !== 'GET' && method !== 'POST') {
31
39
  // handle only get and post requests, while the rest
@@ -43,11 +51,35 @@ export function createUWebSocketsHandler<TRouter extends AnyRouter>(
43
51
 
44
52
  // new request object needs to be created, because socket
45
53
  // can only be accessed synchronously, after await it cannot be accessed
46
- const requestObj: UWebSocketsRequestObject = {
54
+ const request: UWebSocketsRequestObject = {
47
55
  headers,
48
56
  method,
49
57
  query,
50
58
  path,
59
+ getCookies: getCookieFn(headers),
60
+ };
61
+
62
+ const resOverride = {
63
+ headers: new Map<string, string>(),
64
+ cookies: [] as string[],
65
+ status: 0,
66
+ };
67
+
68
+ const response: UWebSocketsResponseObject = {
69
+ setCookie: (
70
+ name: string,
71
+ value: string,
72
+ opts?: CookieSerializeOptions
73
+ ) => {
74
+ const serialized = cookie.serialize(name, value, opts); //.substring(12); //remove the "Set-Cookie: "
75
+ resOverride.cookies.push(serialized);
76
+ },
77
+ setStatus: (status: number) => {
78
+ resOverride.status = status;
79
+ },
80
+ setHeader: (key: string, value: string) => {
81
+ resOverride.headers.set(key, value);
82
+ },
51
83
  };
52
84
 
53
85
  const bodyResult = await readPostBody(method, res);
@@ -59,12 +91,13 @@ export function createUWebSocketsHandler<TRouter extends AnyRouter>(
59
91
  > {
60
92
  //res could be proxied here
61
93
  return await opts.createContext?.({
62
- // res,
63
- req: requestObj,
94
+ uWs: uWsApp,
95
+ req: request,
96
+ res: response,
64
97
  });
65
98
  };
66
99
 
67
- const fakeReqObject: HTTPRequest = {
100
+ const internalReqObj: HTTPRequest = {
68
101
  method,
69
102
  headers,
70
103
  query,
@@ -72,43 +105,56 @@ export function createUWebSocketsHandler<TRouter extends AnyRouter>(
72
105
  };
73
106
 
74
107
  // TODO batching, onError options need implementation.
75
- // responseMeta is not applicable?
76
108
  const result = await resolveHTTPResponse({
77
109
  path,
78
110
  createContext,
79
111
  router: opts.router,
80
- req: fakeReqObject,
112
+ req: internalReqObj,
81
113
  error: bodyResult.ok ? null : bodyResult.error,
82
114
  });
83
115
 
84
- if ('status' in result) {
85
- res.writeStatus(result.status.toString()); //temp
86
- } else {
87
- // assume something went bad, should never happen?
88
- // there is no way to know from res object that something was send to the socket
89
- // can proxy it to detect res calls during createContext and resolve
90
- // then will need to exit from here
91
-
92
- res.cork(() => {
93
- res.writeStatus('500 INTERNAL SERVER ERROR');
94
- res.end();
116
+ // user returned already, do nothing
117
+ res.cork(() => {
118
+ if (resOverride.status != 0) {
119
+ res.writeStatus(resOverride.status.toString());
120
+ } else if ('status' in result) {
121
+ res.writeStatus(result.status.toString());
122
+ } else {
123
+ // assume something went bad, should never happen?
124
+ throw new Error('No status to send');
125
+
126
+ // res.writeStatus('500 INTERNAL SERVER ERROR');
127
+ // res.end();
128
+ // return;
129
+ }
130
+
131
+ //send all cookies
132
+ resOverride.cookies.forEach((value) => {
133
+ res.writeHeader('Set-Cookie', value);
134
+ });
135
+
136
+ resOverride.headers.forEach((value, key) => {
137
+ res.writeHeader(key, value);
95
138
  });
96
- return;
97
- }
98
139
 
99
- for (const [key, value] of Object.entries(result.headers ?? {})) {
100
- if (typeof value === 'undefined') {
101
- continue;
140
+ for (const [key, value] of Object.entries(result.headers ?? {})) {
141
+ if (typeof value === 'undefined') {
142
+ continue;
143
+ }
144
+ // make sure to never override user defined headers
145
+ // cookies are an exception
146
+ if (resOverride.headers.has(key)) continue;
147
+
148
+ // not sure why it could be an array. This code path is not tested
149
+ // maybe its duplicates for the same key? like multiple "Set-Cookie"
150
+ if (Array.isArray(value))
151
+ value.forEach((header) => {
152
+ res.writeHeader(key, header);
153
+ });
154
+ else res.writeHeader(key, value);
102
155
  }
103
- // FIX not sure why it could be an array. This code path is not tested
104
- if (Array.isArray(value))
105
- value.forEach((header) => {
106
- res.writeHeader(key, header);
107
- });
108
- else res.writeHeader(key, value);
109
- }
110
156
 
111
- res.cork(() => {
157
+ //now send user headers
112
158
  if (result.body) res.write(result.body);
113
159
  res.end();
114
160
  });
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AnyRouter, inferRouterContext } from '@trpc/server';
2
- import { HttpResponse } from 'uWebSockets.js';
3
-
2
+ import { TemplatedApp } from 'uWebSockets.js';
3
+ import { CookieParseOptions, CookieSerializeOptions } from 'cookie';
4
4
  export type UWebSocketsRegisterEndpointOptions<TRouter extends AnyRouter> = {
5
5
  router: TRouter;
6
6
  createContext?: (
@@ -13,12 +13,18 @@ export type UWebSocketsRequestObject = {
13
13
  method: 'POST' | 'GET';
14
14
  query: URLSearchParams;
15
15
  path: string;
16
+ getCookies: (opts?: CookieParseOptions) => Record<string, string>;
16
17
  };
17
18
 
18
19
  // if this to be used, it needs to be proxied
19
- export type UWebSocketsResponseObject = HttpResponse;
20
+ export type UWebSocketsResponseObject = {
21
+ setCookie(key: string, value: string, opts?: CookieSerializeOptions): void;
22
+ setStatus(status: number): void;
23
+ setHeader(key: string, value: string): void;
24
+ };
20
25
 
21
26
  export type UWebSocketsCreateContextOptions = {
22
27
  req: UWebSocketsRequestObject;
23
- // res: UWebSocketsResponseObject;
28
+ uWs: TemplatedApp;
29
+ res: UWebSocketsResponseObject;
24
30
  };
package/src/utils.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  import { TRPCError } from '@trpc/server';
2
2
  import { HttpResponse } from 'uWebSockets.js';
3
+ import cookie, { CookieParseOptions } from 'cookie';
4
+ /*
5
+ cookie: 'cookie1=abc; cookie2=d.e'
6
+ */
7
+
8
+ export const getCookieFn =
9
+ (headers: Record<string, string>) => (opts?: CookieParseOptions) => {
10
+ if (!('cookie' in headers)) return {};
11
+
12
+ return cookie.parse(headers.cookie, opts);
13
+ };
3
14
 
4
15
  export function readPostBody(method: string, res: HttpResponse) {
5
16
  return new Promise<
@@ -1,38 +1,17 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import AbortController from 'abort-controller';
3
3
  import fetch from 'node-fetch';
4
- import { z } from 'zod';
5
4
  import { UWebSocketsCreateContextOptions } from '../src/types';
6
5
  import uWs from 'uWebSockets.js';
7
-
6
+ import z from 'zod';
8
7
  import * as trpc from '@trpc/server';
9
- import { TRPCError } from '@trpc/server';
8
+ import { inferAsyncReturnType, TRPCError } from '@trpc/server';
10
9
  import { createUWebSocketsHandler } from '../src/index';
11
- import { createTRPCClient } from '@trpc/client';
12
-
13
- const testPort = 8732;
10
+ import { createTRPCClient, HTTPHeaders } from '@trpc/client';
14
11
 
15
- type Context = {
16
- user: {
17
- name: string;
18
- } | null;
19
- };
20
- async function startServer() {
21
- const createContext = (_opts: UWebSocketsCreateContextOptions): Context => {
22
- const getUser = () => {
23
- if (_opts.req.headers.authorization === 'meow') {
24
- return {
25
- name: 'KATT',
26
- };
27
- }
28
- return null;
29
- };
30
-
31
- return {
32
- user: getUser(),
33
- };
34
- };
12
+ const testPort = 8799;
35
13
 
14
+ function makeRouter() {
36
15
  const router = trpc
37
16
  .router<Context>()
38
17
  .query('hello', {
@@ -65,8 +44,58 @@ async function startServer() {
65
44
  user: ctx.user,
66
45
  };
67
46
  },
47
+ })
48
+ .query('cookie-monster', {
49
+ resolve({ input, ctx }) {
50
+ const cookies = ctx.req.getCookies();
51
+
52
+ const combined = cookies['cookie1'] + cookies['cookie2'];
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
+ },
68
62
  });
69
63
 
64
+ return router;
65
+ }
66
+ export type Router = ReturnType<typeof makeRouter>;
67
+
68
+ function makeContext() {
69
+ const createContext = ({
70
+ req,
71
+ res,
72
+ uWs,
73
+ }: UWebSocketsCreateContextOptions) => {
74
+ const getUser = () => {
75
+ if (req.headers.authorization === 'meow') {
76
+ return {
77
+ name: 'KATT',
78
+ };
79
+ }
80
+ if (req.getCookies()?.user === 'romanzy')
81
+ return {
82
+ name: 'romanzy',
83
+ };
84
+ return null;
85
+ };
86
+
87
+ return {
88
+ req,
89
+ res,
90
+ uWs,
91
+ user: getUser(),
92
+ };
93
+ };
94
+
95
+ return createContext;
96
+ }
97
+ export type Context = inferAsyncReturnType<ReturnType<typeof makeContext>>;
98
+ async function startServer() {
70
99
  const app = uWs.App();
71
100
 
72
101
  // Handle CORS
@@ -76,51 +105,58 @@ async function startServer() {
76
105
  res.end();
77
106
  });
78
107
 
108
+ app.get('/', (res) => {
109
+ res.writeStatus('200 OK');
110
+
111
+ res.end();
112
+ });
113
+
79
114
  // need to register everything on the app object,
80
115
  // as uWebSockets does not have middleware
81
116
  createUWebSocketsHandler(app, '/trpc', {
82
- router,
83
- createContext,
117
+ router: makeRouter(),
118
+ createContext: makeContext(),
84
119
  });
85
120
 
86
- const { server, socket } = await new Promise<{
87
- server: uWs.TemplatedApp;
121
+ app.put('/trpc/put', (res) => {
122
+ res.writeStatus('204');
123
+ res.end();
124
+ });
125
+
126
+ const { socket } = await new Promise<{
88
127
  socket: uWs.us_listen_socket;
89
128
  }>((resolve) => {
90
129
  app.listen('0.0.0.0', testPort, (socket) => {
91
130
  resolve({
92
- server: app,
93
131
  socket,
94
132
  });
95
133
  });
96
134
  });
97
135
 
98
- const client = createTRPCClient<typeof router>({
99
- url: `http://localhost:${testPort}/trpc`,
100
-
101
- AbortController: AbortController as any,
102
- fetch: fetch as any,
103
- headers: {
104
- authorization: 'meow',
105
- },
106
- });
107
-
108
136
  return {
109
137
  close: () =>
110
138
  new Promise<void>((resolve, reject) => {
111
139
  try {
112
140
  uWs.us_listen_socket_close(socket);
141
+ resolve();
113
142
  } catch (error) {
114
143
  reject();
115
144
  }
116
- resolve();
117
145
  }),
118
- router,
119
- client,
120
146
  };
121
147
  }
122
148
 
123
- let t: trpc.inferAsyncReturnType<typeof startServer>;
149
+ function makeClient(headers) {
150
+ return createTRPCClient<Router>({
151
+ url: `http://localhost:${testPort}/trpc`,
152
+
153
+ AbortController: AbortController as any,
154
+ fetch: fetch as any,
155
+ headers,
156
+ });
157
+ }
158
+
159
+ let t!: trpc.inferAsyncReturnType<typeof startServer>;
124
160
  beforeEach(async () => {
125
161
  t = await startServer();
126
162
  });
@@ -129,8 +165,11 @@ afterEach(async () => {
129
165
  });
130
166
 
131
167
  test('simple query', async () => {
168
+ // t.client.runtime.headers = ()
169
+ const client = makeClient({});
170
+
132
171
  expect(
133
- await t.client.query('hello', {
172
+ await client.query('hello', {
134
173
  who: 'test',
135
174
  })
136
175
  ).toMatchInlineSnapshot(`
@@ -139,25 +178,16 @@ test('simple query', async () => {
139
178
  }
140
179
  `);
141
180
 
142
- // t.client.runtime.headers()
143
-
144
- expect(await t.client.query('hello')).toMatchInlineSnapshot(`
145
- Object {
146
- "text": "hello KATT",
147
- }
148
- `);
181
+ expect(client.query('error', null)).rejects.toThrowError('error as expected');
149
182
  });
150
183
 
151
- // Error status codes are correct
152
- test('error handling', async () => {
153
- expect(t.client.query('error', null)).rejects.toThrowError(
154
- 'error as expected'
155
- );
156
- });
184
+ test('mutation with header', async () => {
185
+ const client = makeClient({
186
+ authorization: 'meow',
187
+ });
157
188
 
158
- test('simple mutation', async () => {
159
189
  expect(
160
- await t.client.mutation('test', {
190
+ await client.mutation('test', {
161
191
  value: 'lala',
162
192
  })
163
193
  ).toMatchInlineSnapshot(`
@@ -169,3 +199,44 @@ test('simple mutation', async () => {
169
199
  }
170
200
  `);
171
201
  });
202
+
203
+ // Error status codes are correct
204
+ test('reads cookies', async () => {
205
+ const client = makeClient({
206
+ cookie: 'cookie1=abc; cookie2=d.e; user=romanzy',
207
+ });
208
+
209
+ expect(await client.query('cookie-monster')).toMatchInlineSnapshot(`
210
+ Object {
211
+ "combined": "abcd.e",
212
+ "user": Object {
213
+ "name": "romanzy",
214
+ },
215
+ }
216
+ `);
217
+ });
218
+
219
+ test('setting cookies and headers', async () => {
220
+ const monsterRes = await fetch(
221
+ `http://localhost:${testPort}/trpc/cookie-monster`
222
+ );
223
+ expect(monsterRes.status).toEqual(201);
224
+ expect(monsterRes.headers.get('set-cookie')).toEqual(
225
+ 'one=nom, two=nom%20nom'
226
+ );
227
+ expect(monsterRes.headers.get('x-spooked')).toEqual('true');
228
+
229
+ const indexRes = await fetch(`http://localhost:${testPort}`);
230
+ expect(indexRes.status).toEqual(200);
231
+
232
+ const putRes = await fetch(`http://localhost:${testPort}/trpc/put`, {
233
+ method: 'PUT',
234
+ });
235
+ expect(putRes.status).toEqual(204);
236
+
237
+ const badInput = '{"who": "test';
238
+ const badRes = await fetch(
239
+ `http://localhost:${testPort}/trpc/hello?input=${badInput}`
240
+ );
241
+ expect(badRes.status).toEqual(400);
242
+ });
package/types/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { AnyRouter } from '@trpc/server';
2
- import type * as uWs from 'uWebSockets.js';
3
- import { UWebSocketsRegisterEndpointOptions } from './types';
2
+ import type { TemplatedApp } from 'uWebSockets.js';
3
+ import { UWebSocketsRegisterEndpointOptions as UWebSocketsCreateHandlerOptions } from './types';
4
4
  export * from './types';
5
5
  /**
6
6
  *
7
7
  * @param uWsApp uWebsockets server instance
8
8
  * @param pathPrefix The path to endpoint without trailing slash (ex: "/trpc")
9
- * @param opts router and createContext functions
9
+ * @param opts router and createContext options
10
10
  */
11
- export declare function createUWebSocketsHandler<TRouter extends AnyRouter>(uWsApp: uWs.TemplatedApp, pathPrefix: string, opts: UWebSocketsRegisterEndpointOptions<TRouter>): void;
11
+ export declare function createUWebSocketsHandler<TRouter extends AnyRouter>(uWsApp: TemplatedApp, pathPrefix: string, opts: UWebSocketsCreateHandlerOptions<TRouter>): void;
package/types/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { AnyRouter, inferRouterContext } from '@trpc/server';
2
- import { HttpResponse } from 'uWebSockets.js';
2
+ import { TemplatedApp } from 'uWebSockets.js';
3
+ import { CookieParseOptions, CookieSerializeOptions } from 'cookie';
3
4
  export declare type UWebSocketsRegisterEndpointOptions<TRouter extends AnyRouter> = {
4
5
  router: TRouter;
5
6
  createContext?: (opts: UWebSocketsCreateContextOptions) => Promise<inferRouterContext<TRouter>> | inferRouterContext<TRouter>;
@@ -9,8 +10,15 @@ export declare type UWebSocketsRequestObject = {
9
10
  method: 'POST' | 'GET';
10
11
  query: URLSearchParams;
11
12
  path: string;
13
+ getCookies: (opts?: CookieParseOptions) => Record<string, string>;
14
+ };
15
+ export declare type UWebSocketsResponseObject = {
16
+ setCookie(key: string, value: string, opts?: CookieSerializeOptions): void;
17
+ setStatus(status: number): void;
18
+ setHeader(key: string, value: string): void;
12
19
  };
13
- export declare type UWebSocketsResponseObject = HttpResponse;
14
20
  export declare type UWebSocketsCreateContextOptions = {
15
21
  req: UWebSocketsRequestObject;
22
+ uWs: TemplatedApp;
23
+ res: UWebSocketsResponseObject;
16
24
  };
package/types/utils.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { TRPCError } from '@trpc/server';
2
2
  import { HttpResponse } from 'uWebSockets.js';
3
+ import { CookieParseOptions } from 'cookie';
4
+ export declare const getCookieFn: (headers: Record<string, string>) => (opts?: CookieParseOptions) => Record<string, string>;
3
5
  export declare function readPostBody(method: string, res: HttpResponse): Promise<{
4
6
  ok: true;
5
7
  data: unknown;