@hazeljs/serverless 0.2.0-beta.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.
package/README.md ADDED
@@ -0,0 +1,500 @@
1
+ # @hazeljs/serverless
2
+
3
+ **Serverless Adapters for HazelJS - AWS Lambda & Google Cloud Functions**
4
+
5
+ Deploy HazelJS applications to serverless platforms with zero configuration changes.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@hazeljs/serverless.svg)](https://www.npmjs.com/package/@hazeljs/serverless)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## Features
11
+
12
+ - ☁️ **AWS Lambda** - Deploy to AWS Lambda
13
+ - 🌐 **Google Cloud Functions** - Deploy to GCP
14
+ - 🔄 **Zero Config** - No code changes needed
15
+ - 🎯 **Cold Start Optimization** - Minimize cold start times
16
+ - 📊 **Request/Response Mapping** - Automatic event transformation
17
+ - 🔐 **Environment Variables** - Seamless config management
18
+ - 🎨 **Decorator Support** - `@Serverless` decorator
19
+ - 📦 **Bundle Optimization** - Tree-shaking and minification
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @hazeljs/serverless
25
+ ```
26
+
27
+ ## AWS Lambda
28
+
29
+ ### Quick Start
30
+
31
+ ```typescript
32
+ // lambda.ts
33
+ import { createLambdaHandler } from '@hazeljs/serverless';
34
+ import { AppModule } from './app.module';
35
+
36
+ export const handler = createLambdaHandler(AppModule);
37
+ ```
38
+
39
+ ### With Options
40
+
41
+ ```typescript
42
+ import { createLambdaHandler } from '@hazeljs/serverless';
43
+ import { AppModule } from './app.module';
44
+
45
+ export const handler = createLambdaHandler(AppModule, {
46
+ // Enable binary response
47
+ binaryMimeTypes: ['image/*', 'application/pdf'],
48
+
49
+ // Custom initialization
50
+ onInit: async (app) => {
51
+ console.log('Lambda initialized');
52
+ },
53
+
54
+ // Custom error handling
55
+ onError: (error) => {
56
+ console.error('Lambda error:', error);
57
+ },
58
+ });
59
+ ```
60
+
61
+ ### Deployment
62
+
63
+ #### Using AWS SAM
64
+
65
+ ```yaml
66
+ # template.yaml
67
+ AWSTemplateFormatVersion: '2010-09-09'
68
+ Transform: AWS::Serverless-2016-10-31
69
+
70
+ Resources:
71
+ HazelFunction:
72
+ Type: AWS::Serverless::Function
73
+ Properties:
74
+ CodeUri: dist/
75
+ Handler: lambda.handler
76
+ Runtime: nodejs20.x
77
+ MemorySize: 512
78
+ Timeout: 30
79
+ Environment:
80
+ Variables:
81
+ NODE_ENV: production
82
+ DATABASE_URL: !Ref DatabaseUrl
83
+ Events:
84
+ ApiEvent:
85
+ Type: Api
86
+ Properties:
87
+ Path: /{proxy+}
88
+ Method: ANY
89
+ ```
90
+
91
+ Deploy:
92
+
93
+ ```bash
94
+ npm run build
95
+ sam build
96
+ sam deploy --guided
97
+ ```
98
+
99
+ #### Using Serverless Framework
100
+
101
+ ```yaml
102
+ # serverless.yml
103
+ service: hazeljs-app
104
+
105
+ provider:
106
+ name: aws
107
+ runtime: nodejs20.x
108
+ stage: ${opt:stage, 'dev'}
109
+ region: us-east-1
110
+ environment:
111
+ NODE_ENV: ${self:provider.stage}
112
+ DATABASE_URL: ${env:DATABASE_URL}
113
+
114
+ functions:
115
+ api:
116
+ handler: dist/lambda.handler
117
+ events:
118
+ - http:
119
+ path: /{proxy+}
120
+ method: ANY
121
+ cors: true
122
+
123
+ package:
124
+ individually: true
125
+ patterns:
126
+ - '!node_modules/**'
127
+ - 'node_modules/@hazeljs/**'
128
+ - 'dist/**'
129
+ ```
130
+
131
+ Deploy:
132
+
133
+ ```bash
134
+ npm run build
135
+ serverless deploy
136
+ ```
137
+
138
+ ## Google Cloud Functions
139
+
140
+ ### Quick Start
141
+
142
+ ```typescript
143
+ // index.ts
144
+ import { createCloudFunctionHandler } from '@hazeljs/serverless';
145
+ import { AppModule } from './app.module';
146
+
147
+ export const hazelApp = createCloudFunctionHandler(AppModule);
148
+ ```
149
+
150
+ ### With Options
151
+
152
+ ```typescript
153
+ import { createCloudFunctionHandler } from '@hazeljs/serverless';
154
+ import { AppModule } from './app.module';
155
+
156
+ export const hazelApp = createCloudFunctionHandler(AppModule, {
157
+ // Custom initialization
158
+ onInit: async (app) => {
159
+ console.log('Cloud Function initialized');
160
+ },
161
+
162
+ // Custom error handling
163
+ onError: (error) => {
164
+ console.error('Cloud Function error:', error);
165
+ },
166
+ });
167
+ ```
168
+
169
+ ### Deployment
170
+
171
+ #### Using gcloud CLI
172
+
173
+ ```bash
174
+ # Build
175
+ npm run build
176
+
177
+ # Deploy
178
+ gcloud functions deploy hazeljs-app \
179
+ --runtime nodejs20 \
180
+ --trigger-http \
181
+ --allow-unauthenticated \
182
+ --entry-point hazelApp \
183
+ --source dist \
184
+ --set-env-vars NODE_ENV=production,DATABASE_URL=your-db-url
185
+ ```
186
+
187
+ #### Using Cloud Functions YAML
188
+
189
+ ```yaml
190
+ # function.yaml
191
+ runtime: nodejs20
192
+ entryPoint: hazelApp
193
+ environmentVariables:
194
+ NODE_ENV: production
195
+ DATABASE_URL: ${DATABASE_URL}
196
+ ```
197
+
198
+ Deploy:
199
+
200
+ ```bash
201
+ gcloud functions deploy hazeljs-app --config function.yaml
202
+ ```
203
+
204
+ ## Decorator-Based Optimization
205
+
206
+ Mark routes for serverless optimization:
207
+
208
+ ```typescript
209
+ import { Controller, Get } from '@hazeljs/core';
210
+ import { Serverless } from '@hazeljs/serverless';
211
+
212
+ @Controller('/api')
213
+ export class ApiController {
214
+ @Get('/hello')
215
+ @Serverless({ optimize: true })
216
+ hello() {
217
+ return { message: 'Hello from serverless!' };
218
+ }
219
+
220
+ @Get('/data')
221
+ @Serverless({
222
+ optimize: true,
223
+ cache: {
224
+ enabled: true,
225
+ ttl: 300, // 5 minutes
226
+ },
227
+ })
228
+ getData() {
229
+ return { data: 'cached response' };
230
+ }
231
+ }
232
+ ```
233
+
234
+ ## Cold Start Optimization
235
+
236
+ ### Minimize Bundle Size
237
+
238
+ ```typescript
239
+ // Use dynamic imports for heavy dependencies
240
+ @Get('/heavy')
241
+ async heavyOperation() {
242
+ const { processData } = await import('./heavy-processor');
243
+ return processData();
244
+ }
245
+ ```
246
+
247
+ ### Connection Pooling
248
+
249
+ ```typescript
250
+ // Reuse database connections across invocations
251
+ let cachedDb: any = null;
252
+
253
+ async function connectToDatabase() {
254
+ if (cachedDb) {
255
+ return cachedDb;
256
+ }
257
+
258
+ cachedDb = await createDatabaseConnection();
259
+ return cachedDb;
260
+ }
261
+
262
+ @Injectable()
263
+ export class DatabaseService {
264
+ async query(sql: string) {
265
+ const db = await connectToDatabase();
266
+ return db.query(sql);
267
+ }
268
+ }
269
+ ```
270
+
271
+ ### Provisioned Concurrency (AWS Lambda)
272
+
273
+ ```yaml
274
+ # template.yaml
275
+ Resources:
276
+ HazelFunction:
277
+ Type: AWS::Serverless::Function
278
+ Properties:
279
+ # ... other properties
280
+ ProvisionedConcurrencyConfig:
281
+ ProvisionedConcurrentExecutions: 5
282
+ ```
283
+
284
+ ## Environment-Specific Configuration
285
+
286
+ ```typescript
287
+ // config/serverless.config.ts
288
+ export const serverlessConfig = {
289
+ development: {
290
+ timeout: 30,
291
+ memorySize: 512,
292
+ },
293
+ production: {
294
+ timeout: 60,
295
+ memorySize: 1024,
296
+ provisionedConcurrency: 5,
297
+ },
298
+ };
299
+
300
+ // lambda.ts
301
+ const config = serverlessConfig[process.env.NODE_ENV || 'development'];
302
+
303
+ export const handler = createLambdaHandler(AppModule, {
304
+ timeout: config.timeout,
305
+ });
306
+ ```
307
+
308
+ ## Request/Response Handling
309
+
310
+ ### AWS Lambda Event Types
311
+
312
+ ```typescript
313
+ import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
314
+
315
+ // Access raw Lambda event
316
+ @Get('/lambda-info')
317
+ getLambdaInfo(@Req() req: any) {
318
+ const event: APIGatewayProxyEvent = req.apiGateway.event;
319
+
320
+ return {
321
+ requestId: event.requestContext.requestId,
322
+ sourceIp: event.requestContext.identity.sourceIp,
323
+ userAgent: event.headers['user-agent'],
324
+ };
325
+ }
326
+ ```
327
+
328
+ ### Binary Responses
329
+
330
+ ```typescript
331
+ @Get('/image')
332
+ @Serverless({ binaryResponse: true })
333
+ async getImage(@Res() res: Response) {
334
+ const image = await fs.readFile('image.png');
335
+ res.setHeader('Content-Type', 'image/png');
336
+ res.send(image);
337
+ }
338
+ ```
339
+
340
+ ## Logging
341
+
342
+ ### CloudWatch Logs (AWS)
343
+
344
+ ```typescript
345
+ import { Logger } from '@hazeljs/core';
346
+
347
+ @Injectable()
348
+ export class MyService {
349
+ private logger = new Logger(MyService.name);
350
+
351
+ async doSomething() {
352
+ this.logger.log('Processing request');
353
+ this.logger.error('An error occurred');
354
+ this.logger.warn('Warning message');
355
+ }
356
+ }
357
+ ```
358
+
359
+ ### Cloud Logging (GCP)
360
+
361
+ ```typescript
362
+ import { Logger } from '@hazeljs/core';
363
+
364
+ @Injectable()
365
+ export class MyService {
366
+ private logger = new Logger(MyService.name);
367
+
368
+ async doSomething() {
369
+ // Logs automatically sent to Cloud Logging
370
+ this.logger.log('Processing request', {
371
+ userId: '123',
372
+ action: 'create',
373
+ });
374
+ }
375
+ }
376
+ ```
377
+
378
+ ## Best Practices
379
+
380
+ 1. **Minimize Dependencies** - Only include necessary packages
381
+ 2. **Reuse Connections** - Cache database and API connections
382
+ 3. **Use Environment Variables** - Store configuration externally
383
+ 4. **Optimize Bundle Size** - Use tree-shaking and minification
384
+ 5. **Handle Cold Starts** - Implement warming strategies
385
+ 6. **Monitor Performance** - Track execution time and memory usage
386
+ 7. **Set Appropriate Timeouts** - Balance cost and functionality
387
+ 8. **Use Provisioned Concurrency** - For latency-sensitive endpoints
388
+
389
+ ## Monitoring
390
+
391
+ ### AWS Lambda
392
+
393
+ ```typescript
394
+ // Add X-Ray tracing
395
+ import AWSXRay from 'aws-xray-sdk-core';
396
+
397
+ const AWS = AWSXRay.captureAWS(require('aws-sdk'));
398
+
399
+ // Custom metrics
400
+ import { CloudWatch } from 'aws-sdk';
401
+
402
+ const cloudwatch = new CloudWatch();
403
+
404
+ async function recordMetric(name: string, value: number) {
405
+ await cloudwatch.putMetricData({
406
+ Namespace: 'HazelJS',
407
+ MetricData: [{
408
+ MetricName: name,
409
+ Value: value,
410
+ Unit: 'Count',
411
+ }],
412
+ }).promise();
413
+ }
414
+ ```
415
+
416
+ ### Google Cloud Functions
417
+
418
+ ```typescript
419
+ // Add Cloud Trace
420
+ import { TraceAgent } from '@google-cloud/trace-agent';
421
+
422
+ TraceAgent.start();
423
+
424
+ // Custom metrics
425
+ import { Monitoring } from '@google-cloud/monitoring';
426
+
427
+ const monitoring = new Monitoring.MetricServiceClient();
428
+
429
+ async function recordMetric(name: string, value: number) {
430
+ const request = {
431
+ name: monitoring.projectPath(projectId),
432
+ timeSeries: [{
433
+ metric: { type: `custom.googleapis.com/${name}` },
434
+ points: [{
435
+ interval: { endTime: { seconds: Date.now() / 1000 } },
436
+ value: { doubleValue: value },
437
+ }],
438
+ }],
439
+ };
440
+
441
+ await monitoring.createTimeSeries(request);
442
+ }
443
+ ```
444
+
445
+ ## Cost Optimization
446
+
447
+ ### Request Batching
448
+
449
+ ```typescript
450
+ // Batch multiple operations
451
+ @Post('/batch')
452
+ async batchProcess(@Body() items: any[]) {
453
+ const results = await Promise.all(
454
+ items.map(item => this.processItem(item))
455
+ );
456
+ return results;
457
+ }
458
+ ```
459
+
460
+ ### Caching
461
+
462
+ ```typescript
463
+ import { Cache } from '@hazeljs/cache';
464
+
465
+ @Injectable()
466
+ export class DataService {
467
+ @Cache({ key: 'expensive-data', ttl: 3600 })
468
+ async getExpensiveData() {
469
+ // This will be cached for 1 hour
470
+ return await this.fetchFromDatabase();
471
+ }
472
+ }
473
+ ```
474
+
475
+ ## Examples
476
+
477
+ See the [examples](../../example/src/serverless) directory for complete working examples.
478
+
479
+ ## Testing
480
+
481
+ ```bash
482
+ npm test
483
+ ```
484
+
485
+ ## Contributing
486
+
487
+ Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
488
+
489
+ ## License
490
+
491
+ MIT © [HazelJS](https://hazeljs.com)
492
+
493
+ ## Links
494
+
495
+ - [Documentation](https://hazeljs.com/docs/packages/serverless)
496
+ - [AWS Lambda Docs](https://docs.aws.amazon.com/lambda/)
497
+ - [Google Cloud Functions Docs](https://cloud.google.com/functions/docs)
498
+ - [GitHub](https://github.com/hazel-js/hazeljs)
499
+ - [Issues](https://github.com/hazeljs/hazel-js/issues)
500
+ - [Discord](https://discord.gg/hazeljs)
@@ -0,0 +1,97 @@
1
+ import { HazelApp } from '@hazeljs/core';
2
+ import { Type } from '@hazeljs/core';
3
+ /**
4
+ * Google Cloud Function Request
5
+ */
6
+ export interface CloudFunctionRequest {
7
+ method: string;
8
+ url: string;
9
+ path: string;
10
+ headers: Record<string, string | string[]>;
11
+ query: Record<string, string | string[]>;
12
+ body?: unknown;
13
+ rawBody?: Buffer;
14
+ }
15
+ /**
16
+ * Google Cloud Function Response
17
+ */
18
+ export interface CloudFunctionResponse {
19
+ status(code: number): CloudFunctionResponse;
20
+ set(field: string, value: string): CloudFunctionResponse;
21
+ send(body: unknown): void;
22
+ json(body: unknown): void;
23
+ end(): void;
24
+ }
25
+ /**
26
+ * Cloud Function adapter for HazelJS
27
+ */
28
+ export declare class CloudFunctionAdapter {
29
+ private moduleClass;
30
+ private app?;
31
+ private optimizer;
32
+ private isColdStart;
33
+ constructor(moduleClass: Type<unknown>);
34
+ /**
35
+ * Initialize the HazelJS application
36
+ */
37
+ private initialize;
38
+ /**
39
+ * Create Cloud Function HTTP handler
40
+ */
41
+ createHttpHandler(): (req: CloudFunctionRequest, res: CloudFunctionResponse) => Promise<void>;
42
+ /**
43
+ * Create Cloud Function event handler (for Pub/Sub, Storage, etc.)
44
+ */
45
+ createEventHandler(): (event: unknown, context: unknown) => Promise<void>;
46
+ /**
47
+ * Convert Cloud Function request to internal format
48
+ */
49
+ private convertCloudFunctionRequest;
50
+ /**
51
+ * Normalize headers (convert string[] to string)
52
+ */
53
+ private normalizeHeaders;
54
+ /**
55
+ * Normalize query parameters
56
+ */
57
+ private normalizeQuery;
58
+ /**
59
+ * Process request through HazelJS router
60
+ */
61
+ private processRequest;
62
+ /**
63
+ * Get application instance
64
+ */
65
+ getApp(): HazelApp | undefined;
66
+ /**
67
+ * Check if this is a cold start
68
+ */
69
+ isCold(): boolean;
70
+ }
71
+ /**
72
+ * Create a Cloud Function HTTP handler for a HazelJS module
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // index.ts
77
+ * import { createCloudFunctionHandler } from '@hazeljs/core';
78
+ * import { AppModule } from './app.module';
79
+ *
80
+ * export const handler = createCloudFunctionHandler(AppModule);
81
+ * ```
82
+ */
83
+ export declare function createCloudFunctionHandler(moduleClass: Type<unknown>): (req: CloudFunctionRequest, res: CloudFunctionResponse) => Promise<void>;
84
+ /**
85
+ * Create a Cloud Function event handler for a HazelJS module
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * // index.ts
90
+ * import { createCloudFunctionEventHandler } from '@hazeljs/core';
91
+ * import { AppModule } from './app.module';
92
+ *
93
+ * export const handler = createCloudFunctionEventHandler(AppModule);
94
+ * ```
95
+ */
96
+ export declare function createCloudFunctionEventHandler(moduleClass: Type<unknown>): (event: unknown, context: unknown) => Promise<void>;
97
+ //# sourceMappingURL=cloud-function.adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-function.adapter.d.ts","sourceRoot":"","sources":["../src/cloud-function.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAKrC;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAAC;IAC5C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAAC;IACzD,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,GAAG,IAAI,IAAI,CAAC;CACb;AAED;;GAEG;AACH,qBAAa,oBAAoB;IAKnB,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,iBAAiB,KACD,KAAK,oBAAoB,EAAE,KAAK,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAAC;IAqCrF;;OAEG;IACH,kBAAkB,KACF,OAAO,OAAO,EAAE,SAAS,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;IA0BhE;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAoBnC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;YACW,cAAc;IAsF5B;;OAEG;IACH,MAAM,IAAI,QAAQ,GAAG,SAAS;IAI9B;;OAEG;IACH,MAAM,IAAI,OAAO;CAGlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GACzB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GACzB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAGrD"}