serverless-event-orchestrator 1.0.1

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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/dispatcher.d.ts +18 -0
  4. package/dist/dispatcher.d.ts.map +1 -0
  5. package/dist/dispatcher.js +345 -0
  6. package/dist/dispatcher.js.map +1 -0
  7. package/dist/http/body-parser.d.ts +27 -0
  8. package/dist/http/body-parser.d.ts.map +1 -0
  9. package/dist/http/body-parser.js +56 -0
  10. package/dist/http/body-parser.js.map +1 -0
  11. package/dist/http/cors.d.ts +32 -0
  12. package/dist/http/cors.d.ts.map +1 -0
  13. package/dist/http/cors.js +69 -0
  14. package/dist/http/cors.js.map +1 -0
  15. package/dist/http/index.d.ts +4 -0
  16. package/dist/http/index.d.ts.map +1 -0
  17. package/dist/http/index.js +20 -0
  18. package/dist/http/index.js.map +1 -0
  19. package/dist/http/response.d.ts +104 -0
  20. package/dist/http/response.d.ts.map +1 -0
  21. package/dist/http/response.js +164 -0
  22. package/dist/http/response.js.map +1 -0
  23. package/dist/identity/extractor.d.ts +39 -0
  24. package/dist/identity/extractor.d.ts.map +1 -0
  25. package/dist/identity/extractor.js +88 -0
  26. package/dist/identity/extractor.js.map +1 -0
  27. package/dist/identity/index.d.ts +2 -0
  28. package/dist/identity/index.d.ts.map +1 -0
  29. package/dist/identity/index.js +18 -0
  30. package/dist/identity/index.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +62 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/types/event-type.enum.d.ts +20 -0
  36. package/dist/types/event-type.enum.d.ts.map +1 -0
  37. package/dist/types/event-type.enum.js +25 -0
  38. package/dist/types/event-type.enum.js.map +1 -0
  39. package/dist/types/index.d.ts +3 -0
  40. package/dist/types/index.d.ts.map +1 -0
  41. package/dist/types/index.js +19 -0
  42. package/dist/types/index.js.map +1 -0
  43. package/dist/types/routes.d.ts +163 -0
  44. package/dist/types/routes.d.ts.map +1 -0
  45. package/dist/types/routes.js +3 -0
  46. package/dist/types/routes.js.map +1 -0
  47. package/dist/utils/headers.d.ts +28 -0
  48. package/dist/utils/headers.d.ts.map +1 -0
  49. package/dist/utils/headers.js +61 -0
  50. package/dist/utils/headers.js.map +1 -0
  51. package/dist/utils/index.d.ts +3 -0
  52. package/dist/utils/index.d.ts.map +1 -0
  53. package/dist/utils/index.js +19 -0
  54. package/dist/utils/index.js.map +1 -0
  55. package/dist/utils/path-matcher.d.ts +33 -0
  56. package/dist/utils/path-matcher.d.ts.map +1 -0
  57. package/dist/utils/path-matcher.js +74 -0
  58. package/dist/utils/path-matcher.js.map +1 -0
  59. package/jest.config.js +32 -0
  60. package/package.json +68 -0
  61. package/src/dispatcher.ts +415 -0
  62. package/src/http/body-parser.ts +60 -0
  63. package/src/http/cors.ts +76 -0
  64. package/src/http/index.ts +3 -0
  65. package/src/http/response.ts +194 -0
  66. package/src/identity/extractor.ts +89 -0
  67. package/src/identity/index.ts +1 -0
  68. package/src/index.ts +92 -0
  69. package/src/types/event-type.enum.ts +20 -0
  70. package/src/types/index.ts +2 -0
  71. package/src/types/routes.ts +182 -0
  72. package/src/utils/headers.ts +72 -0
  73. package/src/utils/index.ts +2 -0
  74. package/src/utils/path-matcher.ts +79 -0
  75. package/tests/cors.test.ts +133 -0
  76. package/tests/dispatcher.test.ts +425 -0
  77. package/tests/headers.test.ts +99 -0
  78. package/tests/identity.test.ts +171 -0
  79. package/tests/path-matcher.test.ts +102 -0
  80. package/tests/response.test.ts +155 -0
  81. package/tsconfig.json +24 -0
@@ -0,0 +1,163 @@
1
+ import { RouteSegment } from './event-type.enum.js';
2
+ /**
3
+ * HTTP methods supported by the router
4
+ */
5
+ export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
6
+ /**
7
+ * Middleware function signature
8
+ * Returns the modified event or throws an error to halt execution
9
+ */
10
+ export type MiddlewareFn = (event: NormalizedEvent) => Promise<NormalizedEvent | void>;
11
+ /**
12
+ * Route configuration for a single endpoint
13
+ */
14
+ export interface RouteConfig {
15
+ handler: (event: NormalizedEvent) => Promise<any>;
16
+ middleware?: MiddlewareFn[];
17
+ cors?: boolean | CorsConfig;
18
+ rateLimit?: RateLimitConfig;
19
+ }
20
+ /**
21
+ * CORS configuration options
22
+ */
23
+ export interface CorsConfig {
24
+ origins: string[] | '*';
25
+ methods?: string[];
26
+ headers?: string[];
27
+ credentials?: boolean;
28
+ maxAge?: number;
29
+ exposedHeaders?: string[];
30
+ }
31
+ /**
32
+ * Rate limit configuration
33
+ */
34
+ export interface RateLimitConfig {
35
+ burstLimit: number;
36
+ rateLimit: number;
37
+ }
38
+ /**
39
+ * Standard HTTP router structure
40
+ * Maps HTTP methods to path-handler pairs
41
+ */
42
+ export type HttpRouter = {
43
+ [K in HttpMethod]?: {
44
+ [path: string]: RouteConfig;
45
+ };
46
+ };
47
+ /**
48
+ * Segmented HTTP router for access control categorization
49
+ * Allows organizing routes by security context
50
+ */
51
+ export interface SegmentedHttpRouter {
52
+ public?: HttpRouter;
53
+ private?: HttpRouter;
54
+ backoffice?: HttpRouter;
55
+ internal?: HttpRouter;
56
+ }
57
+ /**
58
+ * Segment configuration with optional middleware
59
+ */
60
+ export interface SegmentConfig {
61
+ routes: HttpRouter;
62
+ middleware?: MiddlewareFn[];
63
+ }
64
+ /**
65
+ * Advanced segmented router with per-segment middleware
66
+ */
67
+ export interface AdvancedSegmentedRouter {
68
+ public?: SegmentConfig | HttpRouter;
69
+ private?: SegmentConfig | HttpRouter;
70
+ backoffice?: SegmentConfig | HttpRouter;
71
+ internal?: SegmentConfig | HttpRouter;
72
+ }
73
+ /**
74
+ * EventBridge routes configuration
75
+ * Maps operation names to handlers
76
+ */
77
+ export type EventBridgeRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
78
+ /**
79
+ * Lambda invocation routes
80
+ */
81
+ export type LambdaRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
82
+ /**
83
+ * SQS queue routes
84
+ * Maps queue names to handlers
85
+ */
86
+ export type SqsRoutes = Record<string, (event: NormalizedEvent) => Promise<any>>;
87
+ /**
88
+ * Complete dispatch routes configuration
89
+ */
90
+ export interface DispatchRoutes {
91
+ apigateway?: HttpRouter | SegmentedHttpRouter | AdvancedSegmentedRouter;
92
+ eventbridge?: EventBridgeRoutes;
93
+ lambda?: LambdaRoutes;
94
+ sqs?: SqsRoutes;
95
+ }
96
+ /**
97
+ * Identity context extracted from the event
98
+ */
99
+ export interface IdentityContext {
100
+ userId?: string;
101
+ email?: string;
102
+ groups?: string[];
103
+ issuer?: string;
104
+ claims?: Record<string, any>;
105
+ }
106
+ /**
107
+ * Route match result with extracted parameters
108
+ */
109
+ export interface RouteMatch {
110
+ handler: (event: NormalizedEvent) => Promise<any>;
111
+ params: Record<string, string>;
112
+ segment: RouteSegment;
113
+ middleware?: MiddlewareFn[];
114
+ config: RouteConfig;
115
+ }
116
+ /**
117
+ * Normalized event structure passed to handlers
118
+ */
119
+ export interface NormalizedEvent {
120
+ eventRaw: any;
121
+ eventType: string;
122
+ payload: {
123
+ body?: Record<string, any>;
124
+ pathParameters?: Record<string, string>;
125
+ queryStringParameters?: Record<string, string>;
126
+ headers?: Record<string, string>;
127
+ };
128
+ params: Record<string, string>;
129
+ context: {
130
+ segment: RouteSegment;
131
+ identity?: IdentityContext;
132
+ requestId?: string;
133
+ };
134
+ }
135
+ /**
136
+ * Orchestrator configuration options
137
+ */
138
+ export interface OrchestratorConfig {
139
+ /**
140
+ * Enable verbose logging for debugging
141
+ */
142
+ debug?: boolean;
143
+ /**
144
+ * User Pool ID mappings for segment-based validation
145
+ */
146
+ userPools?: {
147
+ [K in RouteSegment]?: string;
148
+ };
149
+ /**
150
+ * Global middleware applied to all routes
151
+ */
152
+ globalMiddleware?: MiddlewareFn[];
153
+ /**
154
+ * Custom response handlers
155
+ */
156
+ responses?: {
157
+ notFound?: () => any;
158
+ forbidden?: () => any;
159
+ badRequest?: (message?: string) => any;
160
+ internalError?: (message?: string) => any;
161
+ };
162
+ }
163
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/types/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/types/routes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Header normalization utilities
3
+ * HTTP headers are case-insensitive, this ensures consistent access
4
+ */
5
+ /**
6
+ * Normalizes headers to lowercase keys for consistent access
7
+ * @param headers - Original headers object
8
+ * @returns Headers with lowercase keys
9
+ */
10
+ export declare function normalizeHeaders(headers: Record<string, string> | undefined): Record<string, string>;
11
+ /**
12
+ * Gets a header value case-insensitively
13
+ * @param headers - Headers object
14
+ * @param name - Header name to find
15
+ * @returns Header value or undefined
16
+ */
17
+ export declare function getHeader(headers: Record<string, string> | undefined, name: string): string | undefined;
18
+ /**
19
+ * Standard CORS headers for preflight responses
20
+ */
21
+ export declare function getCorsHeaders(config?: {
22
+ origins?: string[] | '*';
23
+ methods?: string[];
24
+ headers?: string[];
25
+ credentials?: boolean;
26
+ maxAge?: number;
27
+ }): Record<string, string>;
28
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/utils/headers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUpG;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYvG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE;IACtC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoBzB"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * Header normalization utilities
4
+ * HTTP headers are case-insensitive, this ensures consistent access
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.normalizeHeaders = normalizeHeaders;
8
+ exports.getHeader = getHeader;
9
+ exports.getCorsHeaders = getCorsHeaders;
10
+ /**
11
+ * Normalizes headers to lowercase keys for consistent access
12
+ * @param headers - Original headers object
13
+ * @returns Headers with lowercase keys
14
+ */
15
+ function normalizeHeaders(headers) {
16
+ if (!headers)
17
+ return {};
18
+ const normalized = {};
19
+ for (const [key, value] of Object.entries(headers)) {
20
+ normalized[key.toLowerCase()] = value;
21
+ }
22
+ return normalized;
23
+ }
24
+ /**
25
+ * Gets a header value case-insensitively
26
+ * @param headers - Headers object
27
+ * @param name - Header name to find
28
+ * @returns Header value or undefined
29
+ */
30
+ function getHeader(headers, name) {
31
+ if (!headers)
32
+ return undefined;
33
+ const normalizedName = name.toLowerCase();
34
+ for (const [key, value] of Object.entries(headers)) {
35
+ if (key.toLowerCase() === normalizedName) {
36
+ return value;
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+ /**
42
+ * Standard CORS headers for preflight responses
43
+ */
44
+ function getCorsHeaders(config) {
45
+ const origin = config?.origins === '*' ? '*' : (config?.origins?.join(', ') || '*');
46
+ const methods = config?.methods?.join(', ') || 'GET,POST,PUT,DELETE,PATCH,OPTIONS';
47
+ const headers = config?.headers?.join(', ') || 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token';
48
+ const corsHeaders = {
49
+ 'Access-Control-Allow-Origin': origin,
50
+ 'Access-Control-Allow-Methods': methods,
51
+ 'Access-Control-Allow-Headers': headers,
52
+ };
53
+ if (config?.credentials) {
54
+ corsHeaders['Access-Control-Allow-Credentials'] = 'true';
55
+ }
56
+ if (config?.maxAge) {
57
+ corsHeaders['Access-Control-Max-Age'] = String(config.maxAge);
58
+ }
59
+ return corsHeaders;
60
+ }
61
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/utils/headers.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAOH,4CAUC;AAQD,8BAYC;AAKD,wCA0BC;AAlED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,OAA2C;IAC1E,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,MAAM,UAAU,GAA2B,EAAE,CAAC;IAE9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IACxC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,OAA2C,EAAE,IAAY;IACjF,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,cAAc,EAAE,CAAC;YACzC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,MAM9B;IACC,MAAM,MAAM,GAAG,MAAM,EAAE,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,mCAAmC,CAAC;IACnF,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,sEAAsE,CAAC;IAEtH,MAAM,WAAW,GAA2B;QAC1C,6BAA6B,EAAE,MAAM;QACrC,8BAA8B,EAAE,OAAO;QACvC,8BAA8B,EAAE,OAAO;KACxC,CAAC;IAEF,IAAI,MAAM,EAAE,WAAW,EAAE,CAAC;QACxB,WAAW,CAAC,kCAAkC,CAAC,GAAG,MAAM,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;QACnB,WAAW,CAAC,wBAAwB,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './path-matcher.js';
2
+ export * from './headers.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./path-matcher.js"), exports);
18
+ __exportStar(require("./headers.js"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,+CAA6B"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Path matching utilities for extracting path parameters
3
+ * Supports patterns like /users/{id} and /users/{userId}/posts/{postId}
4
+ */
5
+ /**
6
+ * Converts a route pattern to a regex and extracts parameter names
7
+ * @param pattern - Route pattern like /users/{id}
8
+ * @returns Object with regex and parameter names
9
+ */
10
+ export declare function patternToRegex(pattern: string): {
11
+ regex: RegExp;
12
+ paramNames: string[];
13
+ };
14
+ /**
15
+ * Matches a path against a pattern and extracts parameters
16
+ * @param pattern - Route pattern like /users/{id}
17
+ * @param path - Actual path like /users/123
18
+ * @returns Extracted parameters or null if no match
19
+ */
20
+ export declare function matchPath(pattern: string, path: string): Record<string, string> | null;
21
+ /**
22
+ * Checks if a pattern contains path parameters
23
+ * @param pattern - Route pattern to check
24
+ * @returns True if pattern has parameters
25
+ */
26
+ export declare function hasPathParameters(pattern: string): boolean;
27
+ /**
28
+ * Normalizes a path by removing trailing slashes and ensuring leading slash
29
+ * @param path - Path to normalize
30
+ * @returns Normalized path
31
+ */
32
+ export declare function normalizePath(path: string): string;
33
+ //# sourceMappingURL=path-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-matcher.d.ts","sourceRoot":"","sources":["../../src/utils/path-matcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBvF;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CActF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYlD"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Path matching utilities for extracting path parameters
4
+ * Supports patterns like /users/{id} and /users/{userId}/posts/{postId}
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.patternToRegex = patternToRegex;
8
+ exports.matchPath = matchPath;
9
+ exports.hasPathParameters = hasPathParameters;
10
+ exports.normalizePath = normalizePath;
11
+ /**
12
+ * Converts a route pattern to a regex and extracts parameter names
13
+ * @param pattern - Route pattern like /users/{id}
14
+ * @returns Object with regex and parameter names
15
+ */
16
+ function patternToRegex(pattern) {
17
+ const paramNames = [];
18
+ // Escape special regex characters except for our parameter syntax
19
+ let regexPattern = pattern
20
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
21
+ .replace(/\\\{(\w+)\\\}/g, (_, paramName) => {
22
+ paramNames.push(paramName);
23
+ return '([^/]+)';
24
+ });
25
+ // Ensure exact match
26
+ regexPattern = `^${regexPattern}$`;
27
+ return {
28
+ regex: new RegExp(regexPattern),
29
+ paramNames,
30
+ };
31
+ }
32
+ /**
33
+ * Matches a path against a pattern and extracts parameters
34
+ * @param pattern - Route pattern like /users/{id}
35
+ * @param path - Actual path like /users/123
36
+ * @returns Extracted parameters or null if no match
37
+ */
38
+ function matchPath(pattern, path) {
39
+ const { regex, paramNames } = patternToRegex(pattern);
40
+ const match = path.match(regex);
41
+ if (!match) {
42
+ return null;
43
+ }
44
+ const params = {};
45
+ paramNames.forEach((name, index) => {
46
+ params[name] = match[index + 1];
47
+ });
48
+ return params;
49
+ }
50
+ /**
51
+ * Checks if a pattern contains path parameters
52
+ * @param pattern - Route pattern to check
53
+ * @returns True if pattern has parameters
54
+ */
55
+ function hasPathParameters(pattern) {
56
+ return /\{[\w]+\}/.test(pattern);
57
+ }
58
+ /**
59
+ * Normalizes a path by removing trailing slashes and ensuring leading slash
60
+ * @param path - Path to normalize
61
+ * @returns Normalized path
62
+ */
63
+ function normalizePath(path) {
64
+ if (!path)
65
+ return '/';
66
+ // Ensure leading slash
67
+ let normalized = path.startsWith('/') ? path : `/${path}`;
68
+ // Remove trailing slash (except for root)
69
+ if (normalized.length > 1 && normalized.endsWith('/')) {
70
+ normalized = normalized.slice(0, -1);
71
+ }
72
+ return normalized;
73
+ }
74
+ //# sourceMappingURL=path-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-matcher.js","sourceRoot":"","sources":["../../src/utils/path-matcher.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAOH,wCAkBC;AAQD,8BAcC;AAOD,8CAEC;AAOD,sCAYC;AAzED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAe;IAC5C,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,kEAAkE;IAClE,IAAI,YAAY,GAAG,OAAO;SACvB,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC;SACrC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE;QAC1C,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IAEL,qBAAqB;IACrB,YAAY,GAAG,IAAI,YAAY,GAAG,CAAC;IAEnC,OAAO;QACL,KAAK,EAAE,IAAI,MAAM,CAAC,YAAY,CAAC;QAC/B,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,OAAe,EAAE,IAAY;IACrD,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACjC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAgB,iBAAiB,CAAC,OAAe;IAC/C,OAAO,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IAEtB,uBAAuB;IACvB,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAE1D,0CAA0C;IAC1C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
package/jest.config.js ADDED
@@ -0,0 +1,32 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/src', '<rootDir>/tests'],
6
+ testMatch: ['**/*.test.ts'],
7
+ moduleNameMapper: {
8
+ '^(\\.{1,2}/.*)\\.js$': '$1'
9
+ },
10
+ transform: {
11
+ '^.+\\.tsx?$': ['ts-jest', {
12
+ useESM: false,
13
+ tsconfig: {
14
+ module: 'commonjs',
15
+ moduleResolution: 'node'
16
+ }
17
+ }]
18
+ },
19
+ collectCoverageFrom: [
20
+ 'src/**/*.ts',
21
+ '!src/**/*.d.ts',
22
+ '!src/**/index.ts'
23
+ ],
24
+ coverageThreshold: {
25
+ global: {
26
+ branches: 70,
27
+ functions: 90,
28
+ lines: 80,
29
+ statements: 80
30
+ }
31
+ }
32
+ };
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "serverless-event-orchestrator",
3
+ "version": "1.0.1",
4
+ "description": "A lightweight, type-safe event dispatcher and middleware orchestrator for AWS Lambda. Designed for hexagonal architectures with support for segmented routing (public, private, backoffice), Cognito User Pool validation, and built-in infrastructure middlewares.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./http": {
14
+ "import": "./dist/http/index.js",
15
+ "require": "./dist/http/index.js",
16
+ "types": "./dist/http/index.d.ts"
17
+ },
18
+ "./identity": {
19
+ "import": "./dist/identity/index.js",
20
+ "require": "./dist/identity/index.js",
21
+ "types": "./dist/identity/index.d.ts"
22
+ }
23
+ },
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "test": "jest --passWithNoTests",
27
+ "test:watch": "jest --watchAll",
28
+ "test:coverage": "jest --coverage",
29
+ "clean": "rm -rf dist",
30
+ "prepublishOnly": "npm run clean && npm run build && npm test"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/carlosmora01/serverless-event-orchestrator.git"
35
+ },
36
+ "keywords": [
37
+ "aws",
38
+ "lambda",
39
+ "serverless",
40
+ "eventbridge",
41
+ "apigateway",
42
+ "sqs",
43
+ "router",
44
+ "dispatcher",
45
+ "cognito",
46
+ "middleware",
47
+ "hexagonal-architecture"
48
+ ],
49
+ "author": "Carlos Mora",
50
+ "license": "MIT",
51
+ "bugs": {
52
+ "url": "https://github.com/carlosmora01/serverless-event-orchestrator/issues"
53
+ },
54
+ "homepage": "https://github.com/carlosmora01/serverless-event-orchestrator#readme",
55
+ "devDependencies": {
56
+ "@types/jest": "^29.5.0",
57
+ "@types/node": "^20.0.0",
58
+ "jest": "^29.7.0",
59
+ "ts-jest": "^29.1.0",
60
+ "typescript": "^5.3.0"
61
+ },
62
+ "peerDependencies": {
63
+ "@types/aws-lambda": "^8.10.0"
64
+ },
65
+ "engines": {
66
+ "node": ">=18.0.0"
67
+ }
68
+ }