serverless-event-orchestrator 2.0.1 → 2.3.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/LICENSE +21 -21
- package/README.md +489 -434
- package/dist/dispatcher.d.ts +6 -1
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +66 -7
- package/dist/dispatcher.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/event-type.enum.d.ts +1 -0
- package/dist/types/event-type.enum.d.ts.map +1 -1
- package/dist/types/event-type.enum.js +1 -0
- package/dist/types/event-type.enum.js.map +1 -1
- package/dist/types/routes.d.ts +6 -0
- package/dist/types/routes.d.ts.map +1 -1
- package/jest.config.js +32 -32
- package/package.json +82 -81
- package/src/dispatcher.ts +586 -519
- package/src/http/body-parser.ts +60 -60
- package/src/http/cors.ts +76 -76
- package/src/http/index.ts +3 -3
- package/src/http/response.ts +209 -209
- package/src/identity/extractor.ts +207 -207
- package/src/identity/index.ts +2 -2
- package/src/identity/jwt-verifier.ts +41 -41
- package/src/index.ts +128 -127
- package/src/middleware/crm-guard.ts +51 -51
- package/src/middleware/index.ts +3 -3
- package/src/middleware/init-tenant-context.ts +59 -59
- package/src/middleware/tenant-guard.ts +54 -54
- package/src/tenant/TenantContext.ts +115 -115
- package/src/tenant/helpers.ts +112 -112
- package/src/tenant/index.ts +21 -21
- package/src/tenant/types.ts +101 -101
- package/src/types/event-type.enum.ts +21 -20
- package/src/types/index.ts +2 -2
- package/src/types/routes.ts +218 -211
- package/src/utils/headers.ts +72 -72
- package/src/utils/index.ts +2 -2
- package/src/utils/path-matcher.ts +84 -84
- package/tests/cors.test.ts +133 -133
- package/tests/dispatcher.test.ts +795 -715
- package/tests/headers.test.ts +99 -99
- package/tests/identity.test.ts +301 -301
- package/tests/middleware/crm-guard.test.ts +69 -69
- package/tests/middleware/init-tenant-context.test.ts +147 -147
- package/tests/middleware/tenant-guard.test.ts +100 -100
- package/tests/path-matcher.test.ts +102 -102
- package/tests/response.test.ts +155 -155
- package/tests/tenant/TenantContext.test.ts +134 -134
- package/tests/tenant/helpers.test.ts +187 -187
- package/tsconfig.json +24 -24
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path matching utilities for extracting path parameters
|
|
3
|
-
* Supports patterns like /users/{id} and /users/{userId}/posts/{postId}
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Converts a route pattern to a regex and extracts parameter names
|
|
8
|
-
* @param pattern - Route pattern like /users/{id}
|
|
9
|
-
* @returns Object with regex and parameter names
|
|
10
|
-
*/
|
|
11
|
-
export function patternToRegex(pattern: string): { regex: RegExp; paramNames: string[] } {
|
|
12
|
-
const paramNames: string[] = [];
|
|
13
|
-
|
|
14
|
-
// First, handle :paramName format (Express style) BEFORE escaping
|
|
15
|
-
// This converts :id to {id} for uniform processing
|
|
16
|
-
let normalizedPattern = pattern.replace(/:(\w+)/g, '{$1}');
|
|
17
|
-
|
|
18
|
-
// Escape special regex characters except for our parameter syntax
|
|
19
|
-
let regexPattern = normalizedPattern
|
|
20
|
-
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
21
|
-
.replace(/\\\{(\w+)\\\}/g, (_, paramName) => {
|
|
22
|
-
paramNames.push(paramName);
|
|
23
|
-
return '([^/]+)';
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Ensure exact match
|
|
27
|
-
regexPattern = `^${regexPattern}$`;
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
regex: new RegExp(regexPattern),
|
|
31
|
-
paramNames,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Matches a path against a pattern and extracts parameters
|
|
37
|
-
* @param pattern - Route pattern like /users/{id}
|
|
38
|
-
* @param path - Actual path like /users/123
|
|
39
|
-
* @returns Extracted parameters or null if no match
|
|
40
|
-
*/
|
|
41
|
-
export function matchPath(pattern: string, path: string): Record<string, string> | null {
|
|
42
|
-
const { regex, paramNames } = patternToRegex(pattern);
|
|
43
|
-
const match = path.match(regex);
|
|
44
|
-
|
|
45
|
-
if (!match) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const params: Record<string, string> = {};
|
|
50
|
-
paramNames.forEach((name, index) => {
|
|
51
|
-
params[name] = match[index + 1];
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return params;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Checks if a pattern contains path parameters
|
|
59
|
-
* @param pattern - Route pattern to check
|
|
60
|
-
* @returns True if pattern has parameters
|
|
61
|
-
*/
|
|
62
|
-
export function hasPathParameters(pattern: string): boolean {
|
|
63
|
-
// Support both {id} and :id formats
|
|
64
|
-
return /\{[\w]+\}/.test(pattern) || /:[\w]+/.test(pattern);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Normalizes a path by removing trailing slashes and ensuring leading slash
|
|
69
|
-
* @param path - Path to normalize
|
|
70
|
-
* @returns Normalized path
|
|
71
|
-
*/
|
|
72
|
-
export function normalizePath(path: string): string {
|
|
73
|
-
if (!path) return '/';
|
|
74
|
-
|
|
75
|
-
// Ensure leading slash
|
|
76
|
-
let normalized = path.startsWith('/') ? path : `/${path}`;
|
|
77
|
-
|
|
78
|
-
// Remove trailing slash (except for root)
|
|
79
|
-
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
80
|
-
normalized = normalized.slice(0, -1);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return normalized;
|
|
84
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Path matching utilities for extracting path parameters
|
|
3
|
+
* Supports patterns like /users/{id} and /users/{userId}/posts/{postId}
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts a route pattern to a regex and extracts parameter names
|
|
8
|
+
* @param pattern - Route pattern like /users/{id}
|
|
9
|
+
* @returns Object with regex and parameter names
|
|
10
|
+
*/
|
|
11
|
+
export function patternToRegex(pattern: string): { regex: RegExp; paramNames: string[] } {
|
|
12
|
+
const paramNames: string[] = [];
|
|
13
|
+
|
|
14
|
+
// First, handle :paramName format (Express style) BEFORE escaping
|
|
15
|
+
// This converts :id to {id} for uniform processing
|
|
16
|
+
let normalizedPattern = pattern.replace(/:(\w+)/g, '{$1}');
|
|
17
|
+
|
|
18
|
+
// Escape special regex characters except for our parameter syntax
|
|
19
|
+
let regexPattern = normalizedPattern
|
|
20
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
21
|
+
.replace(/\\\{(\w+)\\\}/g, (_, paramName) => {
|
|
22
|
+
paramNames.push(paramName);
|
|
23
|
+
return '([^/]+)';
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Ensure exact match
|
|
27
|
+
regexPattern = `^${regexPattern}$`;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
regex: new RegExp(regexPattern),
|
|
31
|
+
paramNames,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Matches a path against a pattern and extracts parameters
|
|
37
|
+
* @param pattern - Route pattern like /users/{id}
|
|
38
|
+
* @param path - Actual path like /users/123
|
|
39
|
+
* @returns Extracted parameters or null if no match
|
|
40
|
+
*/
|
|
41
|
+
export function matchPath(pattern: string, path: string): Record<string, string> | null {
|
|
42
|
+
const { regex, paramNames } = patternToRegex(pattern);
|
|
43
|
+
const match = path.match(regex);
|
|
44
|
+
|
|
45
|
+
if (!match) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const params: Record<string, string> = {};
|
|
50
|
+
paramNames.forEach((name, index) => {
|
|
51
|
+
params[name] = match[index + 1];
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return params;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Checks if a pattern contains path parameters
|
|
59
|
+
* @param pattern - Route pattern to check
|
|
60
|
+
* @returns True if pattern has parameters
|
|
61
|
+
*/
|
|
62
|
+
export function hasPathParameters(pattern: string): boolean {
|
|
63
|
+
// Support both {id} and :id formats
|
|
64
|
+
return /\{[\w]+\}/.test(pattern) || /:[\w]+/.test(pattern);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Normalizes a path by removing trailing slashes and ensuring leading slash
|
|
69
|
+
* @param path - Path to normalize
|
|
70
|
+
* @returns Normalized path
|
|
71
|
+
*/
|
|
72
|
+
export function normalizePath(path: string): string {
|
|
73
|
+
if (!path) return '/';
|
|
74
|
+
|
|
75
|
+
// Ensure leading slash
|
|
76
|
+
let normalized = path.startsWith('/') ? path : `/${path}`;
|
|
77
|
+
|
|
78
|
+
// Remove trailing slash (except for root)
|
|
79
|
+
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
80
|
+
normalized = normalized.slice(0, -1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return normalized;
|
|
84
|
+
}
|
package/tests/cors.test.ts
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isPreflightRequest,
|
|
3
|
-
createPreflightResponse,
|
|
4
|
-
applyCorsHeaders,
|
|
5
|
-
withCors
|
|
6
|
-
} from '../src/http/cors';
|
|
7
|
-
import { HttpResponse } from '../src/http/response';
|
|
8
|
-
|
|
9
|
-
describe('isPreflightRequest', () => {
|
|
10
|
-
it('should return true for OPTIONS request', () => {
|
|
11
|
-
const event = { httpMethod: 'OPTIONS' };
|
|
12
|
-
expect(isPreflightRequest(event)).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should return true for lowercase options', () => {
|
|
16
|
-
const event = { httpMethod: 'options' };
|
|
17
|
-
expect(isPreflightRequest(event)).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should return false for GET request', () => {
|
|
21
|
-
const event = { httpMethod: 'GET' };
|
|
22
|
-
expect(isPreflightRequest(event)).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should return false for POST request', () => {
|
|
26
|
-
const event = { httpMethod: 'POST' };
|
|
27
|
-
expect(isPreflightRequest(event)).toBe(false);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should return false for undefined httpMethod', () => {
|
|
31
|
-
const event = {};
|
|
32
|
-
expect(isPreflightRequest(event)).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('createPreflightResponse', () => {
|
|
37
|
-
it('should return 204 with default CORS headers', () => {
|
|
38
|
-
const response = createPreflightResponse(true);
|
|
39
|
-
|
|
40
|
-
expect(response.statusCode).toBe(204);
|
|
41
|
-
expect(response.body).toBe('');
|
|
42
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
43
|
-
expect(response.headers?.['Access-Control-Allow-Methods']).toContain('GET');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should use custom CORS config', () => {
|
|
47
|
-
const response = createPreflightResponse({
|
|
48
|
-
origins: ['https://myapp.com'],
|
|
49
|
-
methods: ['GET', 'POST'],
|
|
50
|
-
credentials: true
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://myapp.com');
|
|
54
|
-
expect(response.headers?.['Access-Control-Allow-Methods']).toBe('GET, POST');
|
|
55
|
-
expect(response.headers?.['Access-Control-Allow-Credentials']).toBe('true');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('applyCorsHeaders', () => {
|
|
60
|
-
const originalResponse: HttpResponse = {
|
|
61
|
-
statusCode: 200,
|
|
62
|
-
body: JSON.stringify({ data: 'test' }),
|
|
63
|
-
headers: { 'Content-Type': 'application/json' }
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
it('should add CORS headers to response', () => {
|
|
67
|
-
const response = applyCorsHeaders(originalResponse, true);
|
|
68
|
-
|
|
69
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
70
|
-
expect(response.headers?.['Content-Type']).toBe('application/json');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should not modify response when cors is false', () => {
|
|
74
|
-
const response = applyCorsHeaders(originalResponse, false);
|
|
75
|
-
|
|
76
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBeUndefined();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should use custom CORS config', () => {
|
|
80
|
-
const response = applyCorsHeaders(originalResponse, {
|
|
81
|
-
origins: ['https://example.com'],
|
|
82
|
-
maxAge: 3600
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://example.com');
|
|
86
|
-
expect(response.headers?.['Access-Control-Max-Age']).toBe('3600');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('withCors', () => {
|
|
91
|
-
const mockHandler = jest.fn().mockResolvedValue({
|
|
92
|
-
statusCode: 200,
|
|
93
|
-
body: JSON.stringify({ success: true })
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
beforeEach(() => {
|
|
97
|
-
mockHandler.mockClear();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should handle preflight requests without calling handler', async () => {
|
|
101
|
-
const wrappedHandler = withCors(mockHandler, true);
|
|
102
|
-
|
|
103
|
-
const event = { eventRaw: { httpMethod: 'OPTIONS' } };
|
|
104
|
-
const response = await wrappedHandler(event);
|
|
105
|
-
|
|
106
|
-
expect(response.statusCode).toBe(204);
|
|
107
|
-
expect(mockHandler).not.toHaveBeenCalled();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should call handler and add CORS headers for non-preflight', async () => {
|
|
111
|
-
const wrappedHandler = withCors(mockHandler, true);
|
|
112
|
-
|
|
113
|
-
const event = { eventRaw: { httpMethod: 'GET' } };
|
|
114
|
-
const response = await wrappedHandler(event);
|
|
115
|
-
|
|
116
|
-
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
117
|
-
expect(response.statusCode).toBe(200);
|
|
118
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should work with custom CORS config', async () => {
|
|
122
|
-
const wrappedHandler = withCors(mockHandler, {
|
|
123
|
-
origins: ['https://myapp.com'],
|
|
124
|
-
credentials: true
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const event = { eventRaw: { httpMethod: 'POST' } };
|
|
128
|
-
const response = await wrappedHandler(event);
|
|
129
|
-
|
|
130
|
-
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://myapp.com');
|
|
131
|
-
expect(response.headers?.['Access-Control-Allow-Credentials']).toBe('true');
|
|
132
|
-
});
|
|
133
|
-
});
|
|
1
|
+
import {
|
|
2
|
+
isPreflightRequest,
|
|
3
|
+
createPreflightResponse,
|
|
4
|
+
applyCorsHeaders,
|
|
5
|
+
withCors
|
|
6
|
+
} from '../src/http/cors';
|
|
7
|
+
import { HttpResponse } from '../src/http/response';
|
|
8
|
+
|
|
9
|
+
describe('isPreflightRequest', () => {
|
|
10
|
+
it('should return true for OPTIONS request', () => {
|
|
11
|
+
const event = { httpMethod: 'OPTIONS' };
|
|
12
|
+
expect(isPreflightRequest(event)).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return true for lowercase options', () => {
|
|
16
|
+
const event = { httpMethod: 'options' };
|
|
17
|
+
expect(isPreflightRequest(event)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return false for GET request', () => {
|
|
21
|
+
const event = { httpMethod: 'GET' };
|
|
22
|
+
expect(isPreflightRequest(event)).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return false for POST request', () => {
|
|
26
|
+
const event = { httpMethod: 'POST' };
|
|
27
|
+
expect(isPreflightRequest(event)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return false for undefined httpMethod', () => {
|
|
31
|
+
const event = {};
|
|
32
|
+
expect(isPreflightRequest(event)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('createPreflightResponse', () => {
|
|
37
|
+
it('should return 204 with default CORS headers', () => {
|
|
38
|
+
const response = createPreflightResponse(true);
|
|
39
|
+
|
|
40
|
+
expect(response.statusCode).toBe(204);
|
|
41
|
+
expect(response.body).toBe('');
|
|
42
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
43
|
+
expect(response.headers?.['Access-Control-Allow-Methods']).toContain('GET');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should use custom CORS config', () => {
|
|
47
|
+
const response = createPreflightResponse({
|
|
48
|
+
origins: ['https://myapp.com'],
|
|
49
|
+
methods: ['GET', 'POST'],
|
|
50
|
+
credentials: true
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://myapp.com');
|
|
54
|
+
expect(response.headers?.['Access-Control-Allow-Methods']).toBe('GET, POST');
|
|
55
|
+
expect(response.headers?.['Access-Control-Allow-Credentials']).toBe('true');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('applyCorsHeaders', () => {
|
|
60
|
+
const originalResponse: HttpResponse = {
|
|
61
|
+
statusCode: 200,
|
|
62
|
+
body: JSON.stringify({ data: 'test' }),
|
|
63
|
+
headers: { 'Content-Type': 'application/json' }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
it('should add CORS headers to response', () => {
|
|
67
|
+
const response = applyCorsHeaders(originalResponse, true);
|
|
68
|
+
|
|
69
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
70
|
+
expect(response.headers?.['Content-Type']).toBe('application/json');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should not modify response when cors is false', () => {
|
|
74
|
+
const response = applyCorsHeaders(originalResponse, false);
|
|
75
|
+
|
|
76
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should use custom CORS config', () => {
|
|
80
|
+
const response = applyCorsHeaders(originalResponse, {
|
|
81
|
+
origins: ['https://example.com'],
|
|
82
|
+
maxAge: 3600
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://example.com');
|
|
86
|
+
expect(response.headers?.['Access-Control-Max-Age']).toBe('3600');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('withCors', () => {
|
|
91
|
+
const mockHandler = jest.fn().mockResolvedValue({
|
|
92
|
+
statusCode: 200,
|
|
93
|
+
body: JSON.stringify({ success: true })
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
mockHandler.mockClear();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should handle preflight requests without calling handler', async () => {
|
|
101
|
+
const wrappedHandler = withCors(mockHandler, true);
|
|
102
|
+
|
|
103
|
+
const event = { eventRaw: { httpMethod: 'OPTIONS' } };
|
|
104
|
+
const response = await wrappedHandler(event);
|
|
105
|
+
|
|
106
|
+
expect(response.statusCode).toBe(204);
|
|
107
|
+
expect(mockHandler).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should call handler and add CORS headers for non-preflight', async () => {
|
|
111
|
+
const wrappedHandler = withCors(mockHandler, true);
|
|
112
|
+
|
|
113
|
+
const event = { eventRaw: { httpMethod: 'GET' } };
|
|
114
|
+
const response = await wrappedHandler(event);
|
|
115
|
+
|
|
116
|
+
expect(mockHandler).toHaveBeenCalledTimes(1);
|
|
117
|
+
expect(response.statusCode).toBe(200);
|
|
118
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('*');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should work with custom CORS config', async () => {
|
|
122
|
+
const wrappedHandler = withCors(mockHandler, {
|
|
123
|
+
origins: ['https://myapp.com'],
|
|
124
|
+
credentials: true
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const event = { eventRaw: { httpMethod: 'POST' } };
|
|
128
|
+
const response = await wrappedHandler(event);
|
|
129
|
+
|
|
130
|
+
expect(response.headers?.['Access-Control-Allow-Origin']).toBe('https://myapp.com');
|
|
131
|
+
expect(response.headers?.['Access-Control-Allow-Credentials']).toBe('true');
|
|
132
|
+
});
|
|
133
|
+
});
|