Created
May 3, 2023 02:29
-
-
Save astuyve/d6052a696658214de98f7ebe91daf0bd to your computer and use it in GitHub Desktop.
AWS Lambda NodeJS 18 runtime extracted from image.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Copyright 2019,2020,2021,2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ | |
// node_modules/lambda-runtime/dist/node16/index.mjs // AJ - I double checked, this was pulled from `public.ecr.aws/lambda/nodejs:18`, AWS did not change this comment ¯\_(ツ)_/¯ | |
import { createRequire } from "module"; | |
var require2 = createRequire(import.meta.url); | |
var __defProp = Object.defineProperty; | |
var __defProps = Object.defineProperties; | |
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; | |
var __getOwnPropNames = Object.getOwnPropertyNames; | |
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | |
var __hasOwnProp = Object.prototype.hasOwnProperty; | |
var __propIsEnum = Object.prototype.propertyIsEnumerable; | |
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | |
var __spreadValues = (a, b) => { | |
for (var prop in b || (b = {})) | |
if (__hasOwnProp.call(b, prop)) | |
__defNormalProp(a, prop, b[prop]); | |
if (__getOwnPropSymbols) | |
for (var prop of __getOwnPropSymbols(b)) { | |
if (__propIsEnum.call(b, prop)) | |
__defNormalProp(a, prop, b[prop]); | |
} | |
return a; | |
}; | |
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); | |
var __require = /* @__PURE__ */ ((x) => typeof require2 !== "undefined" ? require2 : typeof Proxy !== "undefined" ? new Proxy(x, { | |
get: (a, b) => (typeof require2 !== "undefined" ? require2 : a)[b] | |
}) : x)(function(x) { | |
if (typeof require2 !== "undefined") | |
return require2.apply(this, arguments); | |
throw new Error('Dynamic require of "' + x + '" is not supported'); | |
}); | |
var __commonJS = (cb, mod) => function __require2() { | |
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; | |
}; | |
var require_Errors = __commonJS({ | |
"Errors.js"(exports, module) { | |
"use strict"; | |
var util = __require("util"); | |
function _isError(obj) { | |
return obj && obj.name && obj.message && obj.stack && typeof obj.name === "string" && typeof obj.message === "string" && typeof obj.stack === "string"; | |
} | |
function intoError(err) { | |
if (err instanceof Error) { | |
return err; | |
} else { | |
return new Error(err); | |
} | |
} | |
module.exports.intoError = intoError; | |
function toRapidResponse(error) { | |
try { | |
if (util.types.isNativeError(error) || _isError(error)) { | |
return { | |
errorType: error.name, | |
errorMessage: error.message, | |
trace: error.stack.split("\n") | |
}; | |
} else { | |
return { | |
errorType: typeof error, | |
errorMessage: error.toString(), | |
trace: [] | |
}; | |
} | |
} catch (_err) { | |
return { | |
errorType: "handled", | |
errorMessage: "callback called with Error argument, but there was a problem while retrieving one or more of its message, name, and stack" | |
}; | |
} | |
} | |
module.exports.toRapidResponse = toRapidResponse; | |
module.exports.toFormatted = (error) => { | |
try { | |
return " " + JSON.stringify(error, (_k, v) => _withEnumerableProperties(v)); | |
} catch (err) { | |
return " " + JSON.stringify(toRapidResponse(error)); | |
} | |
}; | |
function _withEnumerableProperties(error) { | |
if (error instanceof Error) { | |
let ret = Object.assign({ | |
errorType: error.name, | |
errorMessage: error.message, | |
code: error.code | |
}, error); | |
if (typeof error.stack == "string") { | |
ret.stack = error.stack.split("\n"); | |
} | |
return ret; | |
} else { | |
return error; | |
} | |
} | |
var errorClasses = [ | |
class ImportModuleError extends Error { | |
}, | |
class HandlerNotFound extends Error { | |
}, | |
class MalformedHandlerName extends Error { | |
}, | |
class UserCodeSyntaxError extends Error { | |
}, | |
class MalformedStreamingHandler extends Error { | |
}, | |
class InvalidStreamingOperation extends Error { | |
}, | |
class UnhandledPromiseRejection extends Error { | |
constructor(reason, promise) { | |
super(reason); | |
this.reason = reason; | |
this.promise = promise; | |
} | |
} | |
]; | |
errorClasses.forEach((e) => { | |
module.exports[e.name] = e; | |
e.prototype.name = `Runtime.${e.name}`; | |
}); | |
} | |
}); | |
var require_XRayError = __commonJS({ | |
"XRayError.js"(exports, module) { | |
"use strict"; | |
module.exports.formatted = (err) => { | |
try { | |
return JSON.stringify(new XRayFormattedCause(err)); | |
} catch (err2) { | |
return ""; | |
} | |
}; | |
var XRayFormattedCause = class { | |
constructor(err) { | |
this.working_directory = process.cwd(); | |
let stack = []; | |
if (err.stack) { | |
let stackLines = err.stack.split("\n"); | |
stackLines.shift(); | |
stackLines.forEach((stackLine) => { | |
let line = stackLine.trim().replace(/\(|\)/g, ""); | |
line = line.substring(line.indexOf(" ") + 1); | |
let label = line.lastIndexOf(" ") >= 0 ? line.slice(0, line.lastIndexOf(" ")) : null; | |
let path = label == void 0 || label == null || label.length === 0 ? line : line.slice(line.lastIndexOf(" ") + 1); | |
path = path.split(":"); | |
let entry = { | |
path: path[0], | |
line: parseInt(path[1]), | |
label: label || "anonymous" | |
}; | |
stack.push(entry); | |
}); | |
} | |
this.exceptions = [ | |
{ | |
type: err.name, | |
message: err.message, | |
stack | |
} | |
]; | |
let paths = /* @__PURE__ */ new Set(); | |
stack.forEach((entry) => { | |
paths.add(entry.path); | |
}); | |
this.paths = Array.from(paths); | |
} | |
}; | |
} | |
}); | |
var require_VerboseLog = __commonJS({ | |
"VerboseLog.js"(exports) { | |
"use strict"; | |
var EnvVarName = "AWS_LAMBDA_RUNTIME_VERBOSE"; | |
var Tag = "RUNTIME"; | |
var Verbosity = (() => { | |
if (!process.env[EnvVarName]) { | |
return 0; | |
} | |
try { | |
const verbosity = parseInt(process.env[EnvVarName]); | |
return verbosity < 0 ? 0 : verbosity > 3 ? 3 : verbosity; | |
} catch (_) { | |
return 0; | |
} | |
})(); | |
exports.logger = function(category) { | |
return { | |
verbose: function() { | |
if (Verbosity >= 1) { | |
const args = [...arguments].map((arg) => typeof arg === "function" ? arg() : arg); | |
console.log.apply(null, [Tag, category, ...args]); | |
} | |
}, | |
vverbose: function() { | |
if (Verbosity >= 2) { | |
const args = [...arguments].map((arg) => typeof arg === "function" ? arg() : arg); | |
console.log.apply(null, [Tag, category, ...args]); | |
} | |
}, | |
vvverbose: function() { | |
if (Verbosity >= 3) { | |
const args = [...arguments].map((arg) => typeof arg === "function" ? arg() : arg); | |
console.log.apply(null, [Tag, category, ...args]); | |
} | |
} | |
}; | |
}; | |
} | |
}); | |
var require_ResponseStream = __commonJS({ | |
"ResponseStream.js"(exports, module) { | |
"use strict"; | |
var { InvalidStreamingOperation, toRapidResponse } = require_Errors(); | |
var { verbose, vvverbose } = require_VerboseLog().logger("STREAM"); | |
var net = __require("net"); | |
var STATUS_READY = "ready"; | |
var STATUS_WRITE_CALLED = "write_called"; | |
var HEADER_RESPONSE_MODE = "Lambda-Runtime-Function-Response-Mode"; | |
var VALUE_STREAMING = "streaming"; | |
var TRAILER_NAME_ERROR_TYPE = "Lambda-Runtime-Function-Error-Type"; | |
var TRAILER_NAME_ERROR_BODY = "Lambda-Runtime-Function-Error-Body"; | |
var DEFAULT_CONTENT_TYPE = "application/octet-stream"; | |
var failProps = /* @__PURE__ */ new WeakMap(); | |
function addFailWeakProp(req, fn) { | |
failProps.set(req, fn); | |
} | |
function tryCallFail(req, err, callback) { | |
const fail = failProps.get(req); | |
if (typeof fail === "function") { | |
fail(err, callback); | |
return true; | |
} | |
return false; | |
} | |
function writableStreamOnly(inner) { | |
const fns = [ | |
"cork", | |
"destroy", | |
"end", | |
"uncork", | |
"write", | |
"addListener", | |
"emit", | |
"eventNames", | |
"getMaxListeners", | |
"listenerCount", | |
"listeners", | |
"off", | |
"on", | |
"once", | |
"prependListener", | |
"prependOnceListener", | |
"rawListeners", | |
"removeAllListeners", | |
"removeListener", | |
"setMaxListeners", | |
"setContentType" | |
]; | |
const propsReadWrite = ["destroyed", "writable", "_onBeforeFirstWrite"]; | |
const propsReadOnly = [ | |
"writableFinished", | |
"writableObjectMode", | |
"writableBuffer", | |
"writableEnded", | |
"writableNeedDrain", | |
"writableHighWaterMark", | |
"writableCorked", | |
"writableLength" | |
]; | |
const stream = Object.fromEntries(fns.map((fn) => { | |
return [fn, inner[fn].bind(inner)]; | |
})); | |
Object.defineProperties(stream, Object.fromEntries(propsReadWrite.map((prop) => [ | |
prop, | |
{ | |
get() { | |
return inner[prop]; | |
}, | |
set(value) { | |
inner[prop] = value; | |
} | |
} | |
]))); | |
Object.defineProperties(stream, Object.fromEntries(propsReadOnly.map((prop) => [ | |
prop, | |
{ | |
get() { | |
return inner[prop]; | |
} | |
} | |
]))); | |
return stream; | |
} | |
function createResponseStream(options) { | |
let status = STATUS_READY; | |
let req = void 0; | |
const headers = { | |
[HEADER_RESPONSE_MODE]: VALUE_STREAMING, | |
Trailer: [TRAILER_NAME_ERROR_TYPE, TRAILER_NAME_ERROR_BODY], | |
"Content-Type": options.contentType ? options.contentType : DEFAULT_CONTENT_TYPE | |
}; | |
let responseDoneResolve; | |
let responseDoneReject; | |
let responseDonePromise = new Promise((resolve, reject) => { | |
responseDoneResolve = resolve; | |
responseDoneReject = reject; | |
}); | |
let headersDoneResolve; | |
let headersDoneReject; | |
let headersDonePromise = new Promise((resolve, reject) => { | |
headersDoneResolve = resolve; | |
headersDoneReject = reject; | |
}); | |
const agent = options.httpOptions.agent; | |
agent.createConnection = (opts, connectionListener) => { | |
return net.createConnection(__spreadProps(__spreadValues({}, opts), { | |
highWaterMark: options?.httpOptions?.highWaterMark | |
}), connectionListener); | |
}; | |
req = options.httpOptions.http.request({ | |
http: options.httpOptions.http, | |
contentType: options.httpOptions.contentType, | |
method: options.httpOptions.method, | |
hostname: options.httpOptions.hostname, | |
port: options.httpOptions.port, | |
path: options.httpOptions.path, | |
headers, | |
agent | |
}, (res) => { | |
headersDoneResolve({ | |
statusCode: res.statusCode, | |
statusMessage: res.statusMessage, | |
headers: res.headers | |
}); | |
let buf = void 0; | |
res.on("data", (chunk) => { | |
buf = typeof buf === "undefined" ? chunk : Buffer.concat([buf, chunk]); | |
}); | |
res.on("aborted", (err) => { | |
if (responseDoneReject) { | |
responseDoneReject(err); | |
} | |
req.destroy(err); | |
}); | |
res.on("end", () => { | |
vvverbose("rapid response", buf ? buf.toString() : "buf undefined"); | |
responseDoneResolve(buf); | |
}); | |
}); | |
req.on("error", (err) => { | |
if (headersDoneReject) { | |
headersDoneReject(err); | |
} | |
if (responseDoneReject) { | |
responseDoneReject(err); | |
} | |
req.destroy(err); | |
}); | |
const origEnd = req.end.bind(req); | |
req.end = function(cb) { | |
origEnd(cb); | |
}; | |
req.setContentType = function(contentType) { | |
if (status !== STATUS_READY) { | |
throw new InvalidStreamingOperation("Cannot set content-type, too late."); | |
} | |
req.setHeader("Content-Type", contentType); | |
}; | |
const origWrite = req.write.bind(req); | |
req.write = function(chunk, encoding, callback) { | |
vvverbose("ResponseStream::write", chunk.length, "callback:", typeof callback); | |
if (typeof chunk !== "string" && !Buffer.isBuffer(chunk) && chunk?.constructor !== Uint8Array) { | |
chunk = JSON.stringify(chunk); | |
} | |
if (status === STATUS_READY && typeof this._onBeforeFirstWrite === "function") { | |
this._onBeforeFirstWrite((ch) => origWrite(ch)); | |
} | |
const ret = origWrite(chunk, encoding, callback); | |
vvverbose("ResponseStream::origWrite", ret); | |
vvverbose("ResponseStream::write outputData len", this.outputData.length); | |
vvverbose("ResponseStream::write outputSize", this.outputSize); | |
if (status === STATUS_READY) { | |
status = STATUS_WRITE_CALLED; | |
} | |
return ret; | |
}; | |
const request = writableStreamOnly(req); | |
addFailWeakProp(request, function(err, callback) { | |
verbose("ResponseStream::fail err:", err); | |
const error = toRapidResponse(err); | |
req.addTrailers({ | |
[TRAILER_NAME_ERROR_TYPE]: error.errorType, | |
[TRAILER_NAME_ERROR_BODY]: Buffer.from(JSON.stringify(error)).toString("base64") | |
}); | |
req.end(callback); | |
}); | |
return { | |
request, | |
headersDone: headersDonePromise, | |
responseDone: responseDonePromise | |
}; | |
} | |
module.exports.createResponseStream = createResponseStream; | |
module.exports.tryCallFail = tryCallFail; | |
} | |
}); | |
var require_NativeModuleLoader = __commonJS({ | |
"NativeModuleLoader.js"(exports) { | |
"use strict"; | |
exports.load = () => __require("./rapid-client.node"); | |
} | |
}); | |
var require_RAPIDClient = __commonJS({ | |
"RAPIDClient.js"(exports, module) { | |
"use strict"; | |
var Errors2 = require_Errors(); | |
var XRayError = require_XRayError(); | |
var ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type"; | |
var { createResponseStream } = require_ResponseStream(); | |
module.exports = class RAPIDClient { | |
constructor(hostnamePort, httpClient, nativeClient) { | |
this.http = httpClient || __require("http"); | |
this.nativeClient = nativeClient || require_NativeModuleLoader().load(); | |
this.useAlternativeClient = process.env["AWS_LAMBDA_NODEJS_USE_ALTERNATIVE_CLIENT_1"] === "true"; | |
let [hostname, port] = hostnamePort.split(":"); | |
this.hostname = hostname; | |
this.port = parseInt(port, 10); | |
this.agent = new this.http.Agent({ | |
keepAlive: true, | |
maxSockets: 1 | |
}); | |
} | |
postInvocationResponse(response, id, callback) { | |
let bodyString = _trySerializeResponse(response); | |
this.nativeClient.done(id, bodyString); | |
callback(); | |
} | |
getStreamForInvocationResponse(id, callback, options) { | |
const ret = createResponseStream({ | |
httpOptions: { | |
agent: this.agent, | |
http: this.http, | |
hostname: this.hostname, | |
method: "POST", | |
port: this.port, | |
path: "/2018-06-01/runtime/invocation/" + id + "/response", | |
highWaterMark: options?.highWaterMark | |
} | |
}); | |
return { | |
request: ret.request, | |
responseDone: ret.responseDone.then((_) => { | |
if (callback) { | |
callback(); | |
} | |
}) | |
}; | |
} | |
postInitError(error, callback) { | |
let response = Errors2.toRapidResponse(error); | |
this._post(`/2018-06-01/runtime/init/error`, response, { [ERROR_TYPE_HEADER]: response.errorType }, callback); | |
} | |
postInvocationError(error, id, callback) { | |
let response = Errors2.toRapidResponse(error); | |
let bodyString = _trySerializeResponse(response); | |
let xrayString = XRayError.formatted(error); | |
this.nativeClient.error(id, bodyString, xrayString); | |
callback(); | |
} | |
async nextInvocation() { | |
if (this.useAlternativeClient) { | |
const options = { | |
hostname: this.hostname, | |
port: this.port, | |
path: "/2018-06-01/runtime/invocation/next", | |
method: "GET", | |
agent: this.agent | |
}; | |
return new Promise((resolve, reject) => { | |
let request = this.http.request(options, (response) => { | |
let data = ""; | |
response.setEncoding("utf-8").on("data", (chunk) => { | |
data += chunk; | |
}).on("end", () => { | |
resolve({ | |
bodyJson: data, | |
headers: response.headers | |
}); | |
}); | |
}); | |
request.on("error", (e) => { | |
reject(e); | |
}).end(); | |
}); | |
} | |
return this.nativeClient.next(); | |
} | |
_post(path, body, headers, callback) { | |
let bodyString = _trySerializeResponse(body); | |
const options = { | |
hostname: this.hostname, | |
port: this.port, | |
path, | |
method: "POST", | |
headers: Object.assign({ | |
"Content-Type": "application/json", | |
"Content-Length": Buffer.from(bodyString).length | |
}, headers || {}), | |
agent: this.agent | |
}; | |
let request = this.http.request(options, (response) => { | |
response.on("end", () => { | |
callback(); | |
}).on("error", (e) => { | |
throw e; | |
}).on("data", () => { | |
}); | |
}); | |
request.on("error", (e) => { | |
throw e; | |
}).end(bodyString, "utf-8"); | |
} | |
}; | |
function _trySerializeResponse(body) { | |
try { | |
return JSON.stringify(body === void 0 ? null : body); | |
} catch (err) { | |
throw new Error("Unable to stringify response body"); | |
} | |
} | |
} | |
}); | |
var require_LogPatch = __commonJS({ | |
"LogPatch.js"(exports, module) { | |
"use strict"; | |
var util = __require("util"); | |
var fs = __require("fs"); | |
var levels = Object.freeze({ | |
INFO: { name: "INFO" }, | |
DEBUG: { name: "DEBUG" }, | |
WARN: { name: "WARN" }, | |
ERROR: { name: "ERROR" }, | |
TRACE: { name: "TRACE" }, | |
FATAL: { name: "FATAL" } | |
}); | |
var REQUEST_ID_SYMBOL = Symbol.for("aws.lambda.runtime.requestId"); | |
var _currentRequestId = { | |
get: () => global[REQUEST_ID_SYMBOL], | |
set: (id) => global[REQUEST_ID_SYMBOL] = id | |
}; | |
var _logToStdout = (level, message) => { | |
let time = new Date().toISOString(); | |
let requestId = _currentRequestId.get(); | |
let line = `${time} ${requestId} ${level.name} ${message}`; | |
line = line.replace(/\n/g, "\r"); | |
process.stdout.write(line + "\n"); | |
}; | |
var _logToFd = function(logTarget) { | |
let typeAndLength = Buffer.alloc(16); | |
typeAndLength.writeUInt32BE(2774138883, 0); | |
return (level, message) => { | |
let date = new Date(); | |
let time = date.toISOString(); | |
let requestId = _currentRequestId.get(); | |
let enrichedMessage = `${time} ${requestId} ${level.name} ${message} | |
`; | |
let messageBytes = Buffer.from(enrichedMessage, "utf8"); | |
typeAndLength.writeInt32BE(messageBytes.length, 4); | |
typeAndLength.writeBigInt64BE(BigInt(date.valueOf()) * 1000n, 8); | |
fs.writeSync(logTarget, typeAndLength); | |
fs.writeSync(logTarget, messageBytes); | |
}; | |
}; | |
function _patchConsoleWith(log) { | |
console.log = (msg, ...params) => { | |
log(levels.INFO, util.format(msg, ...params)); | |
}; | |
console.debug = (msg, ...params) => { | |
log(levels.DEBUG, util.format(msg, ...params)); | |
}; | |
console.info = (msg, ...params) => { | |
log(levels.INFO, util.format(msg, ...params)); | |
}; | |
console.warn = (msg, ...params) => { | |
log(levels.WARN, util.format(msg, ...params)); | |
}; | |
console.error = (msg, ...params) => { | |
log(levels.ERROR, util.format(msg, ...params)); | |
}; | |
console.trace = (msg, ...params) => { | |
log(levels.TRACE, util.format(msg, ...params)); | |
}; | |
console.fatal = (msg, ...params) => { | |
log(levels.FATAL, util.format(msg, ...params)); | |
}; | |
} | |
var _patchConsole = () => { | |
if (process.env["_LAMBDA_TELEMETRY_LOG_FD"] != null && process.env["_LAMBDA_TELEMETRY_LOG_FD"] != void 0) { | |
let logFd = parseInt(process.env["_LAMBDA_TELEMETRY_LOG_FD"]); | |
_patchConsoleWith(_logToFd(logFd)); | |
delete process.env["_LAMBDA_TELEMETRY_LOG_FD"]; | |
} else { | |
_patchConsoleWith(_logToStdout); | |
} | |
}; | |
module.exports = { | |
setCurrentRequestId: _currentRequestId.set, | |
patchConsole: _patchConsole | |
}; | |
} | |
}); | |
var require_InvokeContext = __commonJS({ | |
"InvokeContext.js"(exports, module) { | |
"use strict"; | |
var assert = __require("assert").strict; | |
var { setCurrentRequestId } = require_LogPatch(); | |
var INVOKE_HEADER = { | |
ClientContext: "lambda-runtime-client-context", | |
CognitoIdentity: "lambda-runtime-cognito-identity", | |
ARN: "lambda-runtime-invoked-function-arn", | |
AWSRequestId: "lambda-runtime-aws-request-id", | |
DeadlineMs: "lambda-runtime-deadline-ms", | |
XRayTrace: "lambda-runtime-trace-id" | |
}; | |
module.exports = class InvokeContext { | |
constructor(headers) { | |
this.headers = _enforceLowercaseKeys(headers); | |
} | |
get invokeId() { | |
let id = this.headers[INVOKE_HEADER.AWSRequestId]; | |
assert.ok(id, "invocation id is missing or invalid"); | |
return id; | |
} | |
updateLoggingContext() { | |
setCurrentRequestId(this.invokeId); | |
} | |
attachEnvironmentData(callbackContext) { | |
this._forwardXRay(); | |
return Object.assign(callbackContext, this._environmentalData(), this._headerData()); | |
} | |
_environmentalData() { | |
return { | |
functionVersion: process.env["AWS_LAMBDA_FUNCTION_VERSION"], | |
functionName: process.env["AWS_LAMBDA_FUNCTION_NAME"], | |
memoryLimitInMB: process.env["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"], | |
logGroupName: process.env["AWS_LAMBDA_LOG_GROUP_NAME"], | |
logStreamName: process.env["AWS_LAMBDA_LOG_STREAM_NAME"] | |
}; | |
} | |
_headerData() { | |
const deadline = this.headers[INVOKE_HEADER.DeadlineMs]; | |
return { | |
clientContext: _parseJson(this.headers[INVOKE_HEADER.ClientContext], "ClientContext"), | |
identity: _parseJson(this.headers[INVOKE_HEADER.CognitoIdentity], "CognitoIdentity"), | |
invokedFunctionArn: this.headers[INVOKE_HEADER.ARN], | |
awsRequestId: this.headers[INVOKE_HEADER.AWSRequestId], | |
getRemainingTimeInMillis: function() { | |
return deadline - Date.now(); | |
} | |
}; | |
} | |
_forwardXRay() { | |
if (this.headers[INVOKE_HEADER.XRayTrace]) { | |
process.env["_X_AMZN_TRACE_ID"] = this.headers[INVOKE_HEADER.XRayTrace]; | |
} else { | |
delete process.env["_X_AMZN_TRACE_ID"]; | |
} | |
} | |
}; | |
function _parseJson(jsonString, name) { | |
if (jsonString !== void 0) { | |
try { | |
return JSON.parse(jsonString); | |
} catch (err) { | |
throw new Error(`Cannot parse ${name} as json: ${err.toString()}`); | |
} | |
} else { | |
return void 0; | |
} | |
} | |
function _enforceLowercaseKeys(original) { | |
return Object.keys(original).reduce((enforced, originalKey) => { | |
enforced[originalKey.toLowerCase()] = original[originalKey]; | |
return enforced; | |
}, {}); | |
} | |
} | |
}); | |
var require_BeforeExitListener = __commonJS({ | |
"BeforeExitListener.js"(exports, module) { | |
"use strict"; | |
var LISTENER_SYMBOL = Symbol.for("aws.lambda.beforeExit"); | |
var NO_OP_LISTENER = () => { | |
}; | |
module.exports = { | |
invoke: () => global[LISTENER_SYMBOL](), | |
reset: () => global[LISTENER_SYMBOL] = NO_OP_LISTENER, | |
set: (listener) => global[LISTENER_SYMBOL] = listener | |
}; | |
} | |
}); | |
var require_CallbackContext = __commonJS({ | |
"CallbackContext.js"(exports, module) { | |
"use strict"; | |
var BeforeExitListener2 = require_BeforeExitListener(); | |
var { toFormatted, intoError } = require_Errors(); | |
function _rawCallbackContext(client, id, scheduleNext) { | |
const postError = (err, callback2) => { | |
console.error("Invoke Error", toFormatted(intoError(err))); | |
client.postInvocationError(err, id, callback2); | |
}; | |
let isCompleteInvoked = false; | |
const complete = (result, callback2) => { | |
if (isCompleteInvoked) { | |
console.error("Invocation has already been reported as done. Cannot call complete more than once per invocation."); | |
return; | |
} | |
isCompleteInvoked = true; | |
client.postInvocationResponse(result, id, callback2); | |
}; | |
let waitForEmptyEventLoop = true; | |
const callback = function(err, result) { | |
BeforeExitListener2.reset(); | |
if (err !== void 0 && err !== null) { | |
postError(err, scheduleNext); | |
} else { | |
if (!waitForEmptyEventLoop) { | |
complete(result, scheduleNext); | |
} else { | |
BeforeExitListener2.set(() => { | |
setImmediate(() => { | |
complete(result, scheduleNext); | |
}); | |
}); | |
} | |
} | |
}; | |
const done = (err, result) => { | |
BeforeExitListener2.reset(); | |
if (err !== void 0 && err !== null) { | |
postError(err, scheduleNext); | |
} else { | |
complete(result, scheduleNext); | |
} | |
}; | |
const succeed = (result) => { | |
done(null, result); | |
}; | |
const fail = (err) => { | |
if (err === void 0 || err === null) { | |
done("handled"); | |
} else { | |
done(err, null); | |
} | |
}; | |
const callbackContext = { | |
get callbackWaitsForEmptyEventLoop() { | |
return waitForEmptyEventLoop; | |
}, | |
set callbackWaitsForEmptyEventLoop(value) { | |
waitForEmptyEventLoop = value; | |
}, | |
succeed, | |
fail, | |
done | |
}; | |
return [ | |
callback, | |
callbackContext, | |
function() { | |
isCompleteInvoked = true; | |
} | |
]; | |
} | |
function _wrappedCallbackContext(callback, callbackContext, markCompleted) { | |
let finished = false; | |
const onlyAllowFirstCall = function(toWrap) { | |
return function() { | |
if (!finished) { | |
toWrap.apply(null, arguments); | |
finished = true; | |
} | |
}; | |
}; | |
callbackContext.succeed = onlyAllowFirstCall(callbackContext.succeed); | |
callbackContext.fail = onlyAllowFirstCall(callbackContext.fail); | |
callbackContext.done = onlyAllowFirstCall(callbackContext.done); | |
return [onlyAllowFirstCall(callback), callbackContext, markCompleted]; | |
} | |
module.exports.build = function(client, id, scheduleNext) { | |
let rawCallbackContext = _rawCallbackContext(client, id, scheduleNext); | |
return _wrappedCallbackContext(...rawCallbackContext); | |
}; | |
} | |
}); | |
var require_StreamingContext = __commonJS({ | |
"StreamingContext.js"(exports, module) { | |
"use strict"; | |
var BeforeExitListener2 = require_BeforeExitListener(); | |
var { | |
InvalidStreamingOperation, | |
toFormatted, | |
intoError | |
} = require_Errors(); | |
var { verbose, vverbose } = require_VerboseLog().logger("STREAM"); | |
var { tryCallFail } = require_ResponseStream(); | |
module.exports.build = function(client, id, scheduleNext, options) { | |
let waitForEmptyEventLoop = true; | |
const scheduleNextNow = () => { | |
verbose("StreamingContext::scheduleNextNow entered"); | |
if (!waitForEmptyEventLoop) { | |
scheduleNext(); | |
} else { | |
BeforeExitListener2.set(() => { | |
setImmediate(() => { | |
scheduleNext(); | |
}); | |
}); | |
} | |
}; | |
let isStreamCreated = false; | |
const streamingContext = { | |
get callbackWaitsForEmptyEventLoop() { | |
return waitForEmptyEventLoop; | |
}, | |
set callbackWaitsForEmptyEventLoop(value) { | |
waitForEmptyEventLoop = value; | |
}, | |
createStream: (callback) => { | |
if (isStreamCreated) { | |
throw new InvalidStreamingOperation("Cannot create stream for the same StreamingContext more than once."); | |
} | |
const { request: responseStream, responseDone: rapidResponse } = client.getStreamForInvocationResponse(id, callback, options); | |
isStreamCreated = true; | |
vverbose("StreamingContext::createStream stream created"); | |
return { | |
fail: (err, callback2) => { | |
console.error("Invoke Error", toFormatted(intoError(err))); | |
tryCallFail(responseStream, err, callback2); | |
}, | |
responseStream, | |
rapidResponse, | |
scheduleNext: () => { | |
verbose("StreamingContext::createStream scheduleNext"); | |
BeforeExitListener2.reset(); | |
scheduleNextNow(); | |
} | |
}; | |
} | |
}; | |
return streamingContext; | |
}; | |
} | |
}); | |
var require_HttpResponseStream = __commonJS({ | |
"HttpResponseStream.js"(exports, module) { | |
"use strict"; | |
var METADATA_PRELUDE_CONTENT_TYPE = "application/vnd.awslambda.http-integration-response"; | |
var DELIMITER_LEN = 8; | |
var HttpResponseStream = class { | |
static from(underlyingStream, prelude) { | |
underlyingStream.setContentType(METADATA_PRELUDE_CONTENT_TYPE); | |
const metadataPrelude = JSON.stringify(prelude); | |
underlyingStream._onBeforeFirstWrite = (write) => { | |
write(metadataPrelude); | |
write(new Uint8Array(DELIMITER_LEN)); | |
}; | |
return underlyingStream; | |
} | |
}; | |
module.exports.HttpResponseStream = HttpResponseStream; | |
} | |
}); | |
var require_UserFunction = __commonJS({ | |
"UserFunction.js"(exports, module) { | |
"use strict"; | |
var path = __require("path"); | |
var fs = __require("fs"); | |
var { | |
HandlerNotFound, | |
MalformedHandlerName, | |
ImportModuleError, | |
UserCodeSyntaxError | |
} = require_Errors(); | |
var { verbose } = require_VerboseLog().logger("LOADER"); | |
var { HttpResponseStream } = require_HttpResponseStream(); | |
var FUNCTION_EXPR = /^([^.]*)\.(.*)$/; | |
var RELATIVE_PATH_SUBSTRING = ".."; | |
var HANDLER_STREAMING = Symbol.for("aws.lambda.runtime.handler.streaming"); | |
var HANDLER_HIGHWATERMARK = Symbol.for("aws.lambda.runtime.handler.streaming.highWaterMark"); | |
var STREAM_RESPONSE = "response"; | |
var NoGlobalAwsLambda = process.env["AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA"] === "1" || process.env["AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA"] === "true"; | |
function _moduleRootAndHandler(fullHandlerString) { | |
let handlerString = path.basename(fullHandlerString); | |
let moduleRoot = fullHandlerString.substring(0, fullHandlerString.indexOf(handlerString)); | |
return [moduleRoot, handlerString]; | |
} | |
function _splitHandlerString(handler) { | |
let match = handler.match(FUNCTION_EXPR); | |
if (!match || match.length != 3) { | |
throw new MalformedHandlerName("Bad handler"); | |
} | |
return [match[1], match[2]]; | |
} | |
function _resolveHandler(object, nestedProperty) { | |
return nestedProperty.split(".").reduce((nested, key) => { | |
return nested && nested[key]; | |
}, object); | |
} | |
function _tryRequireFile(file, extension) { | |
const path2 = file + (extension || ""); | |
verbose("Try loading as commonjs:", path2); | |
return fs.existsSync(path2) ? __require(path2) : void 0; | |
} | |
async function _tryAwaitImport(file, extension) { | |
const path2 = file + (extension || ""); | |
verbose("Try loading as esmodule:", path2); | |
if (fs.existsSync(path2)) { | |
return await import(path2); | |
} | |
return void 0; | |
} | |
function _hasFolderPackageJsonTypeModule(folder) { | |
if (folder.endsWith("/node_modules")) { | |
return false; | |
} | |
const pj = path.join(folder, "/package.json"); | |
if (fs.existsSync(pj)) { | |
try { | |
const pkg = JSON.parse(fs.readFileSync(pj)); | |
if (pkg) { | |
if (pkg.type === "module") { | |
verbose("type: module detected in", pj); | |
return true; | |
} else { | |
verbose("type: module not detected in", pj); | |
return false; | |
} | |
} | |
} catch (e) { | |
console.warn(`${pj} cannot be read, it will be ignored for ES module detection purposes.`, e); | |
return false; | |
} | |
} | |
if (folder === "/") { | |
return false; | |
} | |
return _hasFolderPackageJsonTypeModule(path.resolve(folder, "..")); | |
} | |
function _hasPackageJsonTypeModule(file) { | |
const jsPath = file + ".js"; | |
return fs.existsSync(jsPath) ? _hasFolderPackageJsonTypeModule(path.resolve(path.dirname(jsPath))) : false; | |
} | |
async function _tryRequire(appRoot, moduleRoot, module2) { | |
verbose("Try loading as commonjs: ", module2, " with paths: ,", appRoot, moduleRoot); | |
const lambdaStylePath = path.resolve(appRoot, moduleRoot, module2); | |
const extensionless = _tryRequireFile(lambdaStylePath); | |
if (extensionless) { | |
return extensionless; | |
} | |
const pjHasModule = _hasPackageJsonTypeModule(lambdaStylePath); | |
if (!pjHasModule) { | |
const loaded2 = _tryRequireFile(lambdaStylePath, ".js"); | |
if (loaded2) { | |
return loaded2; | |
} | |
} | |
const loaded = pjHasModule && await _tryAwaitImport(lambdaStylePath, ".js") || await _tryAwaitImport(lambdaStylePath, ".mjs") || _tryRequireFile(lambdaStylePath, ".cjs"); | |
if (loaded) { | |
return loaded; | |
} | |
verbose("Try loading as commonjs: ", module2, " with path(s): ", appRoot, moduleRoot); | |
const nodeStylePath = __require.resolve(module2, { | |
paths: [appRoot, moduleRoot] | |
}); | |
return __require(nodeStylePath); | |
} | |
async function _loadUserApp(appRoot, moduleRoot, module2) { | |
if (!NoGlobalAwsLambda) { | |
globalThis.awslambda = { | |
streamifyResponse: (handler, options) => { | |
handler[HANDLER_STREAMING] = STREAM_RESPONSE; | |
if (typeof options?.highWaterMark === "number") { | |
handler[HANDLER_HIGHWATERMARK] = parseInt(options.highWaterMark); | |
} | |
return handler; | |
}, | |
HttpResponseStream | |
}; | |
} | |
try { | |
return await _tryRequire(appRoot, moduleRoot, module2); | |
} catch (e) { | |
if (e instanceof SyntaxError) { | |
throw new UserCodeSyntaxError(e); | |
} else if (e.code !== void 0 && e.code === "MODULE_NOT_FOUND") { | |
verbose("globalPaths", JSON.stringify(__require("module").globalPaths)); | |
throw new ImportModuleError(e); | |
} else { | |
throw e; | |
} | |
} | |
} | |
function _throwIfInvalidHandler(fullHandlerString) { | |
if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) { | |
throw new MalformedHandlerName(`'${fullHandlerString}' is not a valid handler name. Use absolute paths when specifying root directories in handler names.`); | |
} | |
} | |
function _isHandlerStreaming(handler) { | |
if (typeof handler[HANDLER_STREAMING] === "undefined" || handler[HANDLER_STREAMING] === null || handler[HANDLER_STREAMING] === false) { | |
return false; | |
} | |
if (handler[HANDLER_STREAMING] === STREAM_RESPONSE) { | |
return STREAM_RESPONSE; | |
} else { | |
throw new MalformedStreamingHandler("Only response streaming is supported."); | |
} | |
} | |
function _highWaterMark(handler) { | |
if (typeof handler[HANDLER_HIGHWATERMARK] === "undefined" || handler[HANDLER_HIGHWATERMARK] === null || handler[HANDLER_HIGHWATERMARK] === false) { | |
return void 0; | |
} | |
const hwm = parseInt(handler[HANDLER_HIGHWATERMARK]); | |
if (isNaN(hwm)) { | |
return void 0; | |
} | |
return hwm; | |
} | |
module.exports.load = async function(appRoot, fullHandlerString) { | |
_throwIfInvalidHandler(fullHandlerString); | |
let [moduleRoot, moduleAndHandler] = _moduleRootAndHandler(fullHandlerString); | |
let [module2, handlerPath] = _splitHandlerString(moduleAndHandler); | |
let userApp = await _loadUserApp(appRoot, moduleRoot, module2); | |
let handlerFunc = _resolveHandler(userApp, handlerPath); | |
if (!handlerFunc) { | |
throw new HandlerNotFound(`${fullHandlerString} is undefined or not exported`); | |
} | |
if (typeof handlerFunc !== "function") { | |
throw new HandlerNotFound(`${fullHandlerString} is not a function`); | |
} | |
return handlerFunc; | |
}; | |
module.exports.getHandlerMetadata = function(handlerFunc) { | |
return { | |
streaming: _isHandlerStreaming(handlerFunc), | |
highWaterMark: _highWaterMark(handlerFunc) | |
}; | |
}; | |
module.exports.STREAM_RESPONSE = STREAM_RESPONSE; | |
} | |
}); | |
var require_Runtime = __commonJS({ | |
"Runtime.js"(exports, module) { | |
"use strict"; | |
var InvokeContext = require_InvokeContext(); | |
var CallbackContext = require_CallbackContext(); | |
var StreamingContext = require_StreamingContext(); | |
var BeforeExitListener2 = require_BeforeExitListener(); | |
var { STREAM_RESPONSE } = require_UserFunction(); | |
var { verbose, vverbose } = require_VerboseLog().logger("RAPID"); | |
module.exports = class Runtime { | |
constructor(client, handler, handlerMetadata, errorCallbacks) { | |
this.client = client; | |
this.handler = handler; | |
this.handlerMetadata = handlerMetadata; | |
this.errorCallbacks = errorCallbacks; | |
this.handleOnce = handlerMetadata.streaming === STREAM_RESPONSE ? this.handleOnceStreaming : this.handleOnceNonStreaming; | |
} | |
scheduleIteration() { | |
let that = this; | |
setImmediate(() => { | |
that.handleOnce().then(() => { | |
}, (err) => { | |
console.log(`Unexpected Top Level Error: ${err.toString()}`); | |
this.errorCallbacks.uncaughtException(err); | |
}); | |
}); | |
} | |
async handleOnceNonStreaming() { | |
let { bodyJson, headers } = await this.client.nextInvocation(); | |
let invokeContext = new InvokeContext(headers); | |
invokeContext.updateLoggingContext(); | |
let [callback, callbackContext, markCompleted] = CallbackContext.build(this.client, invokeContext.invokeId, this.scheduleIteration.bind(this)); | |
try { | |
this._setErrorCallbacks(invokeContext.invokeId); | |
this._setDefaultExitListener(invokeContext.invokeId, markCompleted); | |
let result = this.handler(JSON.parse(bodyJson), invokeContext.attachEnvironmentData(callbackContext), callback); | |
if (_isPromise(result)) { | |
result.then(callbackContext.succeed, callbackContext.fail).catch(callbackContext.fail); | |
} | |
} catch (err) { | |
callback(err); | |
} | |
} | |
async handleOnceStreaming() { | |
let { bodyJson, headers } = await this.client.nextInvocation(); | |
let invokeContext = new InvokeContext(headers); | |
invokeContext.updateLoggingContext(); | |
let streamingContext = StreamingContext.build(this.client, invokeContext.invokeId, this.scheduleIteration.bind(this), this.handlerMetadata?.highWaterMark ? { highWaterMark: this.handlerMetadata.highWaterMark } : void 0); | |
const { | |
responseStream, | |
rapidResponse, | |
scheduleNext, | |
fail: ctxFail | |
} = streamingContext.createStream(); | |
delete streamingContext.createStream; | |
try { | |
this._setErrorCallbacks(invokeContext.invokeId); | |
this._setStreamingExitListener(invokeContext.invokeId, responseStream); | |
const ctx = invokeContext.attachEnvironmentData(streamingContext); | |
verbose("Runtime::handleOnceStreaming", "invoking handler"); | |
const event = JSON.parse(bodyJson); | |
const handlerResult = this.handler(event, responseStream, ctx); | |
verbose("Runtime::handleOnceStreaming", "handler returned"); | |
if (!_isPromise(handlerResult)) { | |
verbose("Runtime got non-promise response"); | |
ctxFail("Streaming does not support non-async handlers.", scheduleNext); | |
return; | |
} | |
const result = await handlerResult; | |
if (typeof result !== "undefined") { | |
console.warn("Streaming handlers ignore return values."); | |
} | |
verbose("Runtime::handleOnceStreaming result is awaited."); | |
if (rapidResponse) { | |
const res = await rapidResponse; | |
vverbose("RAPID response", res); | |
} | |
if (!responseStream.writableFinished) { | |
ctxFail("Response stream is not finished.", scheduleNext); | |
return; | |
} | |
scheduleNext(); | |
} catch (err) { | |
verbose("Runtime::handleOnceStreaming::finally stream destroyed"); | |
ctxFail(err, scheduleNext); | |
} | |
} | |
_setErrorCallbacks(invokeId) { | |
this.errorCallbacks.uncaughtException = (error) => { | |
this.client.postInvocationError(error, invokeId, () => { | |
process.exit(129); | |
}); | |
}; | |
this.errorCallbacks.unhandledRejection = (error) => { | |
this.client.postInvocationError(error, invokeId, () => { | |
process.exit(128); | |
}); | |
}; | |
} | |
_setDefaultExitListener(invokeId, markCompleted) { | |
BeforeExitListener2.set(() => { | |
markCompleted(); | |
this.client.postInvocationResponse(null, invokeId, () => this.scheduleIteration()); | |
}); | |
} | |
_setStreamingExitListener(_invokeId) { | |
BeforeExitListener2.set(() => { | |
this.scheduleIteration(); | |
}); | |
} | |
}; | |
function _isPromise(obj) { | |
return obj && obj.then && typeof obj.then === "function"; | |
} | |
} | |
}); | |
var RAPIDClient = require_RAPIDClient(); | |
var Runtime = require_Runtime(); | |
var UserFunction = require_UserFunction(); | |
var Errors = require_Errors(); | |
var BeforeExitListener = require_BeforeExitListener(); | |
var LogPatch = require_LogPatch(); | |
async function start() { | |
LogPatch.patchConsole(); | |
const client = new RAPIDClient(process.env.AWS_LAMBDA_RUNTIME_API); | |
let errorCallbacks = { | |
uncaughtException: (error) => { | |
client.postInitError(error, () => process.exit(129)); | |
}, | |
unhandledRejection: (error) => { | |
client.postInitError(error, () => process.exit(128)); | |
} | |
}; | |
process.on("uncaughtException", (error) => { | |
console.error("Uncaught Exception", Errors.toFormatted(error)); | |
errorCallbacks.uncaughtException(error); | |
}); | |
process.on("unhandledRejection", (reason, promise) => { | |
let error = new Errors.UnhandledPromiseRejection(reason, promise); | |
console.error("Unhandled Promise Rejection", Errors.toFormatted(error)); | |
errorCallbacks.unhandledRejection(error); | |
}); | |
BeforeExitListener.reset(); | |
process.on("beforeExit", BeforeExitListener.invoke); | |
const handlerFunc = await UserFunction.load(process.env.LAMBDA_TASK_ROOT, process.env._HANDLER); | |
const metadata = UserFunction.getHandlerMetadata(handlerFunc); | |
new Runtime(client, handlerFunc, metadata, errorCallbacks).scheduleIteration(); | |
} | |
// src/index.mjs | |
await start(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment