@venturekit/runtime 0.0.0-dev.20260312012510 → 0.0.0-dev.20260329093916
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 +1 -1
- package/dist/config/env.d.ts +62 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +98 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/secrets.d.ts +67 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +132 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/handler/adapter.d.ts.map +1 -0
- package/dist/{adapter.js → handler/adapter.js} +5 -44
- package/dist/handler/adapter.js.map +1 -0
- package/dist/{context.d.ts → handler/context.d.ts} +44 -3
- package/dist/handler/context.d.ts.map +1 -0
- package/dist/{context.js → handler/context.js} +9 -9
- package/dist/handler/context.js.map +1 -0
- package/dist/handler/errors.d.ts.map +1 -0
- package/dist/{errors.js → handler/errors.js} +11 -25
- package/dist/handler/errors.js.map +1 -0
- package/dist/{handler.d.ts → handler/handler.d.ts} +36 -3
- package/dist/handler/handler.d.ts.map +1 -0
- package/dist/{handler.js → handler/handler.js} +59 -23
- package/dist/handler/handler.js.map +1 -0
- package/dist/handler/response.d.ts.map +1 -0
- package/dist/{response.js → handler/response.js} +9 -17
- package/dist/handler/response.js.map +1 -0
- package/dist/handler/task-handler.d.ts +192 -0
- package/dist/handler/task-handler.d.ts.map +1 -0
- package/dist/handler/task-handler.js +262 -0
- package/dist/handler/task-handler.js.map +1 -0
- package/dist/index.d.ts +33 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -48
- package/dist/index.js.map +1 -1
- package/dist/{logger.d.ts → logging/logger.d.ts} +1 -1
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/{logger.js → logging/logger.js} +8 -16
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/tracing.d.ts +51 -0
- package/dist/logging/tracing.d.ts.map +1 -0
- package/dist/logging/tracing.js +77 -0
- package/dist/logging/tracing.js.map +1 -0
- package/dist/middleware/middleware.d.ts +120 -0
- package/dist/middleware/middleware.d.ts.map +1 -0
- package/dist/middleware/middleware.js +270 -0
- package/dist/middleware/middleware.js.map +1 -0
- package/dist/middleware/rate-limit-store.d.ts +153 -0
- package/dist/middleware/rate-limit-store.d.ts.map +1 -0
- package/dist/middleware/rate-limit-store.js +182 -0
- package/dist/middleware/rate-limit-store.js.map +1 -0
- package/dist/openapi/openapi.d.ts +147 -0
- package/dist/openapi/openapi.d.ts.map +1 -0
- package/dist/openapi/openapi.js +235 -0
- package/dist/openapi/openapi.js.map +1 -0
- package/dist/openapi.d.ts +10 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +9 -0
- package/dist/openapi.js.map +1 -0
- package/dist/patterns/circuit-breaker.d.ts +78 -0
- package/dist/patterns/circuit-breaker.d.ts.map +1 -0
- package/dist/patterns/circuit-breaker.js +113 -0
- package/dist/patterns/circuit-breaker.js.map +1 -0
- package/dist/patterns/idempotency.d.ts +175 -0
- package/dist/patterns/idempotency.d.ts.map +1 -0
- package/dist/patterns/idempotency.js +305 -0
- package/dist/patterns/idempotency.js.map +1 -0
- package/dist/patterns/invoke.d.ts +279 -0
- package/dist/patterns/invoke.d.ts.map +1 -0
- package/dist/patterns/invoke.js +581 -0
- package/dist/patterns/invoke.js.map +1 -0
- package/dist/patterns/saga.d.ts +391 -0
- package/dist/patterns/saga.d.ts.map +1 -0
- package/dist/patterns/saga.js +556 -0
- package/dist/patterns/saga.js.map +1 -0
- package/dist/patterns.d.ts +17 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +17 -0
- package/dist/patterns.js.map +1 -0
- package/dist/rate-limit.d.ts +16 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +15 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/secrets.d.ts +11 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +10 -0
- package/dist/secrets.js.map +1 -0
- package/dist/testing/test-utils.d.ts +99 -0
- package/dist/testing/test-utils.d.ts.map +1 -0
- package/dist/testing/test-utils.js +153 -0
- package/dist/testing/test-utils.js.map +1 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +9 -0
- package/dist/testing.js.map +1 -0
- package/dist/ws/ws.d.ts +139 -0
- package/dist/ws/ws.d.ts.map +1 -0
- package/dist/ws/ws.js +294 -0
- package/dist/ws/ws.js.map +1 -0
- package/dist/ws.d.ts +5 -133
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +4 -291
- package/dist/ws.js.map +1 -1
- package/package.json +47 -5
- package/dist/adapter.d.ts.map +0 -1
- package/dist/adapter.js.map +0 -1
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/handler.d.ts.map +0 -1
- package/dist/handler.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/middleware.d.ts +0 -47
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js +0 -147
- package/dist/middleware.js.map +0 -1
- package/dist/response.d.ts.map +0 -1
- package/dist/response.js.map +0 -1
- /package/dist/{adapter.d.ts → handler/adapter.d.ts} +0 -0
- /package/dist/{errors.d.ts → handler/errors.d.ts} +0 -0
- /package/dist/{response.d.ts → handler/response.d.ts} +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Distributed Tracing
|
|
3
|
+
*
|
|
4
|
+
* Correlation ID / trace ID propagation across Lambda invocations.
|
|
5
|
+
* Integrates with X-Ray trace IDs and custom trace context.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Trace headers used for propagation
|
|
9
|
+
*/
|
|
10
|
+
export const TRACE_HEADERS = {
|
|
11
|
+
TRACE_ID: 'x-trace-id',
|
|
12
|
+
SPAN_ID: 'x-span-id',
|
|
13
|
+
PARENT_SPAN_ID: 'x-parent-span-id',
|
|
14
|
+
BAGGAGE: 'x-trace-baggage',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Generate a random ID (16 hex chars)
|
|
18
|
+
*/
|
|
19
|
+
export function generateId() {
|
|
20
|
+
const bytes = new Uint8Array(8);
|
|
21
|
+
crypto.getRandomValues(bytes);
|
|
22
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract trace context from request headers
|
|
26
|
+
*/
|
|
27
|
+
export function extractTraceContext(headers) {
|
|
28
|
+
const traceId = headers[TRACE_HEADERS.TRACE_ID] || generateId();
|
|
29
|
+
const parentSpanId = headers[TRACE_HEADERS.SPAN_ID]; // caller's span becomes our parent
|
|
30
|
+
const spanId = generateId(); // new span for this request
|
|
31
|
+
let baggage = {};
|
|
32
|
+
const baggageHeader = headers[TRACE_HEADERS.BAGGAGE];
|
|
33
|
+
if (baggageHeader) {
|
|
34
|
+
try {
|
|
35
|
+
baggage = JSON.parse(baggageHeader);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Invalid baggage header, ignore
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { traceId, spanId, parentSpanId, baggage };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Serialize trace context into headers for outbound requests
|
|
45
|
+
*/
|
|
46
|
+
export function injectTraceHeaders(trace) {
|
|
47
|
+
const headers = {
|
|
48
|
+
[TRACE_HEADERS.TRACE_ID]: trace.traceId,
|
|
49
|
+
[TRACE_HEADERS.SPAN_ID]: trace.spanId,
|
|
50
|
+
};
|
|
51
|
+
if (trace.parentSpanId) {
|
|
52
|
+
headers[TRACE_HEADERS.PARENT_SPAN_ID] = trace.parentSpanId;
|
|
53
|
+
}
|
|
54
|
+
if (Object.keys(trace.baggage).length > 0) {
|
|
55
|
+
headers[TRACE_HEADERS.BAGGAGE] = JSON.stringify(trace.baggage);
|
|
56
|
+
}
|
|
57
|
+
return headers;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a child trace context (for outbound calls)
|
|
61
|
+
*/
|
|
62
|
+
export function createChildTrace(parent) {
|
|
63
|
+
return {
|
|
64
|
+
traceId: parent.traceId,
|
|
65
|
+
spanId: generateId(),
|
|
66
|
+
parentSpanId: parent.spanId,
|
|
67
|
+
baggage: { ...parent.baggage },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Extract trace context from a RequestContext
|
|
72
|
+
* (reads from rawEvent headers)
|
|
73
|
+
*/
|
|
74
|
+
export function traceFromRequest(ctx) {
|
|
75
|
+
return extractTraceContext(ctx.rawEvent.headers || {});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../../src/logging/tracing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ,EAAE,YAAY;IACtB,OAAO,EAAE,WAAW;IACpB,cAAc,EAAE,kBAAkB;IAClC,OAAO,EAAE,iBAAiB;CAClB,CAAC;AAEX;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA2C;IAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,UAAU,EAAE,CAAC;IAChE,MAAM,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC;IACxF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,CAAC,4BAA4B;IAEzD,IAAI,OAAO,GAA2B,EAAE,CAAC;IACzC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,OAAO,GAA2B;QACtC,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO;QACvC,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM;KACtC,CAAC;IAEF,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC;IAC7D,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,UAAU,EAAE;QACpB,YAAY,EAAE,MAAM,CAAC,MAAM;QAC3B,OAAO,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE;KAC/B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAClD,OAAO,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Middleware
|
|
3
|
+
*
|
|
4
|
+
* Composable middleware for request processing.
|
|
5
|
+
* Extensible for auth, tenancy, rate limiting, etc.
|
|
6
|
+
*/
|
|
7
|
+
import type { APIGatewayProxyResultV2 } from 'aws-lambda';
|
|
8
|
+
import { BaseContext, RequestContext } from '../handler/context.js';
|
|
9
|
+
import { Logger } from '../logging/logger.js';
|
|
10
|
+
/**
|
|
11
|
+
* Middleware function signature.
|
|
12
|
+
*
|
|
13
|
+
* Generic over the context type so the same interface works for both
|
|
14
|
+
* HTTP route handlers (`RequestContext`) and task handlers (`TaskContext`).
|
|
15
|
+
* Middleware written against `BaseContext` is reusable across all handler types.
|
|
16
|
+
*/
|
|
17
|
+
export type MiddlewareFn<TCtx extends BaseContext = BaseContext> = (ctx: TCtx, next: () => Promise<any>) => Promise<any>;
|
|
18
|
+
/**
|
|
19
|
+
* Middleware with configuration.
|
|
20
|
+
*
|
|
21
|
+
* Use without a type parameter to write middleware that works everywhere:
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const timing: Middleware = {
|
|
24
|
+
* name: 'timing',
|
|
25
|
+
* fn: async (ctx, next) => {
|
|
26
|
+
* const start = Date.now();
|
|
27
|
+
* const result = await next();
|
|
28
|
+
* console.log(`${ctx.requestId} took ${Date.now() - start}ms`);
|
|
29
|
+
* return result;
|
|
30
|
+
* },
|
|
31
|
+
* };
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export interface Middleware<TCtx extends BaseContext = BaseContext> {
|
|
35
|
+
name: string;
|
|
36
|
+
fn: MiddlewareFn<TCtx>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Compose multiple middleware into a single function
|
|
40
|
+
*/
|
|
41
|
+
export declare function compose(middlewares: Middleware<RequestContext>[]): MiddlewareFn<RequestContext>;
|
|
42
|
+
/**
|
|
43
|
+
* Logging middleware - logs request/response
|
|
44
|
+
*/
|
|
45
|
+
export declare function loggingMiddleware(logger: Logger): Middleware<RequestContext>;
|
|
46
|
+
/**
|
|
47
|
+
* CORS middleware - adds CORS headers
|
|
48
|
+
*/
|
|
49
|
+
export declare function corsMiddleware(options: {
|
|
50
|
+
allowOrigins: string[];
|
|
51
|
+
allowMethods: string[];
|
|
52
|
+
allowHeaders: string[];
|
|
53
|
+
allowCredentials: boolean;
|
|
54
|
+
maxAge: number;
|
|
55
|
+
}): Middleware<RequestContext>;
|
|
56
|
+
/**
|
|
57
|
+
* Timeout middleware - enforces request timeout
|
|
58
|
+
*/
|
|
59
|
+
export declare function timeoutMiddleware(timeoutMs: number): Middleware<RequestContext>;
|
|
60
|
+
/**
|
|
61
|
+
* Error boundary middleware - catches and formats errors
|
|
62
|
+
*/
|
|
63
|
+
export declare function errorBoundaryMiddleware(errorHandler: (error: unknown, ctx: RequestContext) => APIGatewayProxyResultV2): Middleware<RequestContext>;
|
|
64
|
+
/**
|
|
65
|
+
* Rate limit check function type.
|
|
66
|
+
* Returns { allowed, remaining, resetAt } for the given key.
|
|
67
|
+
*/
|
|
68
|
+
export interface RateLimitCheck {
|
|
69
|
+
(key: string): Promise<{
|
|
70
|
+
allowed: boolean;
|
|
71
|
+
remaining: number;
|
|
72
|
+
limit: number;
|
|
73
|
+
resetAt: number;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Rate limiting middleware options
|
|
78
|
+
*/
|
|
79
|
+
export interface RateLimitOptions {
|
|
80
|
+
/** Function that checks and increments the rate limit counter */
|
|
81
|
+
check: RateLimitCheck;
|
|
82
|
+
/** Extract the rate limit key from the request (default: sourceIp) */
|
|
83
|
+
keyExtractor?: (ctx: RequestContext) => string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rate limiting middleware
|
|
87
|
+
*
|
|
88
|
+
* Uses an external check function so the storage backend is pluggable
|
|
89
|
+
* (DynamoDB, Redis, in-memory, etc.).
|
|
90
|
+
*/
|
|
91
|
+
export declare function rateLimitMiddleware(options: RateLimitOptions): Middleware<RequestContext>;
|
|
92
|
+
/**
|
|
93
|
+
* Request ID propagation middleware
|
|
94
|
+
*
|
|
95
|
+
* Ensures every response includes an X-Request-Id header.
|
|
96
|
+
* Uses the existing requestId from context (from API Gateway)
|
|
97
|
+
* or an incoming X-Request-Id header for distributed tracing.
|
|
98
|
+
*/
|
|
99
|
+
export declare function requestIdMiddleware(): Middleware<RequestContext>;
|
|
100
|
+
/**
|
|
101
|
+
* Timing/metrics middleware
|
|
102
|
+
*
|
|
103
|
+
* Adds Server-Timing header with total request duration.
|
|
104
|
+
* Optionally calls a metrics callback for custom reporting.
|
|
105
|
+
*/
|
|
106
|
+
export declare function timingMiddleware(options?: {
|
|
107
|
+
onMetrics?: (ctx: RequestContext, durationMs: number, statusCode?: number) => void;
|
|
108
|
+
}): Middleware<RequestContext>;
|
|
109
|
+
/**
|
|
110
|
+
* API Key authentication middleware
|
|
111
|
+
*
|
|
112
|
+
* Validates an API key from a configurable header against a validation function.
|
|
113
|
+
*/
|
|
114
|
+
export declare function apiKeyAuthMiddleware(options: {
|
|
115
|
+
/** Header name to read the API key from (default: x-api-key) */
|
|
116
|
+
headerName?: string;
|
|
117
|
+
/** Validate the API key. Return true if valid. */
|
|
118
|
+
validate: (apiKey: string) => Promise<boolean> | boolean;
|
|
119
|
+
}): Middleware<RequestContext>;
|
|
120
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA0B,uBAAuB,EAAqC,MAAM,YAAY,CAAC;AACrH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,WAAW,GAAG,WAAW,IAAI,CACjE,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KACrB,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,UAAU,CAAC,IAAI,SAAS,WAAW,GAAG,WAAW;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,GAAG,YAAY,CAAC,cAAc,CAAC,CAoB/F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,cAAc,CAAC,CAsC5E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,UAAU,CAAC,cAAc,CAAC,CA2C7B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC,cAAc,CAAC,CAa/E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,KAAK,uBAAuB,GAC7E,UAAU,CAAC,cAAc,CAAC,CAW5B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QACrB,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,KAAK,EAAE,cAAc,CAAC;IACtB,sEAAsE;IACtE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,UAAU,CAAC,cAAc,CAAC,CAwCzF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,CAAC,cAAc,CAAC,CAwBhE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IACzC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACpF,GAAG,UAAU,CAAC,cAAc,CAAC,CAsB7B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC1D,GAAG,UAAU,CAAC,cAAc,CAAC,CA+B7B"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Middleware
|
|
3
|
+
*
|
|
4
|
+
* Composable middleware for request processing.
|
|
5
|
+
* Extensible for auth, tenancy, rate limiting, etc.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Compose multiple middleware into a single function
|
|
9
|
+
*/
|
|
10
|
+
export function compose(middlewares) {
|
|
11
|
+
return async (ctx, finalHandler) => {
|
|
12
|
+
let index = -1;
|
|
13
|
+
const dispatch = async (i) => {
|
|
14
|
+
if (i <= index) {
|
|
15
|
+
throw new Error('next() called multiple times');
|
|
16
|
+
}
|
|
17
|
+
index = i;
|
|
18
|
+
if (i >= middlewares.length) {
|
|
19
|
+
return finalHandler();
|
|
20
|
+
}
|
|
21
|
+
const middleware = middlewares[i];
|
|
22
|
+
return middleware.fn(ctx, () => dispatch(i + 1));
|
|
23
|
+
};
|
|
24
|
+
return dispatch(0);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Logging middleware - logs request/response
|
|
29
|
+
*/
|
|
30
|
+
export function loggingMiddleware(logger) {
|
|
31
|
+
return {
|
|
32
|
+
name: 'logging',
|
|
33
|
+
fn: async (ctx, next) => {
|
|
34
|
+
const start = Date.now();
|
|
35
|
+
logger.info('Request started', {
|
|
36
|
+
method: ctx.method,
|
|
37
|
+
path: ctx.path,
|
|
38
|
+
sourceIp: ctx.sourceIp,
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const response = await next();
|
|
42
|
+
const duration = Date.now() - start;
|
|
43
|
+
logger.info('Request completed', {
|
|
44
|
+
method: ctx.method,
|
|
45
|
+
path: ctx.path,
|
|
46
|
+
statusCode: typeof response === 'object' ? response.statusCode : undefined,
|
|
47
|
+
duration,
|
|
48
|
+
});
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const duration = Date.now() - start;
|
|
53
|
+
logger.error('Request failed', {
|
|
54
|
+
method: ctx.method,
|
|
55
|
+
path: ctx.path,
|
|
56
|
+
duration,
|
|
57
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
58
|
+
});
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* CORS middleware - adds CORS headers
|
|
66
|
+
*/
|
|
67
|
+
export function corsMiddleware(options) {
|
|
68
|
+
return {
|
|
69
|
+
name: 'cors',
|
|
70
|
+
fn: async (ctx, next) => {
|
|
71
|
+
const origin = ctx.rawEvent.headers?.origin || '';
|
|
72
|
+
const allowedOrigin = options.allowOrigins.includes('*')
|
|
73
|
+
? '*'
|
|
74
|
+
: options.allowOrigins.includes(origin)
|
|
75
|
+
? origin
|
|
76
|
+
: '';
|
|
77
|
+
// Handle preflight
|
|
78
|
+
if (ctx.method === 'OPTIONS') {
|
|
79
|
+
return {
|
|
80
|
+
statusCode: 204,
|
|
81
|
+
headers: {
|
|
82
|
+
'Access-Control-Allow-Origin': allowedOrigin,
|
|
83
|
+
'Access-Control-Allow-Methods': options.allowMethods.join(', '),
|
|
84
|
+
'Access-Control-Allow-Headers': options.allowHeaders.join(', '),
|
|
85
|
+
'Access-Control-Allow-Credentials': String(options.allowCredentials),
|
|
86
|
+
'Access-Control-Max-Age': String(options.maxAge),
|
|
87
|
+
},
|
|
88
|
+
body: '',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const response = await next();
|
|
92
|
+
// Add CORS headers to response
|
|
93
|
+
if (typeof response !== 'object' || response === null) {
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
const resp = response;
|
|
97
|
+
const corsHeaders = {
|
|
98
|
+
'Access-Control-Allow-Origin': allowedOrigin,
|
|
99
|
+
};
|
|
100
|
+
if (options.allowCredentials) {
|
|
101
|
+
corsHeaders['Access-Control-Allow-Credentials'] = 'true';
|
|
102
|
+
}
|
|
103
|
+
resp.headers = { ...resp.headers, ...corsHeaders };
|
|
104
|
+
return resp;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Timeout middleware - enforces request timeout
|
|
110
|
+
*/
|
|
111
|
+
export function timeoutMiddleware(timeoutMs) {
|
|
112
|
+
return {
|
|
113
|
+
name: 'timeout',
|
|
114
|
+
fn: async (ctx, next) => {
|
|
115
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
118
|
+
}, timeoutMs);
|
|
119
|
+
});
|
|
120
|
+
return Promise.race([next(), timeoutPromise]);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Error boundary middleware - catches and formats errors
|
|
126
|
+
*/
|
|
127
|
+
export function errorBoundaryMiddleware(errorHandler) {
|
|
128
|
+
return {
|
|
129
|
+
name: 'errorBoundary',
|
|
130
|
+
fn: async (ctx, next) => {
|
|
131
|
+
try {
|
|
132
|
+
return await next();
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return errorHandler(error, ctx);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Rate limiting middleware
|
|
142
|
+
*
|
|
143
|
+
* Uses an external check function so the storage backend is pluggable
|
|
144
|
+
* (DynamoDB, Redis, in-memory, etc.).
|
|
145
|
+
*/
|
|
146
|
+
export function rateLimitMiddleware(options) {
|
|
147
|
+
const { check, keyExtractor = (ctx) => ctx.sourceIp } = options;
|
|
148
|
+
return {
|
|
149
|
+
name: 'rateLimit',
|
|
150
|
+
fn: async (ctx, next) => {
|
|
151
|
+
const key = keyExtractor(ctx);
|
|
152
|
+
const result = await check(key);
|
|
153
|
+
if (!result.allowed) {
|
|
154
|
+
return {
|
|
155
|
+
statusCode: 429,
|
|
156
|
+
headers: {
|
|
157
|
+
'Retry-After': String(Math.ceil((result.resetAt - Date.now()) / 1000)),
|
|
158
|
+
'X-RateLimit-Limit': String(result.limit),
|
|
159
|
+
'X-RateLimit-Remaining': '0',
|
|
160
|
+
'X-RateLimit-Reset': String(result.resetAt),
|
|
161
|
+
},
|
|
162
|
+
body: JSON.stringify({
|
|
163
|
+
error: { code: 'RATE_LIMITED', message: 'Too many requests' },
|
|
164
|
+
}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const response = await next();
|
|
168
|
+
// Add rate limit headers to response
|
|
169
|
+
if (typeof response === 'object' && response !== null) {
|
|
170
|
+
const resp = response;
|
|
171
|
+
resp.headers = {
|
|
172
|
+
...resp.headers,
|
|
173
|
+
'X-RateLimit-Limit': String(result.limit),
|
|
174
|
+
'X-RateLimit-Remaining': String(result.remaining),
|
|
175
|
+
'X-RateLimit-Reset': String(result.resetAt),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return response;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Request ID propagation middleware
|
|
184
|
+
*
|
|
185
|
+
* Ensures every response includes an X-Request-Id header.
|
|
186
|
+
* Uses the existing requestId from context (from API Gateway)
|
|
187
|
+
* or an incoming X-Request-Id header for distributed tracing.
|
|
188
|
+
*/
|
|
189
|
+
export function requestIdMiddleware() {
|
|
190
|
+
return {
|
|
191
|
+
name: 'requestId',
|
|
192
|
+
fn: async (ctx, next) => {
|
|
193
|
+
// Prefer incoming header for distributed tracing, fall back to API Gateway request ID
|
|
194
|
+
const incomingId = ctx.rawEvent.headers?.['x-request-id'];
|
|
195
|
+
if (incomingId) {
|
|
196
|
+
ctx.requestId = incomingId;
|
|
197
|
+
}
|
|
198
|
+
const response = await next();
|
|
199
|
+
// Inject X-Request-Id into response
|
|
200
|
+
if (typeof response === 'object' && response !== null) {
|
|
201
|
+
const resp = response;
|
|
202
|
+
resp.headers = {
|
|
203
|
+
...resp.headers,
|
|
204
|
+
'X-Request-Id': ctx.requestId,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return response;
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Timing/metrics middleware
|
|
213
|
+
*
|
|
214
|
+
* Adds Server-Timing header with total request duration.
|
|
215
|
+
* Optionally calls a metrics callback for custom reporting.
|
|
216
|
+
*/
|
|
217
|
+
export function timingMiddleware(options) {
|
|
218
|
+
return {
|
|
219
|
+
name: 'timing',
|
|
220
|
+
fn: async (ctx, next) => {
|
|
221
|
+
const start = Date.now();
|
|
222
|
+
const response = await next();
|
|
223
|
+
const duration = Date.now() - start;
|
|
224
|
+
// Add Server-Timing header
|
|
225
|
+
if (typeof response === 'object' && response !== null) {
|
|
226
|
+
const resp = response;
|
|
227
|
+
resp.headers = {
|
|
228
|
+
...resp.headers,
|
|
229
|
+
'Server-Timing': `total;dur=${duration}`,
|
|
230
|
+
};
|
|
231
|
+
options?.onMetrics?.(ctx, duration, resp.statusCode);
|
|
232
|
+
}
|
|
233
|
+
return response;
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* API Key authentication middleware
|
|
239
|
+
*
|
|
240
|
+
* Validates an API key from a configurable header against a validation function.
|
|
241
|
+
*/
|
|
242
|
+
export function apiKeyAuthMiddleware(options) {
|
|
243
|
+
const headerName = (options.headerName ?? 'x-api-key').toLowerCase();
|
|
244
|
+
return {
|
|
245
|
+
name: 'apiKeyAuth',
|
|
246
|
+
fn: async (ctx, next) => {
|
|
247
|
+
// Read API key from header (headers are lowercased in API Gateway V2)
|
|
248
|
+
const apiKey = ctx.rawEvent.headers?.[headerName];
|
|
249
|
+
if (!apiKey) {
|
|
250
|
+
return {
|
|
251
|
+
statusCode: 401,
|
|
252
|
+
body: JSON.stringify({
|
|
253
|
+
error: { code: 'UNAUTHORIZED', message: 'API key is required' },
|
|
254
|
+
}),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const isValid = await options.validate(apiKey);
|
|
258
|
+
if (!isValid) {
|
|
259
|
+
return {
|
|
260
|
+
statusCode: 401,
|
|
261
|
+
body: JSON.stringify({
|
|
262
|
+
error: { code: 'UNAUTHORIZED', message: 'Invalid API key' },
|
|
263
|
+
}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return next();
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Rate Limit Stores
|
|
3
|
+
*
|
|
4
|
+
* Pluggable backends for the rateLimitMiddleware.
|
|
5
|
+
*
|
|
6
|
+
* - `createDynamoDBRateLimitStore()` — production-ready, distributed
|
|
7
|
+
* - `createMemoryRateLimitStore()` — for local dev/testing only
|
|
8
|
+
* - `createDefaultRateLimiter()` — zero-config rate limiter with sensible defaults
|
|
9
|
+
*/
|
|
10
|
+
import type { RateLimitCheck } from './middleware.js';
|
|
11
|
+
/**
|
|
12
|
+
* Rate limit preset name.
|
|
13
|
+
*/
|
|
14
|
+
export type RateLimitPreset = 'relaxed' | 'standard' | 'strict' | 'api-key';
|
|
15
|
+
/**
|
|
16
|
+
* Rate limit preset configuration.
|
|
17
|
+
*/
|
|
18
|
+
export interface RateLimitPresetConfig {
|
|
19
|
+
/** Max requests per window */
|
|
20
|
+
limit: number;
|
|
21
|
+
/** Window size in milliseconds */
|
|
22
|
+
windowMs: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Built-in rate limit presets covering common requirements.
|
|
26
|
+
*
|
|
27
|
+
* | Preset | Limit | Window | Use Case |
|
|
28
|
+
* |------------|---------|--------|---------------------------------------|
|
|
29
|
+
* | `relaxed` | 200/min | 60s | Internal APIs, dev environments |
|
|
30
|
+
* | `standard` | 100/min | 60s | Public APIs, most production needs |
|
|
31
|
+
* | `strict` | 20/min | 60s | Auth endpoints, sensitive operations |
|
|
32
|
+
* | `api-key` | 1000/min| 60s | Authenticated API-key consumers |
|
|
33
|
+
*/
|
|
34
|
+
export declare const RATE_LIMIT_PRESETS: Record<RateLimitPreset, RateLimitPresetConfig>;
|
|
35
|
+
/**
|
|
36
|
+
* Create an in-memory rate limit check function.
|
|
37
|
+
*
|
|
38
|
+
* **WARNING:** This stores counters in the Lambda process memory.
|
|
39
|
+
* It does NOT work correctly with multiple Lambda instances.
|
|
40
|
+
* Use only for local development and testing.
|
|
41
|
+
*
|
|
42
|
+
* For production, use `createDynamoDBRateLimitStore()`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createMemoryRateLimitStore(options?: {
|
|
45
|
+
/** Max requests per window (default: 100) */
|
|
46
|
+
limit?: number;
|
|
47
|
+
/** Window size in ms (default: 60000 = 1 minute) */
|
|
48
|
+
windowMs?: number;
|
|
49
|
+
/** Or use a preset */
|
|
50
|
+
preset?: RateLimitPreset;
|
|
51
|
+
}): RateLimitCheck;
|
|
52
|
+
/**
|
|
53
|
+
* DynamoDB rate limit store options.
|
|
54
|
+
*/
|
|
55
|
+
export interface DynamoDBRateLimitStoreOptions {
|
|
56
|
+
/** DynamoDB table name (default: 'venturekit-rate-limits') */
|
|
57
|
+
tableName?: string;
|
|
58
|
+
/** Max requests per window (default: 100) */
|
|
59
|
+
limit?: number;
|
|
60
|
+
/** Window size in ms (default: 60000 = 1 minute) */
|
|
61
|
+
windowMs?: number;
|
|
62
|
+
/** Or use a preset */
|
|
63
|
+
preset?: RateLimitPreset;
|
|
64
|
+
/** AWS region (default: process.env.AWS_REGION) */
|
|
65
|
+
region?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a DynamoDB-backed rate limit check function.
|
|
69
|
+
*
|
|
70
|
+
* Uses atomic `UpdateItem` with `ADD` to safely increment counters
|
|
71
|
+
* across multiple Lambda instances. Expired entries are automatically
|
|
72
|
+
* cleaned up via DynamoDB TTL.
|
|
73
|
+
*
|
|
74
|
+
* ### DynamoDB Table Schema
|
|
75
|
+
*
|
|
76
|
+
* The table must have:
|
|
77
|
+
* - **Partition key:** `pk` (String) — the rate limit key
|
|
78
|
+
* - **TTL attribute:** `ttl` (Number) — epoch seconds for auto-cleanup
|
|
79
|
+
*
|
|
80
|
+
* You can create this table via infrastructure intents or manually:
|
|
81
|
+
*
|
|
82
|
+
* ```bash
|
|
83
|
+
* aws dynamodb create-table \
|
|
84
|
+
* --table-name venturekit-rate-limits \
|
|
85
|
+
* --attribute-definitions AttributeName=pk,AttributeType=S \
|
|
86
|
+
* --key-schema AttributeName=pk,KeyType=HASH \
|
|
87
|
+
* --billing-mode PAY_PER_REQUEST
|
|
88
|
+
*
|
|
89
|
+
* aws dynamodb update-time-to-live \
|
|
90
|
+
* --table-name venturekit-rate-limits \
|
|
91
|
+
* --time-to-live-specification "Enabled=true, AttributeName=ttl"
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* import { rateLimitMiddleware } from '@venturekit/runtime';
|
|
97
|
+
* import { createDynamoDBRateLimitStore } from '@venturekit/runtime';
|
|
98
|
+
*
|
|
99
|
+
* const rateLimit = rateLimitMiddleware({
|
|
100
|
+
* check: await createDynamoDBRateLimitStore({ preset: 'standard' }),
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function createDynamoDBRateLimitStore(options?: DynamoDBRateLimitStoreOptions): Promise<RateLimitCheck>;
|
|
105
|
+
/**
|
|
106
|
+
* Options for the default rate limiter.
|
|
107
|
+
*
|
|
108
|
+
* Resolution order for `limit` and `windowMs`:
|
|
109
|
+
* 1. Explicit value (`limit` / `windowMs`) — highest priority
|
|
110
|
+
* 2. Preset values (if `preset` is provided)
|
|
111
|
+
* 3. Built-in defaults (100 req / 60 000 ms)
|
|
112
|
+
*
|
|
113
|
+
* This means you can use a preset as a base and selectively override
|
|
114
|
+
* individual values, e.g. `{ preset: 'strict', limit: 50 }`.
|
|
115
|
+
*/
|
|
116
|
+
export interface DefaultRateLimiterOptions {
|
|
117
|
+
/** Preset to use (no default — uses built-in 100 req/min when omitted) */
|
|
118
|
+
preset?: RateLimitPreset;
|
|
119
|
+
/** Custom limit (overrides preset if both are provided) */
|
|
120
|
+
limit?: number;
|
|
121
|
+
/** Custom window in ms (overrides preset if both are provided) */
|
|
122
|
+
windowMs?: number;
|
|
123
|
+
/** DynamoDB table name (default: 'venturekit-rate-limits') */
|
|
124
|
+
tableName?: string;
|
|
125
|
+
/** Force in-memory store even in production (NOT recommended) */
|
|
126
|
+
forceMemory?: boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create a rate limit check function with sensible defaults.
|
|
130
|
+
*
|
|
131
|
+
* - In production (Lambda): uses DynamoDB for distributed rate limiting
|
|
132
|
+
* - In local dev: uses in-memory store
|
|
133
|
+
*
|
|
134
|
+
* This is the recommended way to add rate limiting with minimal configuration.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { rateLimitMiddleware } from '@venturekit/runtime';
|
|
139
|
+
* import { createDefaultRateLimiter } from '@venturekit/runtime';
|
|
140
|
+
*
|
|
141
|
+
* // Zero-config — uses 'standard' preset (100 req/min)
|
|
142
|
+
* const rateLimit = rateLimitMiddleware({
|
|
143
|
+
* check: await createDefaultRateLimiter(),
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* // Strict for auth endpoints
|
|
147
|
+
* const authRateLimit = rateLimitMiddleware({
|
|
148
|
+
* check: await createDefaultRateLimiter({ preset: 'strict' }),
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export declare function createDefaultRateLimiter(options?: DefaultRateLimiterOptions): Promise<RateLimitCheck>;
|
|
153
|
+
//# sourceMappingURL=rate-limit-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-store.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,eAAe,EAAE,qBAAqB,CAK7E,CAAC;AAMF;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE;IACnD,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,cAAc,CAwBjB;AAMD;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,GAAE,6BAAkC,GAC1C,OAAO,CAAC,cAAc,CAAC,CAqDzB;AAMD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,cAAc,CAAC,CAiBzB"}
|