serverless-event-orchestrator 2.2.0 → 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 -489
- package/dist/dispatcher.d.ts +6 -1
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +31 -7
- package/dist/dispatcher.js.map +1 -1
- package/jest.config.js +32 -32
- package/package.json +82 -81
- package/src/dispatcher.ts +586 -558
- 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 -128
- 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 -21
- package/src/types/index.ts +2 -2
- package/src/types/routes.ts +218 -218
- 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
package/src/types/routes.ts
CHANGED
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
import { RouteSegment } from './event-type.enum.js';
|
|
2
|
-
import type { TenantInfo } from '../tenant/types.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* HTTP methods supported by the router
|
|
6
|
-
*/
|
|
7
|
-
export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Middleware function signature
|
|
11
|
-
* Returns the modified event or throws an error to halt execution
|
|
12
|
-
*/
|
|
13
|
-
export type MiddlewareFn = (event: NormalizedEvent) => Promise<NormalizedEvent | void>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Route configuration for a single endpoint
|
|
17
|
-
*/
|
|
18
|
-
export interface RouteConfig {
|
|
19
|
-
handler: (event: NormalizedEvent) => Promise<any>;
|
|
20
|
-
middleware?: MiddlewareFn[];
|
|
21
|
-
cors?: boolean | CorsConfig;
|
|
22
|
-
rateLimit?: RateLimitConfig;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* CORS configuration options
|
|
27
|
-
*/
|
|
28
|
-
export interface CorsConfig {
|
|
29
|
-
origins: string[] | '*';
|
|
30
|
-
methods?: string[];
|
|
31
|
-
headers?: string[];
|
|
32
|
-
credentials?: boolean;
|
|
33
|
-
maxAge?: number;
|
|
34
|
-
exposedHeaders?: string[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Rate limit configuration
|
|
39
|
-
*/
|
|
40
|
-
export interface RateLimitConfig {
|
|
41
|
-
burstLimit: number;
|
|
42
|
-
rateLimit: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Standard HTTP router structure
|
|
47
|
-
* Maps HTTP methods to path-handler pairs
|
|
48
|
-
*/
|
|
49
|
-
export type HttpRouter = {
|
|
50
|
-
[K in HttpMethod]?: {
|
|
51
|
-
[path: string]: RouteConfig;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Segmented HTTP router for access control categorization
|
|
57
|
-
* Allows organizing routes by security context
|
|
58
|
-
*/
|
|
59
|
-
export interface SegmentedHttpRouter {
|
|
60
|
-
public?: HttpRouter;
|
|
61
|
-
private?: HttpRouter;
|
|
62
|
-
backoffice?: HttpRouter;
|
|
63
|
-
internal?: HttpRouter;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Segment configuration with optional middleware
|
|
68
|
-
*/
|
|
69
|
-
export interface SegmentConfig {
|
|
70
|
-
routes: HttpRouter;
|
|
71
|
-
middleware?: MiddlewareFn[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Advanced segmented router with per-segment middleware
|
|
76
|
-
*/
|
|
77
|
-
export interface AdvancedSegmentedRouter {
|
|
78
|
-
public?: SegmentConfig | HttpRouter;
|
|
79
|
-
private?: SegmentConfig | HttpRouter;
|
|
80
|
-
backoffice?: SegmentConfig | HttpRouter;
|
|
81
|
-
internal?: SegmentConfig | HttpRouter;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* EventBridge routes configuration
|
|
86
|
-
* Maps operation names to handlers
|
|
87
|
-
*/
|
|
88
|
-
export type EventBridgeRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Lambda invocation routes
|
|
92
|
-
*/
|
|
93
|
-
export type LambdaRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* SQS queue routes
|
|
97
|
-
* Maps queue names to handlers
|
|
98
|
-
*/
|
|
99
|
-
export type SqsRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Scheduled event routes (EventBridge Scheduler / CloudWatch Events rules)
|
|
103
|
-
* Maps rule names to handlers. Use 'default' as fallback.
|
|
104
|
-
*/
|
|
105
|
-
export type ScheduledRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Complete dispatch routes configuration
|
|
109
|
-
*/
|
|
110
|
-
export interface DispatchRoutes {
|
|
111
|
-
apigateway?: HttpRouter | SegmentedHttpRouter | AdvancedSegmentedRouter;
|
|
112
|
-
eventbridge?: EventBridgeRoutes;
|
|
113
|
-
lambda?: LambdaRoutes;
|
|
114
|
-
sqs?: SqsRoutes;
|
|
115
|
-
scheduled?: ScheduledRoutes;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Identity context extracted from the event
|
|
120
|
-
*/
|
|
121
|
-
export interface IdentityContext {
|
|
122
|
-
userId?: string;
|
|
123
|
-
email?: string;
|
|
124
|
-
groups?: string[];
|
|
125
|
-
issuer?: string;
|
|
126
|
-
claims?: Record<string, any>;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Route match result with extracted parameters
|
|
131
|
-
*/
|
|
132
|
-
export interface RouteMatch {
|
|
133
|
-
handler: (event: NormalizedEvent) => Promise<any>;
|
|
134
|
-
params: Record<string, string>;
|
|
135
|
-
segment: RouteSegment;
|
|
136
|
-
middleware?: MiddlewareFn[];
|
|
137
|
-
config: RouteConfig;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Normalized event structure passed to handlers
|
|
142
|
-
*/
|
|
143
|
-
export interface NormalizedEvent {
|
|
144
|
-
eventRaw: any;
|
|
145
|
-
eventType: string;
|
|
146
|
-
payload: {
|
|
147
|
-
body?: Record<string, any>;
|
|
148
|
-
pathParameters?: Record<string, string>;
|
|
149
|
-
queryStringParameters?: Record<string, string>;
|
|
150
|
-
headers?: Record<string, string>;
|
|
151
|
-
};
|
|
152
|
-
params: Record<string, string>;
|
|
153
|
-
context: {
|
|
154
|
-
segment: RouteSegment;
|
|
155
|
-
identity?: IdentityContext;
|
|
156
|
-
requestId?: string;
|
|
157
|
-
tenantInfo?: TenantInfo;
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* JWT verification configuration for a Cognito User Pool
|
|
163
|
-
*/
|
|
164
|
-
export interface JwtVerificationPoolConfig {
|
|
165
|
-
/** Cognito User Pool ID (e.g., "us-east-1_ABC123") */
|
|
166
|
-
userPoolId: string;
|
|
167
|
-
/** App Client ID(s). Pass null to skip client ID verification. */
|
|
168
|
-
clientId?: string | string[] | null;
|
|
169
|
-
/** Expected token use. Pass null to accept either. Defaults to null. */
|
|
170
|
-
tokenUse?: 'id' | 'access' | null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Orchestrator configuration options
|
|
175
|
-
*/
|
|
176
|
-
export interface OrchestratorConfig {
|
|
177
|
-
/**
|
|
178
|
-
* Enable verbose logging for debugging
|
|
179
|
-
*/
|
|
180
|
-
debug?: boolean;
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* User Pool ID mappings for segment-based validation (issuer string only).
|
|
184
|
-
* @deprecated Use jwtVerification instead for cryptographic signature verification.
|
|
185
|
-
*/
|
|
186
|
-
userPools?: {
|
|
187
|
-
[K in RouteSegment]?: string;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Global middleware applied to all routes
|
|
192
|
-
*/
|
|
193
|
-
globalMiddleware?: MiddlewareFn[];
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Custom response handlers
|
|
197
|
-
*/
|
|
198
|
-
responses?: {
|
|
199
|
-
notFound?: () => any;
|
|
200
|
-
forbidden?: () => any;
|
|
201
|
-
badRequest?: (message?: string) => any;
|
|
202
|
-
internalError?: (message?: string) => any;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Automatically extract identity from Authorization header if no authorizer is present
|
|
207
|
-
*/
|
|
208
|
-
autoExtractIdentity?: boolean;
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* JWT signature verification configuration.
|
|
212
|
-
* When provided for a segment, JWTs from the Authorization header
|
|
213
|
-
* are cryptographically verified against the Cognito JWKS endpoint.
|
|
214
|
-
*/
|
|
215
|
-
jwtVerification?: {
|
|
216
|
-
[K in RouteSegment]?: JwtVerificationPoolConfig;
|
|
217
|
-
};
|
|
218
|
-
}
|
|
1
|
+
import { RouteSegment } from './event-type.enum.js';
|
|
2
|
+
import type { TenantInfo } from '../tenant/types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HTTP methods supported by the router
|
|
6
|
+
*/
|
|
7
|
+
export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Middleware function signature
|
|
11
|
+
* Returns the modified event or throws an error to halt execution
|
|
12
|
+
*/
|
|
13
|
+
export type MiddlewareFn = (event: NormalizedEvent) => Promise<NormalizedEvent | void>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Route configuration for a single endpoint
|
|
17
|
+
*/
|
|
18
|
+
export interface RouteConfig {
|
|
19
|
+
handler: (event: NormalizedEvent) => Promise<any>;
|
|
20
|
+
middleware?: MiddlewareFn[];
|
|
21
|
+
cors?: boolean | CorsConfig;
|
|
22
|
+
rateLimit?: RateLimitConfig;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CORS configuration options
|
|
27
|
+
*/
|
|
28
|
+
export interface CorsConfig {
|
|
29
|
+
origins: string[] | '*';
|
|
30
|
+
methods?: string[];
|
|
31
|
+
headers?: string[];
|
|
32
|
+
credentials?: boolean;
|
|
33
|
+
maxAge?: number;
|
|
34
|
+
exposedHeaders?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Rate limit configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface RateLimitConfig {
|
|
41
|
+
burstLimit: number;
|
|
42
|
+
rateLimit: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Standard HTTP router structure
|
|
47
|
+
* Maps HTTP methods to path-handler pairs
|
|
48
|
+
*/
|
|
49
|
+
export type HttpRouter = {
|
|
50
|
+
[K in HttpMethod]?: {
|
|
51
|
+
[path: string]: RouteConfig;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Segmented HTTP router for access control categorization
|
|
57
|
+
* Allows organizing routes by security context
|
|
58
|
+
*/
|
|
59
|
+
export interface SegmentedHttpRouter {
|
|
60
|
+
public?: HttpRouter;
|
|
61
|
+
private?: HttpRouter;
|
|
62
|
+
backoffice?: HttpRouter;
|
|
63
|
+
internal?: HttpRouter;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Segment configuration with optional middleware
|
|
68
|
+
*/
|
|
69
|
+
export interface SegmentConfig {
|
|
70
|
+
routes: HttpRouter;
|
|
71
|
+
middleware?: MiddlewareFn[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Advanced segmented router with per-segment middleware
|
|
76
|
+
*/
|
|
77
|
+
export interface AdvancedSegmentedRouter {
|
|
78
|
+
public?: SegmentConfig | HttpRouter;
|
|
79
|
+
private?: SegmentConfig | HttpRouter;
|
|
80
|
+
backoffice?: SegmentConfig | HttpRouter;
|
|
81
|
+
internal?: SegmentConfig | HttpRouter;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* EventBridge routes configuration
|
|
86
|
+
* Maps operation names to handlers
|
|
87
|
+
*/
|
|
88
|
+
export type EventBridgeRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Lambda invocation routes
|
|
92
|
+
*/
|
|
93
|
+
export type LambdaRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* SQS queue routes
|
|
97
|
+
* Maps queue names to handlers
|
|
98
|
+
*/
|
|
99
|
+
export type SqsRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Scheduled event routes (EventBridge Scheduler / CloudWatch Events rules)
|
|
103
|
+
* Maps rule names to handlers. Use 'default' as fallback.
|
|
104
|
+
*/
|
|
105
|
+
export type ScheduledRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Complete dispatch routes configuration
|
|
109
|
+
*/
|
|
110
|
+
export interface DispatchRoutes {
|
|
111
|
+
apigateway?: HttpRouter | SegmentedHttpRouter | AdvancedSegmentedRouter;
|
|
112
|
+
eventbridge?: EventBridgeRoutes;
|
|
113
|
+
lambda?: LambdaRoutes;
|
|
114
|
+
sqs?: SqsRoutes;
|
|
115
|
+
scheduled?: ScheduledRoutes;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Identity context extracted from the event
|
|
120
|
+
*/
|
|
121
|
+
export interface IdentityContext {
|
|
122
|
+
userId?: string;
|
|
123
|
+
email?: string;
|
|
124
|
+
groups?: string[];
|
|
125
|
+
issuer?: string;
|
|
126
|
+
claims?: Record<string, any>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Route match result with extracted parameters
|
|
131
|
+
*/
|
|
132
|
+
export interface RouteMatch {
|
|
133
|
+
handler: (event: NormalizedEvent) => Promise<any>;
|
|
134
|
+
params: Record<string, string>;
|
|
135
|
+
segment: RouteSegment;
|
|
136
|
+
middleware?: MiddlewareFn[];
|
|
137
|
+
config: RouteConfig;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Normalized event structure passed to handlers
|
|
142
|
+
*/
|
|
143
|
+
export interface NormalizedEvent {
|
|
144
|
+
eventRaw: any;
|
|
145
|
+
eventType: string;
|
|
146
|
+
payload: {
|
|
147
|
+
body?: Record<string, any>;
|
|
148
|
+
pathParameters?: Record<string, string>;
|
|
149
|
+
queryStringParameters?: Record<string, string>;
|
|
150
|
+
headers?: Record<string, string>;
|
|
151
|
+
};
|
|
152
|
+
params: Record<string, string>;
|
|
153
|
+
context: {
|
|
154
|
+
segment: RouteSegment;
|
|
155
|
+
identity?: IdentityContext;
|
|
156
|
+
requestId?: string;
|
|
157
|
+
tenantInfo?: TenantInfo;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* JWT verification configuration for a Cognito User Pool
|
|
163
|
+
*/
|
|
164
|
+
export interface JwtVerificationPoolConfig {
|
|
165
|
+
/** Cognito User Pool ID (e.g., "us-east-1_ABC123") */
|
|
166
|
+
userPoolId: string;
|
|
167
|
+
/** App Client ID(s). Pass null to skip client ID verification. */
|
|
168
|
+
clientId?: string | string[] | null;
|
|
169
|
+
/** Expected token use. Pass null to accept either. Defaults to null. */
|
|
170
|
+
tokenUse?: 'id' | 'access' | null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Orchestrator configuration options
|
|
175
|
+
*/
|
|
176
|
+
export interface OrchestratorConfig {
|
|
177
|
+
/**
|
|
178
|
+
* Enable verbose logging for debugging
|
|
179
|
+
*/
|
|
180
|
+
debug?: boolean;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* User Pool ID mappings for segment-based validation (issuer string only).
|
|
184
|
+
* @deprecated Use jwtVerification instead for cryptographic signature verification.
|
|
185
|
+
*/
|
|
186
|
+
userPools?: {
|
|
187
|
+
[K in RouteSegment]?: string;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Global middleware applied to all routes
|
|
192
|
+
*/
|
|
193
|
+
globalMiddleware?: MiddlewareFn[];
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Custom response handlers
|
|
197
|
+
*/
|
|
198
|
+
responses?: {
|
|
199
|
+
notFound?: () => any;
|
|
200
|
+
forbidden?: () => any;
|
|
201
|
+
badRequest?: (message?: string) => any;
|
|
202
|
+
internalError?: (message?: string) => any;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Automatically extract identity from Authorization header if no authorizer is present
|
|
207
|
+
*/
|
|
208
|
+
autoExtractIdentity?: boolean;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* JWT signature verification configuration.
|
|
212
|
+
* When provided for a segment, JWTs from the Authorization header
|
|
213
|
+
* are cryptographically verified against the Cognito JWKS endpoint.
|
|
214
|
+
*/
|
|
215
|
+
jwtVerification?: {
|
|
216
|
+
[K in RouteSegment]?: JwtVerificationPoolConfig;
|
|
217
|
+
};
|
|
218
|
+
}
|
package/src/utils/headers.ts
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Header normalization utilities
|
|
3
|
-
* HTTP headers are case-insensitive, this ensures consistent access
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Normalizes headers to lowercase keys for consistent access
|
|
8
|
-
* @param headers - Original headers object
|
|
9
|
-
* @returns Headers with lowercase keys
|
|
10
|
-
*/
|
|
11
|
-
export function normalizeHeaders(headers: Record<string, string> | undefined): Record<string, string> {
|
|
12
|
-
if (!headers) return {};
|
|
13
|
-
|
|
14
|
-
const normalized: Record<string, string> = {};
|
|
15
|
-
|
|
16
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
17
|
-
normalized[key.toLowerCase()] = value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return normalized;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Gets a header value case-insensitively
|
|
25
|
-
* @param headers - Headers object
|
|
26
|
-
* @param name - Header name to find
|
|
27
|
-
* @returns Header value or undefined
|
|
28
|
-
*/
|
|
29
|
-
export function getHeader(headers: Record<string, string> | undefined, name: string): string | undefined {
|
|
30
|
-
if (!headers) return undefined;
|
|
31
|
-
|
|
32
|
-
const normalizedName = name.toLowerCase();
|
|
33
|
-
|
|
34
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
35
|
-
if (key.toLowerCase() === normalizedName) {
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Standard CORS headers for preflight responses
|
|
45
|
-
*/
|
|
46
|
-
export function getCorsHeaders(config?: {
|
|
47
|
-
origins?: string[] | '*';
|
|
48
|
-
methods?: string[];
|
|
49
|
-
headers?: string[];
|
|
50
|
-
credentials?: boolean;
|
|
51
|
-
maxAge?: number;
|
|
52
|
-
}): Record<string, string> {
|
|
53
|
-
const origin = config?.origins === '*' ? '*' : (config?.origins?.join(', ') || '*');
|
|
54
|
-
const methods = config?.methods?.join(', ') || 'GET,POST,PUT,DELETE,PATCH,OPTIONS';
|
|
55
|
-
const headers = config?.headers?.join(', ') || 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token';
|
|
56
|
-
|
|
57
|
-
const corsHeaders: Record<string, string> = {
|
|
58
|
-
'Access-Control-Allow-Origin': origin,
|
|
59
|
-
'Access-Control-Allow-Methods': methods,
|
|
60
|
-
'Access-Control-Allow-Headers': headers,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
if (config?.credentials) {
|
|
64
|
-
corsHeaders['Access-Control-Allow-Credentials'] = 'true';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (config?.maxAge) {
|
|
68
|
-
corsHeaders['Access-Control-Max-Age'] = String(config.maxAge);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return corsHeaders;
|
|
72
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Header normalization utilities
|
|
3
|
+
* HTTP headers are case-insensitive, this ensures consistent access
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes headers to lowercase keys for consistent access
|
|
8
|
+
* @param headers - Original headers object
|
|
9
|
+
* @returns Headers with lowercase keys
|
|
10
|
+
*/
|
|
11
|
+
export function normalizeHeaders(headers: Record<string, string> | undefined): Record<string, string> {
|
|
12
|
+
if (!headers) return {};
|
|
13
|
+
|
|
14
|
+
const normalized: Record<string, string> = {};
|
|
15
|
+
|
|
16
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
17
|
+
normalized[key.toLowerCase()] = value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets a header value case-insensitively
|
|
25
|
+
* @param headers - Headers object
|
|
26
|
+
* @param name - Header name to find
|
|
27
|
+
* @returns Header value or undefined
|
|
28
|
+
*/
|
|
29
|
+
export function getHeader(headers: Record<string, string> | undefined, name: string): string | undefined {
|
|
30
|
+
if (!headers) return undefined;
|
|
31
|
+
|
|
32
|
+
const normalizedName = name.toLowerCase();
|
|
33
|
+
|
|
34
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
35
|
+
if (key.toLowerCase() === normalizedName) {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Standard CORS headers for preflight responses
|
|
45
|
+
*/
|
|
46
|
+
export function getCorsHeaders(config?: {
|
|
47
|
+
origins?: string[] | '*';
|
|
48
|
+
methods?: string[];
|
|
49
|
+
headers?: string[];
|
|
50
|
+
credentials?: boolean;
|
|
51
|
+
maxAge?: number;
|
|
52
|
+
}): Record<string, string> {
|
|
53
|
+
const origin = config?.origins === '*' ? '*' : (config?.origins?.join(', ') || '*');
|
|
54
|
+
const methods = config?.methods?.join(', ') || 'GET,POST,PUT,DELETE,PATCH,OPTIONS';
|
|
55
|
+
const headers = config?.headers?.join(', ') || 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token';
|
|
56
|
+
|
|
57
|
+
const corsHeaders: Record<string, string> = {
|
|
58
|
+
'Access-Control-Allow-Origin': origin,
|
|
59
|
+
'Access-Control-Allow-Methods': methods,
|
|
60
|
+
'Access-Control-Allow-Headers': headers,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (config?.credentials) {
|
|
64
|
+
corsHeaders['Access-Control-Allow-Credentials'] = 'true';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (config?.maxAge) {
|
|
68
|
+
corsHeaders['Access-Control-Max-Age'] = String(config.maxAge);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return corsHeaders;
|
|
72
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './path-matcher.js';
|
|
2
|
-
export * from './headers.js';
|
|
1
|
+
export * from './path-matcher.js';
|
|
2
|
+
export * from './headers.js';
|