@geek-fun/serverlessinsight
Version:
Full life cycle cross providers serverless application management for your fast-growing business.
271 lines (270 loc) • 10.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.invokeFunction = exports.runFunction = void 0;
const node_fs_1 = require("node:fs");
const node_path_1 = __importStar(require("node:path"));
const node_url_1 = require("node:url");
const node_worker_threads_1 = require("node:worker_threads");
// ============================================================================
// Worker Thread Code (runs in worker context)
// ============================================================================
const parseHandler = (handler) => {
const [handlerFile, handlerMethod] = handler.split('.');
return [handlerFile, handlerMethod];
};
const resolveHandlerPath = (codeDir, servicePath, handlerFile) => servicePath
? node_path_1.default.resolve(servicePath, codeDir, handlerFile)
: node_path_1.default.resolve(codeDir, handlerFile);
const loadHandlerModule = async (handlerPath) => {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
return require(handlerPath);
}
catch {
const fileUrl = (0, node_url_1.pathToFileURL)(handlerPath + '.js').href;
return (await Promise.resolve(`${fileUrl}`).then(s => __importStar(require(s))));
}
};
const getHandlerFunction = (handlerModule, handlerMethod, handlerPath) => {
const handlerFn = handlerModule[handlerMethod];
if (typeof handlerFn !== 'function') {
throw new Error(`Handler "${handlerMethod}" not found or is not a function in ${handlerPath}`);
}
return handlerFn;
};
const invokeHandler = (handlerFn, event, context) => new Promise((resolve, reject) => {
// Callback-style handler (3+ parameters)
if (handlerFn.length >= 3) {
try {
handlerFn(event, context, (error, result) => {
return error ? reject(error) : resolve(result);
});
}
catch (error) {
reject(error);
}
}
else {
return Promise.resolve(handlerFn(event, context)).then(resolve).catch(reject);
}
});
const createTimeoutHandler = (port, timeoutMs) => {
const timeoutId = setTimeout(() => {
port.postMessage(new Error(`Function execution timed out after ${timeoutMs}ms`));
port.close();
}, timeoutMs);
return {
timeoutId,
clearTimer: () => clearTimeout(timeoutId),
};
};
const executeHandler = async ({ event, context, port }) => {
let clearTimer;
try {
const wd = node_worker_threads_1.workerData;
const { codeDir, handler, servicePath, timeout } = wd;
const timer = createTimeoutHandler(port, timeout);
clearTimer = timer.clearTimer;
// Convert Uint8Array back to Buffer if needed (worker thread serialization converts Buffers to Uint8Arrays)
let actualEvent = event;
if (event instanceof Uint8Array && !Buffer.isBuffer(event)) {
actualEvent = Buffer.from(event);
}
// Reconstruct logger and tracing function for Aliyun FC contexts
let actualContext = context;
if (context &&
typeof context === 'object' &&
'requestId' in context &&
!('logger' in context) &&
'function' in context &&
'service' in context) {
const requestId = context.requestId;
const formatLog = (level, message) => {
const timestamp = new Date().toISOString();
console.log(`${timestamp} ${requestId} [${level}] ${message}`);
};
const baseContext = context;
actualContext = {
...baseContext,
tracing: {
...baseContext.tracing,
parseOpenTracingBaggages: () => ({}),
},
logger: {
debug: (message) => formatLog('DEBUG', message),
info: (message) => formatLog('INFO', message),
warn: (message) => formatLog('WARNING', message),
error: (message) => formatLog('ERROR', message),
log: (message) => formatLog('INFO', message),
},
};
}
const [handlerFile, handlerMethod] = parseHandler(handler);
const handlerPath = resolveHandlerPath(codeDir, servicePath, handlerFile);
const handlerModule = await loadHandlerModule(handlerPath);
const handlerFn = getHandlerFunction(handlerModule, handlerMethod, handlerPath);
const result = await invokeHandler(handlerFn, actualEvent, actualContext);
clearTimer();
port.postMessage(result);
port.close();
}
catch (error) {
if (clearTimer)
clearTimer();
port.postMessage(error instanceof Error ? error : new Error(String(error)));
port.close();
}
};
// Initialize worker thread message handler
if (!node_worker_threads_1.isMainThread) {
node_worker_threads_1.parentPort?.on('message', async (message) => {
try {
await executeHandler(message);
}
catch (error) {
message.port.postMessage(error instanceof Error ? error : new Error(String(error)));
message.port.close();
}
});
}
// ============================================================================
// Main Thread Code (functional API)
// ============================================================================
const resolveWorkerPath = () => {
const localPath = (0, node_path_1.join)(__dirname, 'functionRunner.js');
if ((0, node_fs_1.existsSync)(localPath)) {
return localPath;
}
// Fallback to dist directory
const distPath = __dirname.replace(/src\/stack\/localStack$/, 'dist/src/stack/localStack');
return (0, node_path_1.join)(distPath, 'functionRunner.js');
};
const createWorker = (funOptions, env) => {
const { codeDir, functionKey, handler, servicePath, timeout } = funOptions;
const workerPath = resolveWorkerPath();
return new node_worker_threads_1.Worker(workerPath, {
env,
workerData: {
codeDir,
functionKey,
handler,
servicePath,
timeout,
},
});
};
const createMessageHandler = (port, resolve, reject) => {
let resolved = false;
const handleMessage = (value) => {
if (resolved)
return;
resolved = true;
port.close();
return value instanceof Error ? reject(value) : resolve(value);
};
const handleError = (err) => {
if (resolved)
return;
resolved = true;
port.close();
reject(err);
};
const handleClose = () => {
if (resolved)
return;
resolved = true;
reject(new Error('Port closed before receiving response'));
};
port.on('message', handleMessage).on('error', handleError).on('close', handleClose);
return () => {
port.off('message', handleMessage);
port.off('error', handleError);
port.off('close', handleClose);
port.close();
};
};
const sendMessage = (worker, event, context, port2) => {
worker.postMessage({
context,
event,
port: port2,
}, [port2]);
};
const runFunction = (funOptions, env) => {
const worker = createWorker(funOptions, env);
const execute = (event, context) => new Promise((resolve, reject) => {
const { port1, port2 } = new node_worker_threads_1.MessageChannel();
const cleanup = createMessageHandler(port1, resolve, reject);
// Handle worker errors/exit
const handleWorkerError = (err) => {
cleanup();
reject(err);
};
const handleWorkerExit = (code) => {
if (code !== 0) {
cleanup();
reject(new Error(`Worker stopped with exit code ${code}`));
}
};
worker.once('error', handleWorkerError);
worker.once('exit', handleWorkerExit);
try {
sendMessage(worker, event, context, port2);
}
catch (error) {
worker.off('error', handleWorkerError);
worker.off('exit', handleWorkerExit);
cleanup();
reject(error instanceof Error ? error : new Error(String(error)));
}
});
const terminate = () => worker.terminate();
return {
execute,
terminate,
};
};
exports.runFunction = runFunction;
const invokeFunction = async (funOptions, env, event, context) => {
const runner = (0, exports.runFunction)(funOptions, env);
try {
return await runner.execute(event, context);
}
finally {
await runner.terminate();
}
};
exports.invokeFunction = invokeFunction;