@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 +500 -0
- package/dist/cloud-function.adapter.d.ts +97 -0
- package/dist/cloud-function.adapter.d.ts.map +1 -0
- package/dist/cloud-function.adapter.js +252 -0
- package/dist/cold-start.optimizer.d.ts +67 -0
- package/dist/cold-start.optimizer.d.ts.map +1 -0
- package/dist/cold-start.optimizer.js +202 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/lambda.adapter.d.ts +86 -0
- package/dist/lambda.adapter.d.ts.map +1 -0
- package/dist/lambda.adapter.js +208 -0
- package/dist/serverless.decorator.d.ts +166 -0
- package/dist/serverless.decorator.d.ts.map +1 -0
- package/dist/serverless.decorator.js +56 -0
- package/package.json +47 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CloudFunctionAdapter = void 0;
|
|
7
|
+
exports.createCloudFunctionHandler = createCloudFunctionHandler;
|
|
8
|
+
exports.createCloudFunctionEventHandler = createCloudFunctionEventHandler;
|
|
9
|
+
const core_1 = require("@hazeljs/core");
|
|
10
|
+
const core_2 = __importDefault(require("@hazeljs/core"));
|
|
11
|
+
const serverless_decorator_1 = require("./serverless.decorator");
|
|
12
|
+
const cold_start_optimizer_1 = require("./cold-start.optimizer");
|
|
13
|
+
/**
|
|
14
|
+
* Cloud Function adapter for HazelJS
|
|
15
|
+
*/
|
|
16
|
+
class CloudFunctionAdapter {
|
|
17
|
+
constructor(moduleClass) {
|
|
18
|
+
this.moduleClass = moduleClass;
|
|
19
|
+
this.isColdStart = true;
|
|
20
|
+
this.optimizer = cold_start_optimizer_1.ColdStartOptimizer.getInstance();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Initialize the HazelJS application
|
|
24
|
+
*/
|
|
25
|
+
async initialize() {
|
|
26
|
+
if (this.app) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
core_2.default.info('Initializing HazelJS application for Cloud Functions...');
|
|
31
|
+
// Check if cold start optimization is enabled
|
|
32
|
+
const metadata = (0, serverless_decorator_1.getServerlessMetadata)(this.moduleClass);
|
|
33
|
+
if (metadata?.coldStartOptimization) {
|
|
34
|
+
await this.optimizer.warmUp();
|
|
35
|
+
}
|
|
36
|
+
// Create HazelJS application
|
|
37
|
+
this.app = new core_1.HazelApp(this.moduleClass);
|
|
38
|
+
const duration = Date.now() - startTime;
|
|
39
|
+
core_2.default.info(`Cloud Function initialization completed in ${duration}ms`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create Cloud Function HTTP handler
|
|
43
|
+
*/
|
|
44
|
+
createHttpHandler() {
|
|
45
|
+
return async (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
// Log cold start
|
|
48
|
+
if (this.isColdStart) {
|
|
49
|
+
core_2.default.info('Cloud Function cold start detected');
|
|
50
|
+
this.isColdStart = false;
|
|
51
|
+
}
|
|
52
|
+
// Initialize application
|
|
53
|
+
await this.initialize();
|
|
54
|
+
// Convert Cloud Function request to internal format
|
|
55
|
+
const request = this.convertCloudFunctionRequest(req);
|
|
56
|
+
// Process request through HazelJS
|
|
57
|
+
const response = await this.processRequest(request);
|
|
58
|
+
// Send response
|
|
59
|
+
res.status(response.statusCode);
|
|
60
|
+
if (response.headers) {
|
|
61
|
+
Object.entries(response.headers).forEach(([key, value]) => {
|
|
62
|
+
res.set(key, value);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
res.send(response.body);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
core_2.default.error('Cloud Function handler error:', error);
|
|
69
|
+
res.status(500).json({
|
|
70
|
+
error: 'Internal Server Error',
|
|
71
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create Cloud Function event handler (for Pub/Sub, Storage, etc.)
|
|
78
|
+
*/
|
|
79
|
+
createEventHandler() {
|
|
80
|
+
return async (event, context) => {
|
|
81
|
+
try {
|
|
82
|
+
// Log cold start
|
|
83
|
+
if (this.isColdStart) {
|
|
84
|
+
core_2.default.info('Cloud Function event cold start detected');
|
|
85
|
+
this.isColdStart = false;
|
|
86
|
+
}
|
|
87
|
+
// Initialize application
|
|
88
|
+
await this.initialize();
|
|
89
|
+
core_2.default.info('Processing Cloud Function event:', {
|
|
90
|
+
eventType: context.eventType,
|
|
91
|
+
resource: context.resource,
|
|
92
|
+
});
|
|
93
|
+
// Process event
|
|
94
|
+
// In a real implementation, this would route to appropriate handlers
|
|
95
|
+
core_2.default.info('Event processed successfully');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
core_2.default.error('Cloud Function event handler error:', error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Convert Cloud Function request to internal format
|
|
105
|
+
*/
|
|
106
|
+
convertCloudFunctionRequest(req) {
|
|
107
|
+
return {
|
|
108
|
+
method: req.method,
|
|
109
|
+
url: req.url,
|
|
110
|
+
path: req.path,
|
|
111
|
+
headers: this.normalizeHeaders(req.headers),
|
|
112
|
+
query: this.normalizeQuery(req.query),
|
|
113
|
+
body: req.body,
|
|
114
|
+
rawBody: req.rawBody,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Normalize headers (convert string[] to string)
|
|
119
|
+
*/
|
|
120
|
+
normalizeHeaders(headers) {
|
|
121
|
+
const normalized = {};
|
|
122
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
123
|
+
normalized[key] = Array.isArray(value) ? value[0] : value;
|
|
124
|
+
}
|
|
125
|
+
return normalized;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Normalize query parameters
|
|
129
|
+
*/
|
|
130
|
+
normalizeQuery(query) {
|
|
131
|
+
const normalized = {};
|
|
132
|
+
for (const [key, value] of Object.entries(query)) {
|
|
133
|
+
normalized[key] = Array.isArray(value) ? value[0] : value;
|
|
134
|
+
}
|
|
135
|
+
return normalized;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Process request through HazelJS router
|
|
139
|
+
*/
|
|
140
|
+
async processRequest(request) {
|
|
141
|
+
if (!this.app) {
|
|
142
|
+
return {
|
|
143
|
+
statusCode: 500,
|
|
144
|
+
body: JSON.stringify({ message: 'Application not initialized' }),
|
|
145
|
+
headers: { 'Content-Type': 'application/json' },
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const router = this.app.getRouter();
|
|
149
|
+
// Build request context for the router
|
|
150
|
+
const context = {
|
|
151
|
+
method: request.method,
|
|
152
|
+
url: request.url,
|
|
153
|
+
headers: request.headers,
|
|
154
|
+
query: request.query,
|
|
155
|
+
params: {},
|
|
156
|
+
body: request.body,
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
const route = await router.match(request.method, request.url, context);
|
|
160
|
+
if (!route) {
|
|
161
|
+
return {
|
|
162
|
+
statusCode: 404,
|
|
163
|
+
body: JSON.stringify({ statusCode: 404, message: 'Route not found' }),
|
|
164
|
+
headers: { 'Content-Type': 'application/json' },
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Create a synthetic request/response to capture the handler output
|
|
168
|
+
const syntheticReq = {
|
|
169
|
+
method: request.method,
|
|
170
|
+
url: request.url,
|
|
171
|
+
headers: request.headers,
|
|
172
|
+
query: request.query,
|
|
173
|
+
params: context.params || {},
|
|
174
|
+
body: request.body,
|
|
175
|
+
};
|
|
176
|
+
let responseBody;
|
|
177
|
+
let responseStatus = 200;
|
|
178
|
+
const responseHeaders = { 'Content-Type': 'application/json' };
|
|
179
|
+
const syntheticRes = {
|
|
180
|
+
statusCode: 200,
|
|
181
|
+
status(code) { responseStatus = code; return syntheticRes; },
|
|
182
|
+
json(data) { responseBody = data; responseStatus = responseStatus || 200; },
|
|
183
|
+
send(data) { responseBody = data; },
|
|
184
|
+
setHeader(key, value) { responseHeaders[key] = value; },
|
|
185
|
+
getHeader(key) { return responseHeaders[key]; },
|
|
186
|
+
};
|
|
187
|
+
const result = await route.handler(syntheticReq, syntheticRes);
|
|
188
|
+
if (result !== undefined && responseBody === undefined) {
|
|
189
|
+
responseBody = result;
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
statusCode: responseStatus,
|
|
193
|
+
body: typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody),
|
|
194
|
+
headers: responseHeaders,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const statusCode = error.statusCode || 500;
|
|
199
|
+
const message = error instanceof Error ? error.message : 'Internal Server Error';
|
|
200
|
+
return {
|
|
201
|
+
statusCode,
|
|
202
|
+
body: JSON.stringify({ statusCode, message }),
|
|
203
|
+
headers: { 'Content-Type': 'application/json' },
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get application instance
|
|
209
|
+
*/
|
|
210
|
+
getApp() {
|
|
211
|
+
return this.app;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if this is a cold start
|
|
215
|
+
*/
|
|
216
|
+
isCold() {
|
|
217
|
+
return this.isColdStart;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.CloudFunctionAdapter = CloudFunctionAdapter;
|
|
221
|
+
/**
|
|
222
|
+
* Create a Cloud Function HTTP handler for a HazelJS module
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* // index.ts
|
|
227
|
+
* import { createCloudFunctionHandler } from '@hazeljs/core';
|
|
228
|
+
* import { AppModule } from './app.module';
|
|
229
|
+
*
|
|
230
|
+
* export const handler = createCloudFunctionHandler(AppModule);
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
function createCloudFunctionHandler(moduleClass) {
|
|
234
|
+
const adapter = new CloudFunctionAdapter(moduleClass);
|
|
235
|
+
return adapter.createHttpHandler();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Create a Cloud Function event handler for a HazelJS module
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* // index.ts
|
|
243
|
+
* import { createCloudFunctionEventHandler } from '@hazeljs/core';
|
|
244
|
+
* import { AppModule } from './app.module';
|
|
245
|
+
*
|
|
246
|
+
* export const handler = createCloudFunctionEventHandler(AppModule);
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
function createCloudFunctionEventHandler(moduleClass) {
|
|
250
|
+
const adapter = new CloudFunctionAdapter(moduleClass);
|
|
251
|
+
return adapter.createEventHandler();
|
|
252
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cold start optimization strategies
|
|
3
|
+
*/
|
|
4
|
+
export declare class ColdStartOptimizer {
|
|
5
|
+
private static instance;
|
|
6
|
+
private isWarmedUp;
|
|
7
|
+
private warmupTimestamp?;
|
|
8
|
+
private preloadedModules;
|
|
9
|
+
private constructor();
|
|
10
|
+
static getInstance(): ColdStartOptimizer;
|
|
11
|
+
/**
|
|
12
|
+
* Warm up the application
|
|
13
|
+
*/
|
|
14
|
+
warmUp(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Pre-initialize the DI container
|
|
17
|
+
*/
|
|
18
|
+
private preInitializeContainer;
|
|
19
|
+
/**
|
|
20
|
+
* Preload critical modules
|
|
21
|
+
*/
|
|
22
|
+
private preloadCriticalModules;
|
|
23
|
+
/**
|
|
24
|
+
* Setup connection pools
|
|
25
|
+
*/
|
|
26
|
+
private setupConnectionPools;
|
|
27
|
+
/**
|
|
28
|
+
* Check if the application is warmed up
|
|
29
|
+
*/
|
|
30
|
+
isWarm(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get warmup timestamp
|
|
33
|
+
*/
|
|
34
|
+
getWarmupTimestamp(): number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Get warmup duration
|
|
37
|
+
*/
|
|
38
|
+
getWarmupDuration(): number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Reset warmup state (for testing)
|
|
41
|
+
*/
|
|
42
|
+
reset(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Get preloaded modules
|
|
45
|
+
*/
|
|
46
|
+
getPreloadedModules(): string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decorator to enable cold start optimization for a method
|
|
50
|
+
*/
|
|
51
|
+
export declare function OptimizeColdStart(): MethodDecorator;
|
|
52
|
+
/**
|
|
53
|
+
* Keep-alive helper to prevent cold starts
|
|
54
|
+
*/
|
|
55
|
+
export declare class KeepAliveHelper {
|
|
56
|
+
private intervalId?;
|
|
57
|
+
private pingUrl?;
|
|
58
|
+
/**
|
|
59
|
+
* Start keep-alive pings
|
|
60
|
+
*/
|
|
61
|
+
start(url: string, intervalMs?: number): void;
|
|
62
|
+
/**
|
|
63
|
+
* Stop keep-alive pings
|
|
64
|
+
*/
|
|
65
|
+
stop(): void;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=cold-start.optimizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cold-start.optimizer.d.ts","sourceRoot":"","sources":["../src/cold-start.optimizer.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAqB;IAC5C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,kBAAkB;IAOxC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B7B;;OAEG;YACW,sBAAsB;IAOpC;;OAEG;YACW,sBAAsB;IAgBpC;;OAEG;YACW,oBAAoB;IAMlC;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,kBAAkB,IAAI,MAAM,GAAG,SAAS;IAIxC;;OAEG;IACH,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAKvC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,mBAAmB,IAAI,MAAM,EAAE;CAGhC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,eAAe,CAgBnD;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,MAAsB,GAAG,IAAI;IAgB5D;;OAEG;IACH,IAAI,IAAI,IAAI;CAOb"}
|
|
@@ -0,0 +1,202 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.KeepAliveHelper = exports.ColdStartOptimizer = void 0;
|
|
40
|
+
exports.OptimizeColdStart = OptimizeColdStart;
|
|
41
|
+
const core_1 = __importDefault(require("@hazeljs/core"));
|
|
42
|
+
const core_2 = require("@hazeljs/core");
|
|
43
|
+
/**
|
|
44
|
+
* Cold start optimization strategies
|
|
45
|
+
*/
|
|
46
|
+
class ColdStartOptimizer {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.isWarmedUp = false;
|
|
49
|
+
this.preloadedModules = new Set();
|
|
50
|
+
}
|
|
51
|
+
static getInstance() {
|
|
52
|
+
if (!ColdStartOptimizer.instance) {
|
|
53
|
+
ColdStartOptimizer.instance = new ColdStartOptimizer();
|
|
54
|
+
}
|
|
55
|
+
return ColdStartOptimizer.instance;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Warm up the application
|
|
59
|
+
*/
|
|
60
|
+
async warmUp() {
|
|
61
|
+
if (this.isWarmedUp) {
|
|
62
|
+
core_1.default.debug('Application already warmed up');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const startTime = Date.now();
|
|
66
|
+
core_1.default.info('Starting cold start optimization...');
|
|
67
|
+
try {
|
|
68
|
+
// Pre-initialize container
|
|
69
|
+
await this.preInitializeContainer();
|
|
70
|
+
// Preload critical modules
|
|
71
|
+
await this.preloadCriticalModules();
|
|
72
|
+
// Setup connection pools
|
|
73
|
+
await this.setupConnectionPools();
|
|
74
|
+
this.isWarmedUp = true;
|
|
75
|
+
this.warmupTimestamp = Date.now();
|
|
76
|
+
const duration = Date.now() - startTime;
|
|
77
|
+
core_1.default.info(`Cold start optimization completed in ${duration}ms`);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
core_1.default.error('Cold start optimization failed:', error);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Pre-initialize the DI container
|
|
86
|
+
*/
|
|
87
|
+
async preInitializeContainer() {
|
|
88
|
+
core_1.default.debug('Pre-initializing DI container...');
|
|
89
|
+
core_2.Container.getInstance();
|
|
90
|
+
// Container is already initialized, just log it
|
|
91
|
+
core_1.default.debug('DI container ready');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Preload critical modules
|
|
95
|
+
*/
|
|
96
|
+
async preloadCriticalModules() {
|
|
97
|
+
core_1.default.debug('Preloading critical modules...');
|
|
98
|
+
const criticalModules = ['http', 'https', 'crypto', 'buffer'];
|
|
99
|
+
for (const moduleName of criticalModules) {
|
|
100
|
+
try {
|
|
101
|
+
await Promise.resolve(`${moduleName}`).then(s => __importStar(require(s)));
|
|
102
|
+
this.preloadedModules.add(moduleName);
|
|
103
|
+
core_1.default.debug(`Preloaded module: ${moduleName}`);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
core_1.default.warn(`Failed to preload module ${moduleName}:`, error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Setup connection pools
|
|
112
|
+
*/
|
|
113
|
+
async setupConnectionPools() {
|
|
114
|
+
core_1.default.debug('Setting up connection pools...');
|
|
115
|
+
// Connection pools would be initialized here
|
|
116
|
+
// For now, just a placeholder
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if the application is warmed up
|
|
120
|
+
*/
|
|
121
|
+
isWarm() {
|
|
122
|
+
return this.isWarmedUp;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get warmup timestamp
|
|
126
|
+
*/
|
|
127
|
+
getWarmupTimestamp() {
|
|
128
|
+
return this.warmupTimestamp;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get warmup duration
|
|
132
|
+
*/
|
|
133
|
+
getWarmupDuration() {
|
|
134
|
+
if (!this.warmupTimestamp)
|
|
135
|
+
return undefined;
|
|
136
|
+
return Date.now() - this.warmupTimestamp;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reset warmup state (for testing)
|
|
140
|
+
*/
|
|
141
|
+
reset() {
|
|
142
|
+
this.isWarmedUp = false;
|
|
143
|
+
this.warmupTimestamp = undefined;
|
|
144
|
+
this.preloadedModules.clear();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get preloaded modules
|
|
148
|
+
*/
|
|
149
|
+
getPreloadedModules() {
|
|
150
|
+
return Array.from(this.preloadedModules);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.ColdStartOptimizer = ColdStartOptimizer;
|
|
154
|
+
/**
|
|
155
|
+
* Decorator to enable cold start optimization for a method
|
|
156
|
+
*/
|
|
157
|
+
function OptimizeColdStart() {
|
|
158
|
+
return (target, propertyKey, descriptor) => {
|
|
159
|
+
const originalMethod = descriptor.value;
|
|
160
|
+
descriptor.value = async function (...args) {
|
|
161
|
+
const optimizer = ColdStartOptimizer.getInstance();
|
|
162
|
+
if (!optimizer.isWarm()) {
|
|
163
|
+
await optimizer.warmUp();
|
|
164
|
+
}
|
|
165
|
+
return originalMethod.apply(this, args);
|
|
166
|
+
};
|
|
167
|
+
return descriptor;
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Keep-alive helper to prevent cold starts
|
|
172
|
+
*/
|
|
173
|
+
class KeepAliveHelper {
|
|
174
|
+
/**
|
|
175
|
+
* Start keep-alive pings
|
|
176
|
+
*/
|
|
177
|
+
start(url, intervalMs = 5 * 60 * 1000) {
|
|
178
|
+
this.pingUrl = url;
|
|
179
|
+
this.intervalId = setInterval(async () => {
|
|
180
|
+
try {
|
|
181
|
+
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
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
core_1.default.error('Keep-alive ping failed:', error);
|
|
187
|
+
}
|
|
188
|
+
}, intervalMs);
|
|
189
|
+
core_1.default.info(`Keep-alive started for ${url} (interval: ${intervalMs}ms)`);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Stop keep-alive pings
|
|
193
|
+
*/
|
|
194
|
+
stop() {
|
|
195
|
+
if (this.intervalId) {
|
|
196
|
+
clearInterval(this.intervalId);
|
|
197
|
+
this.intervalId = undefined;
|
|
198
|
+
core_1.default.info('Keep-alive stopped');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.KeepAliveHelper = KeepAliveHelper;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hazeljs/serverless - Serverless adapters for HazelJS
|
|
3
|
+
*/
|
|
4
|
+
export { Serverless, getServerlessMetadata, isServerless, type ServerlessOptions, type ServerlessHandler, type ServerlessContext, type ServerlessEvent, type ServerlessResponse, } from './serverless.decorator';
|
|
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';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hazeljs/serverless - Serverless adapters for HazelJS
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createCloudFunctionEventHandler = exports.createCloudFunctionHandler = exports.CloudFunctionAdapter = exports.createLambdaHandler = exports.LambdaAdapter = exports.KeepAliveHelper = exports.OptimizeColdStart = exports.ColdStartOptimizer = exports.isServerless = exports.getServerlessMetadata = exports.Serverless = void 0;
|
|
7
|
+
var serverless_decorator_1 = require("./serverless.decorator");
|
|
8
|
+
Object.defineProperty(exports, "Serverless", { enumerable: true, get: function () { return serverless_decorator_1.Serverless; } });
|
|
9
|
+
Object.defineProperty(exports, "getServerlessMetadata", { enumerable: true, get: function () { return serverless_decorator_1.getServerlessMetadata; } });
|
|
10
|
+
Object.defineProperty(exports, "isServerless", { enumerable: true, get: function () { return serverless_decorator_1.isServerless; } });
|
|
11
|
+
var cold_start_optimizer_1 = require("./cold-start.optimizer");
|
|
12
|
+
Object.defineProperty(exports, "ColdStartOptimizer", { enumerable: true, get: function () { return cold_start_optimizer_1.ColdStartOptimizer; } });
|
|
13
|
+
Object.defineProperty(exports, "OptimizeColdStart", { enumerable: true, get: function () { return cold_start_optimizer_1.OptimizeColdStart; } });
|
|
14
|
+
Object.defineProperty(exports, "KeepAliveHelper", { enumerable: true, get: function () { return cold_start_optimizer_1.KeepAliveHelper; } });
|
|
15
|
+
var lambda_adapter_1 = require("./lambda.adapter");
|
|
16
|
+
Object.defineProperty(exports, "LambdaAdapter", { enumerable: true, get: function () { return lambda_adapter_1.LambdaAdapter; } });
|
|
17
|
+
Object.defineProperty(exports, "createLambdaHandler", { enumerable: true, get: function () { return lambda_adapter_1.createLambdaHandler; } });
|
|
18
|
+
var cloud_function_adapter_1 = require("./cloud-function.adapter");
|
|
19
|
+
Object.defineProperty(exports, "CloudFunctionAdapter", { enumerable: true, get: function () { return cloud_function_adapter_1.CloudFunctionAdapter; } });
|
|
20
|
+
Object.defineProperty(exports, "createCloudFunctionHandler", { enumerable: true, get: function () { return cloud_function_adapter_1.createCloudFunctionHandler; } });
|
|
21
|
+
Object.defineProperty(exports, "createCloudFunctionEventHandler", { enumerable: true, get: function () { return cloud_function_adapter_1.createCloudFunctionEventHandler; } });
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { HazelApp } from '@hazeljs/core';
|
|
2
|
+
import { Type } from '@hazeljs/core';
|
|
3
|
+
import { ServerlessEvent, ServerlessResponse, ServerlessContext } from './serverless.decorator';
|
|
4
|
+
/**
|
|
5
|
+
* AWS Lambda event types
|
|
6
|
+
*/
|
|
7
|
+
export interface LambdaEvent extends ServerlessEvent {
|
|
8
|
+
resource?: string;
|
|
9
|
+
pathParameters?: Record<string, string>;
|
|
10
|
+
stageVariables?: Record<string, string>;
|
|
11
|
+
requestContext?: {
|
|
12
|
+
accountId: string;
|
|
13
|
+
apiId: string;
|
|
14
|
+
protocol: string;
|
|
15
|
+
httpMethod: string;
|
|
16
|
+
path: string;
|
|
17
|
+
stage: string;
|
|
18
|
+
requestId: string;
|
|
19
|
+
requestTime: string;
|
|
20
|
+
requestTimeEpoch: number;
|
|
21
|
+
identity: {
|
|
22
|
+
sourceIp: string;
|
|
23
|
+
userAgent: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* AWS Lambda context
|
|
29
|
+
*/
|
|
30
|
+
export interface LambdaContext extends ServerlessContext {
|
|
31
|
+
awsRequestId: string;
|
|
32
|
+
invokedFunctionArn: string;
|
|
33
|
+
callbackWaitsForEmptyEventLoop: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lambda adapter for HazelJS
|
|
37
|
+
*/
|
|
38
|
+
export declare class LambdaAdapter {
|
|
39
|
+
private moduleClass;
|
|
40
|
+
private app?;
|
|
41
|
+
private optimizer;
|
|
42
|
+
private isColdStart;
|
|
43
|
+
constructor(moduleClass: Type<unknown>);
|
|
44
|
+
/**
|
|
45
|
+
* Initialize the HazelJS application
|
|
46
|
+
*/
|
|
47
|
+
private initialize;
|
|
48
|
+
/**
|
|
49
|
+
* Create Lambda handler
|
|
50
|
+
*/
|
|
51
|
+
createHandler(): (event: LambdaEvent, context: LambdaContext) => Promise<ServerlessResponse>;
|
|
52
|
+
/**
|
|
53
|
+
* Convert Lambda event to HTTP request format
|
|
54
|
+
*/
|
|
55
|
+
private convertLambdaEventToRequest;
|
|
56
|
+
/**
|
|
57
|
+
* Parse request body
|
|
58
|
+
*/
|
|
59
|
+
private parseBody;
|
|
60
|
+
/**
|
|
61
|
+
* Process request through HazelJS router
|
|
62
|
+
*/
|
|
63
|
+
private processRequest;
|
|
64
|
+
/**
|
|
65
|
+
* Get application instance
|
|
66
|
+
*/
|
|
67
|
+
getApp(): HazelApp | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Check if this is a cold start
|
|
70
|
+
*/
|
|
71
|
+
isCold(): boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a Lambda handler for a HazelJS module
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* // handler.ts
|
|
79
|
+
* import { createLambdaHandler } from '@hazeljs/core';
|
|
80
|
+
* import { AppModule } from './app.module';
|
|
81
|
+
*
|
|
82
|
+
* export const handler = createLambdaHandler(AppModule);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function createLambdaHandler(moduleClass: Type<unknown>): (event: LambdaEvent, context: LambdaContext) => Promise<ServerlessResponse>;
|
|
86
|
+
//# sourceMappingURL=lambda.adapter.d.ts.map
|
|
@@ -0,0 +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;IA4F5B;;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"}
|