Skip to content

Commit 658a612

Browse files
authored
feat: CORS (#5)
1 parent 217164b commit 658a612

File tree

15 files changed

+103
-84
lines changed

15 files changed

+103
-84
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
const app = await createApp({
55
host: 'localhost',
66
port: 3000,
7-
cors: [],
87
});
98

109
app.plugin(dbPlugin);

__tests__/events.test.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ declare module '../src' {
1010

1111
describe('events', () => {
1212
it('should emit system events server, request and response', async () => {
13-
const app = await createApp({
14-
hostname: 'localhost',
15-
port: 3000,
16-
cors: [],
17-
}).route({
13+
const app = createApp({}).route({
1814
path: '/users/:userId/invoices/:invoiceId',
1915
method: 'post',
2016
validation: {
@@ -45,11 +41,7 @@ describe('events', () => {
4541
});
4642

4743
it('should allow emitting custom events', async () => {
48-
const app = await createApp({
49-
hostname: 'localhost',
50-
port: 3000,
51-
cors: [],
52-
}).route({
44+
const app = createApp({}).route({
5345
path: '/users',
5446
method: 'post',
5547
validation: {

__tests__/handler.test-d.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import { expectType } from 'tsd';
33

44
import { createApp } from '../src';
55

6-
const app = createApp({
7-
hostname: 'localhost',
8-
port: 3000,
9-
cors: [],
10-
});
6+
const app = createApp({});
117

128
void app.route({
139
path: '/users/:userId/invoices/:invoiceId',

__tests__/handler.test.ts

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import { createApp } from '../src';
55
describe('handler', () => {
66
describe('validation', () => {
77
it('should error on invalid parameters', async () => {
8-
const app = createApp({
9-
hostname: 'localhost',
10-
port: 3000,
11-
cors: [],
12-
}).route({
8+
const app = createApp({}).route({
139
path: '/users/:userId/invoices/:invoiceId',
1410
method: 'get',
1511
validation: {
@@ -54,11 +50,7 @@ Object {
5450
});
5551

5652
it('should error on invalid query', async () => {
57-
const app = createApp({
58-
hostname: 'localhost',
59-
port: 3000,
60-
cors: [],
61-
}).route({
53+
const app = createApp({}).route({
6254
path: '/users',
6355
method: 'get',
6456
validation: {
@@ -98,11 +90,7 @@ Object {
9890
});
9991

10092
it('should error on invalid payload', async () => {
101-
const app = createApp({
102-
hostname: 'localhost',
103-
port: 3000,
104-
cors: [],
105-
}).route({
93+
const app = createApp({}).route({
10694
path: '/users',
10795
method: 'post',
10896
validation: {
@@ -152,11 +140,7 @@ Object {
152140
});
153141

154142
it('should return 500 on invalid response', async () => {
155-
const app = createApp({
156-
hostname: 'localhost',
157-
port: 3000,
158-
cors: [],
159-
}).route({
143+
const app = createApp({}).route({
160144
path: '/users',
161145
method: 'get',
162146
validation: {
@@ -184,11 +168,7 @@ Object {
184168
});
185169

186170
it('should parse and validate query, params and payload', async () => {
187-
const app = createApp({
188-
hostname: 'localhost',
189-
port: 3000,
190-
cors: [],
191-
}).route({
171+
const app = createApp({}).route({
192172
path: '/users/:userId/invoices/:invoiceId',
193173
method: 'post',
194174
validation: {
@@ -222,11 +202,7 @@ Object {
222202

223203
describe('happy path', () => {
224204
it('should return data when all validation passes', async () => {
225-
const app = createApp({
226-
hostname: 'localhost',
227-
port: 3000,
228-
cors: [],
229-
}).route({
205+
const app = createApp({}).route({
230206
path: '/users/:userId/invoices/:invoiceId',
231207
method: 'post',
232208
validation: {
@@ -256,11 +232,7 @@ Object {
256232
});
257233

258234
it('should return 204 on empty response', async () => {
259-
const app = createApp({
260-
hostname: 'localhost',
261-
port: 3000,
262-
cors: [],
263-
}).route({
235+
const app = createApp({}).route({
264236
path: '/users',
265237
method: 'get',
266238
validation: {},
@@ -278,11 +250,7 @@ Object {
278250
const uniqueRequestIds = new Set<string>();
279251
const uniqueServerIds = new Set<string>();
280252

281-
const app = createApp({
282-
hostname: 'localhost',
283-
port: 3000,
284-
cors: [],
285-
}).route({
253+
const app = createApp({}).route({
286254
path: '/users',
287255
method: 'get',
288256
validation: {},

__tests__/merge.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { deepMerge } from '../src/utils/merge';
2+
3+
describe('deepMerge', () => {
4+
it('should merge', () => {
5+
const obj1 = { par1: -1, par2: { par2_1: -21, par2_5: -25 }, arr: [0, 1, 2] };
6+
const obj2 = { par1: 1, par2: { par2_1: 21 }, par3: 3, arr: [3, 4, 5] };
7+
const obj3 = deepMerge(obj1, obj2);
8+
expect(obj3).toEqual({ par1: -1, par2: { par2_1: -21, par2_5: -25 }, par3: 3, arr: [0, 1, 2] });
9+
});
10+
});

__tests__/router.test.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ declare module '../src' {
88

99
describe('router', () => {
1010
it('should not accept multiple params in path segment', () => {
11-
const app = createApp({
12-
hostname: 'localhost',
13-
port: 3000,
14-
cors: [],
15-
});
11+
const app = createApp({});
1612

1713
return expect(() =>
1814
app.route({
@@ -30,11 +26,7 @@ describe('router', () => {
3026
});
3127

3228
it('should not accept regexes', () => {
33-
const app = createApp({
34-
hostname: 'localhost',
35-
port: 3000,
36-
cors: [],
37-
});
29+
const app = createApp({});
3830

3931
return expect(() =>
4032
app.route({

examples/simple.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,7 @@ const loggerPlugin = createPlugin('logger', (app) => {
3333
});
3434

3535
// app
36-
const app = createApp({
37-
hostname: 'localhost',
38-
port: 3000,
39-
cors: [],
40-
});
36+
const app = createApp({});
4137

4238
void app.plugin(loggerPlugin);
4339
void app.plugin(dbPlugin);

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
],
3636
"dependencies": {
3737
"@typeofweb/schema": "0.8.0-5",
38+
"@types/cors": "2.8.10",
3839
"@types/express": "4.17.12",
3940
"@types/supertest": "2.0.11",
41+
"cors": "2.8.5",
4042
"express": "4.17.1",
4143
"stoppable": "1.1.0",
4244
"supertest": "6.1.3"

rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ LICENSE file in the root directory of this source tree.
9090
`.trim(),
9191
}),
9292
],
93-
external: ['url', 'body-parser', 'express', 'stoppable', '@typeofweb/schema', 'events', 'supertest'],
93+
external: ['url', 'body-parser', 'express', 'stoppable', '@typeofweb/schema', 'events', 'supertest', 'cors', 'os'],
9494
},
9595
];
9696
// eslint-disable-next-line import/no-default-export

src/modules/app.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
import { URL } from 'url';
22

3+
import Cors from 'cors';
34
import Supertest from 'supertest';
45

6+
import { deepMerge } from '../utils/merge';
57
import { generateServerId } from '../utils/uniqueId';
68

79
import { createEventBus } from './events';
810
import { initApp, listenExpressServer } from './http';
911
import { initRouter, validateRoute } from './router';
1012

11-
import type { DeepWritable } from '../utils/types';
13+
import type { DeepPartial, DeepWritable } from '../utils/types';
1214
import type { TypeOfWebServerMeta } from './augment';
1315
import type { TypeOfWebPluginInternal } from './plugins';
1416
import type { AppOptions, TypeOfWebApp, TypeOfWebServer } from './shared';
1517

16-
export function createApp(options: AppOptions): TypeOfWebApp {
18+
const defaultAppOptions: AppOptions = {
19+
hostname: 'localhost',
20+
port: 3000,
21+
cors: {
22+
origin: true,
23+
credentials: true,
24+
},
25+
};
26+
27+
export function createApp(opts: DeepPartial<AppOptions>): TypeOfWebApp {
28+
const options = deepMerge(opts, defaultAppOptions);
29+
1730
const server: DeepWritable<TypeOfWebServer> = {
1831
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- these properties are supposed to be added by the plugins inside `async start()`
1932
plugins: {} as TypeOfWebServer['plugins'],
@@ -59,13 +72,22 @@ export function createApp(options: AppOptions): TypeOfWebApp {
5972
return;
6073
}
6174

75+
if (options.cors) {
76+
app._rawExpressApp.use(
77+
Cors({
78+
origin: options.cors.origin,
79+
credentials: options.cors.credentials,
80+
}),
81+
);
82+
}
83+
6284
await initServerPlugins();
6385

6486
app._rawExpressRouter = initRouter({ server, routes, plugins });
6587
app._rawExpressApp.use(app._rawExpressRouter);
6688

67-
server.events.emit(':server', server);
6889
mutableIsInitialized = true;
90+
server.events.emit(':server', server);
6991
}
7092

7193
const app: DeepWritable<TypeOfWebApp> = {
@@ -95,6 +117,7 @@ export function createApp(options: AppOptions): TypeOfWebApp {
95117
if (injection.payload) {
96118
mutableTest = mutableTest.send(injection.payload);
97119
}
120+
98121
if (injection.headers) {
99122
mutableTest = Object.entries(injection.headers).reduce(
100123
(acc, [header, value]) => acc.set(header, value),

0 commit comments

Comments
 (0)