UNPKG

@geek-fun/serverlessinsight

Version:

Full life cycle cross providers serverless application management for your fast-growing business.

271 lines (270 loc) 10.2 kB
"use strict"; 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;