From c9b4c5d3d87318bc4bf21ec4cabc74882d08b6db Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Wed, 11 Mar 2026 12:21:26 -0600 Subject: [PATCH] feat(wrappers): convert Lambda handler exports to async for nodejs20.x+ compatibility Newer AWS Lambda Node.js runtimes no longer support the callback-style handler pattern. This wraps each handler export with wrapAsAsync() which converts the callback-style function to an async function returning a Promise, while preserving 100% of the internal callback logic unchanged. Task: ES-2929 Made-with: Cursor --- wrappers/cron.js | 5 +++-- wrappers/fanout.js | 5 +++-- wrappers/resource.js | 5 +++-- wrappers/test.js | 5 +++-- wrappers/wrapAsAsync.js | 20 ++++++++++++++++++++ 5 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 wrappers/wrapAsAsync.js diff --git a/wrappers/cron.js b/wrappers/cron.js index a85cd510..162637fc 100644 --- a/wrappers/cron.js +++ b/wrappers/cron.js @@ -1,5 +1,6 @@ "use strict"; +const wrapAsAsync = require("./wrapAsAsync"); let cachedHandler; module.exports = function(configOverride, botHandler) { @@ -100,7 +101,7 @@ module.exports = function(configOverride, botHandler) { } Object.assign(config, configOverride); - return function(event, context, callback) { + return wrapAsAsync(function(event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; context.resources = process.resources; context.botId = event.botId || botId; @@ -252,6 +253,6 @@ module.exports = function(configOverride, botHandler) { } }); } - }; + }); }; module.exports.CronWrapper = module.exports; diff --git a/wrappers/fanout.js b/wrappers/fanout.js index 1713a5cd..0e63673f 100644 --- a/wrappers/fanout.js +++ b/wrappers/fanout.js @@ -1,4 +1,5 @@ +const wrapAsAsync = require("./wrapAsAsync"); const { Lambda } = require("@aws-sdk/client-lambda"); const async = require("async"); const eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; @@ -110,7 +111,7 @@ const fanoutFactory = (handler, eventPartition, opts = {}) => { logger.log("Fanout Return", process.env.FANOUT_iid, process.env.FANOUT_icount, process.env.FANOUT_maxeid); - return (event, context, callback) => { + return wrapAsAsync((event, context, callback) => { cronData = event.__cron || {}; @@ -199,7 +200,7 @@ const fanoutFactory = (handler, eventPartition, opts = {}) => { return callback(err); }); } - }; + }); }; function callCheckpointOnResponses(leoBotCheckpoint, callback) { diff --git a/wrappers/resource.js b/wrappers/resource.js index ed7f71c5..e329ec51 100644 --- a/wrappers/resource.js +++ b/wrappers/resource.js @@ -1,4 +1,5 @@ "use strict"; +const wrapAsAsync = require("./wrapAsAsync"); let config = require("../leoConfigure.js"); module.exports = function (configOverride, botHandler) { if (!botHandler) { @@ -6,7 +7,7 @@ module.exports = function (configOverride, botHandler) { configOverride = {}; } Object.assign(config, configOverride); - return function (event, context, callback) { + return wrapAsAsync(function (event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; if (context.identity) { // Called Directly not via Api Gateway event = { @@ -120,5 +121,5 @@ module.exports = function (configOverride, botHandler) { } }); } - }; + }); }; diff --git a/wrappers/test.js b/wrappers/test.js index fb2f24e3..6645888e 100644 --- a/wrappers/test.js +++ b/wrappers/test.js @@ -1,5 +1,6 @@ "use strict"; +const wrapAsAsync = require("./wrapAsAsync"); let cachedHandler; module.exports = function(configOverride, botHandler) { @@ -47,7 +48,7 @@ module.exports = function(configOverride, botHandler) { } Object.assign(config, configOverride); - return function(event, context, callback) { + return wrapAsAsync(function(event, context, callback) { let cb = callback; callback = (err, data) => { assert.print(); @@ -97,5 +98,5 @@ module.exports = function(configOverride, botHandler) { } - }; + }); }; diff --git a/wrappers/wrapAsAsync.js b/wrappers/wrapAsAsync.js new file mode 100644 index 00000000..1af80a88 --- /dev/null +++ b/wrappers/wrapAsAsync.js @@ -0,0 +1,20 @@ +"use strict"; + +/** + * Converts a callback-style Lambda handler into an async handler that returns a Promise. + * Newer Node.js Lambda runtimes (nodejs20.x+) no longer support the callback pattern, + * so all exported handlers must be async. + * + * @param {function(event, context, callback)} handler - A callback-style Lambda handler + * @returns {function(event, context): Promise} An async Lambda handler + */ +module.exports = function wrapAsAsync(handler) { + return async function(event, context) { + return new Promise((resolve, reject) => { + handler(event, context, function(err, result) { + if (err) reject(err); + else resolve(result); + }); + }); + }; +};