@hazeljs/serverless 0.2.0-beta.8 → 0.2.0-beta.81

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.
@@ -41,7 +41,8 @@ exports.OptimizeColdStart = OptimizeColdStart;
41
41
  const core_1 = __importDefault(require("@hazeljs/core"));
42
42
  const core_2 = require("@hazeljs/core");
43
43
  /**
44
- * Cold start optimization strategies
44
+ * Cold start optimization strategies.
45
+ * Best-effort and environment-dependent; preloads built-in modules and the DI container.
45
46
  */
46
47
  class ColdStartOptimizer {
47
48
  constructor() {
@@ -108,12 +109,10 @@ class ColdStartOptimizer {
108
109
  }
109
110
  }
110
111
  /**
111
- * Setup connection pools
112
+ * Setup connection pools (placeholder; override or extend for DB/API connection warming).
112
113
  */
113
114
  async setupConnectionPools() {
114
115
  core_1.default.debug('Setting up connection pools...');
115
- // Connection pools would be initialized here
116
- // For now, just a placeholder
117
116
  }
118
117
  /**
119
118
  * Check if the application is warmed up
@@ -168,19 +167,20 @@ function OptimizeColdStart() {
168
167
  };
169
168
  }
170
169
  /**
171
- * Keep-alive helper to prevent cold starts
170
+ * Keep-alive helper to schedule periodic pings.
171
+ * Does not perform an HTTP request; use a cron, external pinger, or implement fetch/http.get in the interval
172
+ * to actually warm the function.
172
173
  */
173
174
  class KeepAliveHelper {
174
175
  /**
175
- * Start keep-alive pings
176
+ * Start keep-alive interval (logs only; implement your own HTTP ping or use provisioned concurrency for warming).
176
177
  */
177
178
  start(url, intervalMs = 5 * 60 * 1000) {
178
179
  this.pingUrl = url;
179
180
  this.intervalId = setInterval(async () => {
180
181
  try {
181
182
  core_1.default.debug(`Sending keep-alive ping to ${url}`);
182
- // In a real implementation, you would make an HTTP request here
183
- // For now, just log it
183
+ // Implement fetch(url) or http.get(url) here to actually warm the function
184
184
  }
185
185
  catch (error) {
186
186
  core_1.default.error('Keep-alive ping failed:', error);
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export { Serverless, getServerlessMetadata, isServerless, type ServerlessOptions, type ServerlessHandler, type ServerlessContext, type ServerlessEvent, type ServerlessResponse, } from './serverless.decorator';
5
5
  export { ColdStartOptimizer, OptimizeColdStart, KeepAliveHelper } from './cold-start.optimizer';
6
- export { LambdaAdapter, createLambdaHandler, type LambdaEvent, type LambdaContext, } from './lambda.adapter';
7
- export { CloudFunctionAdapter, createCloudFunctionHandler, createCloudFunctionEventHandler, type CloudFunctionRequest, type CloudFunctionResponse, } from './cloud-function.adapter';
6
+ export { LambdaAdapter, createLambdaHandler, type LambdaEvent, type LambdaContext, type LambdaHandlerOptions, } from './lambda.adapter';
7
+ export { CloudFunctionAdapter, createCloudFunctionHandler, createCloudFunctionEventHandler, type CloudFunctionRequest, type CloudFunctionResponse, type CloudFunctionHandlerOptions, } from './cloud-function.adapter';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,+BAA+B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,+BAA+B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,GACjC,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Integration tests: real HazelJS app through Lambda and Cloud Function adapters.
3
+ * No mocks of @hazeljs/core — uses real HazelApp, router, and controllers.
4
+ */
5
+ import 'reflect-metadata';
6
+ //# sourceMappingURL=integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ /**
16
+ * Integration tests: real HazelJS app through Lambda and Cloud Function adapters.
17
+ * No mocks of @hazeljs/core — uses real HazelApp, router, and controllers.
18
+ */
19
+ require("reflect-metadata");
20
+ const core_1 = require("@hazeljs/core");
21
+ const lambda_adapter_1 = require("./lambda.adapter");
22
+ const cloud_function_adapter_1 = require("./cloud-function.adapter");
23
+ const cold_start_optimizer_1 = require("./cold-start.optimizer");
24
+ // ─── Minimal app for integration ───────────────────────────────────────────
25
+ let IntegrationController = class IntegrationController {
26
+ ping() {
27
+ return { ok: true, message: 'pong' };
28
+ }
29
+ getById(id) {
30
+ return { id };
31
+ }
32
+ echo(body) {
33
+ return { echoed: body };
34
+ }
35
+ };
36
+ __decorate([
37
+ (0, core_1.Get)('/ping'),
38
+ __metadata("design:type", Function),
39
+ __metadata("design:paramtypes", []),
40
+ __metadata("design:returntype", void 0)
41
+ ], IntegrationController.prototype, "ping", null);
42
+ __decorate([
43
+ (0, core_1.Get)('/params/:id'),
44
+ __param(0, (0, core_1.Param)('id')),
45
+ __metadata("design:type", Function),
46
+ __metadata("design:paramtypes", [String]),
47
+ __metadata("design:returntype", void 0)
48
+ ], IntegrationController.prototype, "getById", null);
49
+ __decorate([
50
+ (0, core_1.Post)('/echo'),
51
+ __param(0, (0, core_1.Body)()),
52
+ __metadata("design:type", Function),
53
+ __metadata("design:paramtypes", [Object]),
54
+ __metadata("design:returntype", void 0)
55
+ ], IntegrationController.prototype, "echo", null);
56
+ IntegrationController = __decorate([
57
+ (0, core_1.Controller)('/api')
58
+ ], IntegrationController);
59
+ let IntegrationTestModule = class IntegrationTestModule {
60
+ };
61
+ IntegrationTestModule = __decorate([
62
+ (0, core_1.HazelModule)({
63
+ controllers: [IntegrationController],
64
+ })
65
+ ], IntegrationTestModule);
66
+ // ─── Lambda helpers ──────────────────────────────────────────────────────────
67
+ function makeLambdaEvent(overrides = {}) {
68
+ return {
69
+ httpMethod: 'GET',
70
+ path: '/api/ping',
71
+ headers: { 'content-type': 'application/json' },
72
+ queryStringParameters: null,
73
+ body: null,
74
+ isBase64Encoded: false,
75
+ ...overrides,
76
+ };
77
+ }
78
+ function makeLambdaContext(overrides = {}) {
79
+ return {
80
+ awsRequestId: 'int-test-req-1',
81
+ invokedFunctionArn: 'arn:aws:lambda:us-east-1:000:function:integration-test',
82
+ functionName: 'integration-test',
83
+ functionVersion: '$LATEST',
84
+ getRemainingTimeInMillis: () => 30000,
85
+ callbackWaitsForEmptyEventLoop: true,
86
+ ...overrides,
87
+ };
88
+ }
89
+ // ─── Cloud Function helpers ──────────────────────────────────────────────────
90
+ function makeCloudReq(overrides = {}) {
91
+ return {
92
+ method: 'GET',
93
+ url: '/api/ping',
94
+ path: '/api/ping',
95
+ headers: { 'content-type': 'application/json' },
96
+ query: {},
97
+ ...overrides,
98
+ };
99
+ }
100
+ function makeCloudRes() {
101
+ const state = {
102
+ _status: 200,
103
+ _body: undefined,
104
+ _headers: {},
105
+ };
106
+ return Object.assign(state, {
107
+ status(code) {
108
+ state._status = code;
109
+ return this;
110
+ },
111
+ set(field, value) {
112
+ state._headers[field] = value;
113
+ return this;
114
+ },
115
+ send(body) {
116
+ state._body = body;
117
+ },
118
+ json(body) {
119
+ state._body = body;
120
+ },
121
+ end() { },
122
+ });
123
+ }
124
+ // ─── Integration tests ───────────────────────────────────────────────────────
125
+ describe('integration (real HazelApp)', () => {
126
+ beforeEach(() => {
127
+ cold_start_optimizer_1.ColdStartOptimizer.getInstance().reset();
128
+ });
129
+ describe('createLambdaHandler', () => {
130
+ const handler = (0, lambda_adapter_1.createLambdaHandler)(IntegrationTestModule);
131
+ it('GET /api/ping returns 200 and pong', async () => {
132
+ const result = await handler(makeLambdaEvent({ httpMethod: 'GET', path: '/api/ping' }), makeLambdaContext());
133
+ expect(result.statusCode).toBe(200);
134
+ const data = JSON.parse(result.body);
135
+ expect(data).toEqual({ ok: true, message: 'pong' });
136
+ });
137
+ it('GET /api/params/123 returns 200 and id', async () => {
138
+ const result = await handler(makeLambdaEvent({
139
+ httpMethod: 'GET',
140
+ path: '/api/params/123',
141
+ pathParameters: { id: '123' },
142
+ }), makeLambdaContext());
143
+ expect(result.statusCode).toBe(200);
144
+ const data = JSON.parse(result.body);
145
+ expect(data).toEqual({ id: '123' });
146
+ });
147
+ it('POST /api/echo returns 200 and echoed body', async () => {
148
+ const result = await handler(makeLambdaEvent({
149
+ httpMethod: 'POST',
150
+ path: '/api/echo',
151
+ body: JSON.stringify({ x: 1, text: 'hello' }),
152
+ }), makeLambdaContext());
153
+ expect(result.statusCode).toBe(200);
154
+ const data = JSON.parse(result.body);
155
+ expect(data).toEqual({ echoed: { x: 1, text: 'hello' } });
156
+ });
157
+ it('GET /api/missing returns 404', async () => {
158
+ const result = await handler(makeLambdaEvent({ httpMethod: 'GET', path: '/api/missing' }), makeLambdaContext());
159
+ expect(result.statusCode).toBe(404);
160
+ const data = JSON.parse(result.body);
161
+ expect(data.message).toBe('Route not found');
162
+ });
163
+ });
164
+ describe('createCloudFunctionHandler', () => {
165
+ const handler = (0, cloud_function_adapter_1.createCloudFunctionHandler)(IntegrationTestModule);
166
+ it('GET /api/ping returns 200 and pong', async () => {
167
+ const res = makeCloudRes();
168
+ await handler(makeCloudReq({ method: 'GET', url: '/api/ping', path: '/api/ping' }), res);
169
+ expect(res._status).toBe(200);
170
+ const body = typeof res._body === 'string' ? JSON.parse(res._body) : res._body;
171
+ expect(body).toEqual({ ok: true, message: 'pong' });
172
+ });
173
+ it('POST /api/echo returns 200 and echoed body', async () => {
174
+ const res = makeCloudRes();
175
+ await handler(makeCloudReq({
176
+ method: 'POST',
177
+ url: '/api/echo',
178
+ path: '/api/echo',
179
+ body: { x: 2, text: 'world' },
180
+ }), res);
181
+ expect(res._status).toBe(200);
182
+ const body = typeof res._body === 'string' ? JSON.parse(res._body) : res._body;
183
+ expect(body).toEqual({ echoed: { x: 2, text: 'world' } });
184
+ });
185
+ it('GET /api/missing returns 404', async () => {
186
+ const res = makeCloudRes();
187
+ await handler(makeCloudReq({ method: 'GET', url: '/api/missing', path: '/api/missing' }), res);
188
+ expect(res._status).toBe(404);
189
+ });
190
+ });
191
+ });
@@ -32,15 +32,27 @@ export interface LambdaContext extends ServerlessContext {
32
32
  invokedFunctionArn: string;
33
33
  callbackWaitsForEmptyEventLoop: boolean;
34
34
  }
35
+ /**
36
+ * Options for createLambdaHandler
37
+ */
38
+ export interface LambdaHandlerOptions {
39
+ /** Called after the HazelJS app is initialized (e.g. for custom setup). */
40
+ onInit?: (app: HazelApp) => Promise<void>;
41
+ /** Called when the handler throws (e.g. for custom logging or reporting). */
42
+ onError?: (error: unknown) => void;
43
+ /** MIME types (e.g. 'image/png', 'image/*') that trigger base64 encoding of Buffer body for Lambda. */
44
+ binaryMimeTypes?: string[];
45
+ }
35
46
  /**
36
47
  * Lambda adapter for HazelJS
37
48
  */
38
49
  export declare class LambdaAdapter {
39
50
  private moduleClass;
51
+ private options;
40
52
  private app?;
41
53
  private optimizer;
42
54
  private isColdStart;
43
- constructor(moduleClass: Type<unknown>);
55
+ constructor(moduleClass: Type<unknown>, options?: LambdaHandlerOptions);
44
56
  /**
45
57
  * Initialize the HazelJS application
46
58
  */
@@ -61,6 +73,10 @@ export declare class LambdaAdapter {
61
73
  * Process request through HazelJS router
62
74
  */
63
75
  private processRequest;
76
+ /**
77
+ * Whether the given Content-Type matches any of the binaryMimeTypes patterns.
78
+ */
79
+ private isBinaryContentType;
64
80
  /**
65
81
  * Get application instance
66
82
  */
@@ -82,5 +98,5 @@ export declare class LambdaAdapter {
82
98
  * export const handler = createLambdaHandler(AppModule);
83
99
  * ```
84
100
  */
85
- export declare function createLambdaHandler(moduleClass: Type<unknown>): (event: LambdaEvent, context: LambdaContext) => Promise<ServerlessResponse>;
101
+ export declare function createLambdaHandler(moduleClass: Type<unknown>, options?: LambdaHandlerOptions): (event: LambdaEvent, context: LambdaContext) => Promise<ServerlessResponse>;
86
102
  //# sourceMappingURL=lambda.adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lambda.adapter.d.ts","sourceRoot":"","sources":["../src/lambda.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AAGhC;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE;YACR,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,8BAA8B,EAAE,OAAO,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,aAAa;IAKZ,OAAO,CAAC,WAAW;IAJ/B,OAAO,CAAC,GAAG,CAAC,CAAW;IACvB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAQ;gBAEP,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC;IAI9C;;OAEG;YACW,UAAU;IAqBxB;;OAEG;IACH,aAAa,KACG,OAAO,WAAW,EAAE,SAAS,aAAa,KAAG,OAAO,CAAC,kBAAkB,CAAC;IAkCxF;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA+BnC;;OAEG;IACH,OAAO,CAAC,SAAS;IAYjB;;OAEG;YACW,cAAc;IAiH5B;;OAEG;IACH,MAAM,IAAI,QAAQ,GAAG,SAAS;IAI9B;;OAEG;IACH,MAAM,IAAI,OAAO;CAGlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GACzB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAG7E"}
1
+ {"version":3,"file":"lambda.adapter.d.ts","sourceRoot":"","sources":["../src/lambda.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AAGhC;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE;YACR,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,8BAA8B,EAAE,OAAO,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,6EAA6E;IAC7E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,uGAAuG;IACvG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,aAAa;IAMtB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,OAAO;IANjB,OAAO,CAAC,GAAG,CAAC,CAAW;IACvB,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAQ;gBAGjB,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAC1B,OAAO,GAAE,oBAAyB;IAK5C;;OAEG;YACW,UAAU;IAyBxB;;OAEG;IACH,aAAa,KACG,OAAO,WAAW,EAAE,SAAS,aAAa,KAAG,OAAO,CAAC,kBAAkB,CAAC;IAmCxF;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA+BnC;;OAEG;IACH,OAAO,CAAC,SAAS;IAYjB;;OAEG;YACW,cAAc;IAqI5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;OAEG;IACH,MAAM,IAAI,QAAQ,GAAG,SAAS;IAI9B;;OAEG;IACH,MAAM,IAAI,OAAO;CAGlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAC1B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAG7E"}
@@ -13,8 +13,9 @@ const cold_start_optimizer_1 = require("./cold-start.optimizer");
13
13
  * Lambda adapter for HazelJS
14
14
  */
15
15
  class LambdaAdapter {
16
- constructor(moduleClass) {
16
+ constructor(moduleClass, options = {}) {
17
17
  this.moduleClass = moduleClass;
18
+ this.options = options;
18
19
  this.isColdStart = true;
19
20
  this.optimizer = cold_start_optimizer_1.ColdStartOptimizer.getInstance();
20
21
  }
@@ -34,6 +35,9 @@ class LambdaAdapter {
34
35
  }
35
36
  // Create HazelJS application
36
37
  this.app = new core_1.HazelApp(this.moduleClass);
38
+ if (this.options.onInit && this.app) {
39
+ await this.options.onInit(this.app);
40
+ }
37
41
  const duration = Date.now() - startTime;
38
42
  core_2.default.info(`Lambda initialization completed in ${duration}ms`);
39
43
  }
@@ -57,6 +61,7 @@ class LambdaAdapter {
57
61
  return response;
58
62
  }
59
63
  catch (error) {
64
+ this.options.onError?.(error);
60
65
  core_2.default.error('Lambda handler error:', error);
61
66
  return {
62
67
  statusCode: 500,
@@ -141,7 +146,7 @@ class LambdaAdapter {
141
146
  url: request.url,
142
147
  headers: request.headers,
143
148
  query: request.query,
144
- params: context.params || {},
149
+ params: route.context?.params ?? context.params ?? {},
145
150
  body: request.body,
146
151
  };
147
152
  let responseBody;
@@ -167,15 +172,29 @@ class LambdaAdapter {
167
172
  return responseHeaders[key];
168
173
  },
169
174
  };
170
- const result = await route.handler(syntheticReq, syntheticRes);
175
+ const result = await route.handler(syntheticReq, syntheticRes, route.context);
171
176
  // If handler returned a value directly, use it
172
177
  if (result !== undefined && responseBody === undefined) {
173
178
  responseBody = result;
174
179
  }
180
+ const contentType = responseHeaders['content-type'] ?? responseHeaders['Content-Type'] ?? '';
181
+ const isBinary = this.options.binaryMimeTypes?.length &&
182
+ this.isBinaryContentType(contentType) &&
183
+ (Buffer.isBuffer(responseBody) || responseBody instanceof Uint8Array);
184
+ let body;
185
+ let isBase64Encoded = false;
186
+ if (isBinary) {
187
+ body = Buffer.from(responseBody).toString('base64');
188
+ isBase64Encoded = true;
189
+ }
190
+ else {
191
+ body = typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody);
192
+ }
175
193
  return {
176
194
  statusCode: responseStatus,
177
- body: typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody),
195
+ body,
178
196
  headers: responseHeaders,
197
+ ...(isBase64Encoded && { isBase64Encoded: true }),
179
198
  };
180
199
  }
181
200
  catch (error) {
@@ -188,6 +207,25 @@ class LambdaAdapter {
188
207
  };
189
208
  }
190
209
  }
210
+ /**
211
+ * Whether the given Content-Type matches any of the binaryMimeTypes patterns.
212
+ */
213
+ isBinaryContentType(contentType) {
214
+ if (!this.options.binaryMimeTypes?.length || !contentType)
215
+ return false;
216
+ const ct = contentType.split(';')[0].trim().toLowerCase();
217
+ for (const pattern of this.options.binaryMimeTypes) {
218
+ const p = pattern.toLowerCase();
219
+ if (p.endsWith('/*')) {
220
+ if (ct.startsWith(p.slice(0, -1)))
221
+ return true;
222
+ }
223
+ else if (ct === p || ct.startsWith(p + '/')) {
224
+ return true;
225
+ }
226
+ }
227
+ return false;
228
+ }
191
229
  /**
192
230
  * Get application instance
193
231
  */
@@ -214,7 +252,7 @@ exports.LambdaAdapter = LambdaAdapter;
214
252
  * export const handler = createLambdaHandler(AppModule);
215
253
  * ```
216
254
  */
217
- function createLambdaHandler(moduleClass) {
218
- const adapter = new LambdaAdapter(moduleClass);
255
+ function createLambdaHandler(moduleClass, options) {
256
+ const adapter = new LambdaAdapter(moduleClass, options ?? {});
219
257
  return adapter.createHandler();
220
258
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serverless.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverless.test.d.ts","sourceRoot":"","sources":["../src/serverless.test.ts"],"names":[],"mappings":""}