serverless-offline 13.6.0 → 14.1.0
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/LICENSE +1 -1
- package/README.md +43 -161
- package/package.json +19 -18
- package/src/config/supportedRuntimes.js +2 -0
- package/src/events/alb/HttpServer.js +2 -0
- package/src/events/authMatchPolicyResource.js +5 -8
- package/src/events/http/HttpServer.js +26 -10
- package/src/events/http/createAuthScheme.js +2 -0
- package/src/events/http/createJWTAuthScheme.js +4 -5
- package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +2 -2
- package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +2 -2
- package/src/events/http/lambda-events/VelocityContext.js +1 -1
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/Errors.js +32 -0
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/HttpResponseStream.js +38 -0
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js +313 -334
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/VerboseLog.js +57 -0
- package/src/lambda/handler-runner/python-runner/invoke.py +6 -0
|
@@ -1,359 +1,338 @@
|
|
|
1
|
+
/* eslint-disable import/no-dynamic-require */
|
|
2
|
+
/* eslint-disable global-require */
|
|
3
|
+
/* eslint-disable no-underscore-dangle */
|
|
4
|
+
/* eslint-disable func-names */
|
|
5
|
+
/**
|
|
6
|
+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* This code was copied from:
|
|
9
|
+
* https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/UserFunction.js
|
|
10
|
+
*
|
|
11
|
+
* This module defines the functions for loading the user's code as specified
|
|
12
|
+
* in a handler string.
|
|
13
|
+
*/
|
|
14
|
+
|
|
1
15
|
"use strict"
|
|
2
16
|
|
|
17
|
+
const path = require("node:path")
|
|
3
18
|
const { pathToFileURL } = require("node:url")
|
|
19
|
+
const fs = require("node:fs")
|
|
20
|
+
const process = require("node:process")
|
|
4
21
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
errorType: typeof error,
|
|
53
|
-
errorMessage: error.toString(),
|
|
54
|
-
trace: [],
|
|
55
|
-
}
|
|
56
|
-
} catch (_err) {
|
|
57
|
-
return {
|
|
58
|
-
errorType: "handled",
|
|
59
|
-
errorMessage:
|
|
60
|
-
"callback called with Error argument, but there was a problem while retrieving one or more of its message, name, and stack",
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
module2.exports.toRapidResponse = toRapidResponse
|
|
65
|
-
module2.exports.toFormatted = (error) => {
|
|
66
|
-
try {
|
|
67
|
-
return ` ${JSON.stringify(error, (_k, v) =>
|
|
68
|
-
_withEnumerableProperties(v),
|
|
69
|
-
)}`
|
|
70
|
-
} catch (err) {
|
|
71
|
-
return ` ${JSON.stringify(toRapidResponse(error))}`
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function _withEnumerableProperties(error) {
|
|
75
|
-
if (error instanceof Error) {
|
|
76
|
-
const ret = {
|
|
77
|
-
errorType: error.name,
|
|
78
|
-
errorMessage: error.message,
|
|
79
|
-
code: error.code,
|
|
80
|
-
...error,
|
|
81
|
-
}
|
|
82
|
-
if (typeof error.stack === "string") {
|
|
83
|
-
ret.stack = error.stack.split("\n")
|
|
84
|
-
}
|
|
85
|
-
return ret
|
|
86
|
-
}
|
|
87
|
-
return error
|
|
88
|
-
}
|
|
89
|
-
const errorClasses = [
|
|
90
|
-
class ImportModuleError extends Error {},
|
|
91
|
-
class HandlerNotFound extends Error {},
|
|
92
|
-
class MalformedHandlerName extends Error {},
|
|
93
|
-
class UserCodeSyntaxError extends Error {},
|
|
94
|
-
class MalformedStreamingHandler extends Error {},
|
|
95
|
-
class InvalidStreamingOperation extends Error {},
|
|
96
|
-
class UnhandledPromiseRejection extends Error {
|
|
97
|
-
constructor(reason, promise) {
|
|
98
|
-
super(reason)
|
|
99
|
-
this.reason = reason
|
|
100
|
-
this.promise = promise
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
]
|
|
104
|
-
errorClasses.forEach((e) => {
|
|
105
|
-
module2.exports[e.name] = e
|
|
106
|
-
e.prototype.name = `Runtime.${e.name}`
|
|
107
|
-
})
|
|
108
|
-
},
|
|
109
|
-
})
|
|
110
|
-
const require_VerboseLog = __commonJS({
|
|
111
|
-
"VerboseLog.js": function (exports2) {
|
|
112
|
-
"use strict"
|
|
113
|
-
|
|
114
|
-
const EnvVarName = "AWS_LAMBDA_RUNTIME_VERBOSE"
|
|
115
|
-
const Tag = "RUNTIME"
|
|
116
|
-
const Verbosity = (() => {
|
|
117
|
-
if (!process.env[EnvVarName]) {
|
|
118
|
-
return 0
|
|
119
|
-
}
|
|
120
|
-
try {
|
|
121
|
-
const verbosity = parseInt(process.env[EnvVarName])
|
|
122
|
-
return verbosity < 0 ? 0 : verbosity > 3 ? 3 : verbosity
|
|
123
|
-
} catch (_) {
|
|
124
|
-
return 0
|
|
125
|
-
}
|
|
126
|
-
})()
|
|
127
|
-
exports2.logger = function (category) {
|
|
128
|
-
return {
|
|
129
|
-
verbose() {
|
|
130
|
-
if (Verbosity >= 1) {
|
|
131
|
-
console.log.apply(null, [Tag, category, ...arguments])
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
vverbose() {
|
|
135
|
-
if (Verbosity >= 2) {
|
|
136
|
-
console.log.apply(null, [Tag, category, ...arguments])
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
vvverbose() {
|
|
140
|
-
if (Verbosity >= 3) {
|
|
141
|
-
console.log.apply(null, [Tag, category, ...arguments])
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
})
|
|
148
|
-
const require_HttpResponseStream = __commonJS({
|
|
149
|
-
"HttpResponseStream.js": function (exports2, module2) {
|
|
150
|
-
"use strict"
|
|
151
|
-
|
|
152
|
-
const METADATA_PRELUDE_CONTENT_TYPE =
|
|
153
|
-
"application/vnd.awslambda.http-integration-response"
|
|
154
|
-
const DELIMITER_LEN = 8
|
|
155
|
-
const HttpResponseStream2 = class {
|
|
156
|
-
static from(underlyingStream, prelude) {
|
|
157
|
-
underlyingStream.setContentType(METADATA_PRELUDE_CONTENT_TYPE)
|
|
158
|
-
const metadataPrelude = JSON.stringify(prelude)
|
|
159
|
-
underlyingStream._onBeforeFirstWrite = (write) => {
|
|
160
|
-
write(metadataPrelude)
|
|
161
|
-
write(new Uint8Array(DELIMITER_LEN))
|
|
162
|
-
}
|
|
163
|
-
return underlyingStream
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
module2.exports.HttpResponseStream = HttpResponseStream2
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
const path = require("path")
|
|
170
|
-
const fs = require("fs")
|
|
171
|
-
const {
|
|
172
|
-
HandlerNotFound,
|
|
173
|
-
MalformedHandlerName,
|
|
174
|
-
ImportModuleError,
|
|
175
|
-
UserCodeSyntaxError,
|
|
176
|
-
} = require_Errors()
|
|
177
|
-
const { verbose } = require_VerboseLog().logger("LOADER")
|
|
178
|
-
const { HttpResponseStream } = require_HttpResponseStream()
|
|
179
|
-
const FUNCTION_EXPR = /^([^.]*)\.(.*)$/
|
|
180
|
-
const RELATIVE_PATH_SUBSTRING = ".."
|
|
181
|
-
const HANDLER_STREAMING = Symbol.for("aws.lambda.runtime.handler.streaming")
|
|
182
|
-
const STREAM_RESPONSE = "response"
|
|
183
|
-
const NoGlobalAwsLambda =
|
|
184
|
-
process.env.AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA === "1" ||
|
|
185
|
-
process.env.AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA === "true"
|
|
186
|
-
function _moduleRootAndHandler(fullHandlerString) {
|
|
187
|
-
const handlerString = path.basename(fullHandlerString)
|
|
188
|
-
const moduleRoot = fullHandlerString.substring(
|
|
189
|
-
0,
|
|
190
|
-
fullHandlerString.indexOf(handlerString),
|
|
191
|
-
)
|
|
192
|
-
return [moduleRoot, handlerString]
|
|
193
|
-
}
|
|
194
|
-
function _splitHandlerString(handler) {
|
|
195
|
-
const match = handler.match(FUNCTION_EXPR)
|
|
196
|
-
if (!match || match.length != 3) {
|
|
197
|
-
throw new MalformedHandlerName("Bad handler")
|
|
198
|
-
}
|
|
199
|
-
return [match[1], match[2]]
|
|
200
|
-
}
|
|
201
|
-
function _resolveHandler(object, nestedProperty) {
|
|
202
|
-
return nestedProperty.split(".").reduce((nested, key) => {
|
|
203
|
-
return nested && nested[key]
|
|
204
|
-
}, object)
|
|
22
|
+
const { require: tsxRequire } = require(`tsx/cjs/api`)
|
|
23
|
+
const {
|
|
24
|
+
HandlerNotFound,
|
|
25
|
+
MalformedHandlerName,
|
|
26
|
+
ImportModuleError,
|
|
27
|
+
UserCodeSyntaxError,
|
|
28
|
+
MalformedStreamingHandler,
|
|
29
|
+
} = require("./Errors.js")
|
|
30
|
+
const { verbose } = require("./VerboseLog.js").logger("LOADER")
|
|
31
|
+
const { HttpResponseStream } = require("./HttpResponseStream.js")
|
|
32
|
+
|
|
33
|
+
const FUNCTION_EXPR = /^([^.]*)\.(.*)$/
|
|
34
|
+
const RELATIVE_PATH_SUBSTRING = ".."
|
|
35
|
+
const HANDLER_STREAMING = Symbol.for("aws.lambda.runtime.handler.streaming")
|
|
36
|
+
const HANDLER_HIGHWATERMARK = Symbol.for(
|
|
37
|
+
"aws.lambda.runtime.handler.streaming.highWaterMark",
|
|
38
|
+
)
|
|
39
|
+
const STREAM_RESPONSE = "response"
|
|
40
|
+
|
|
41
|
+
// `awslambda.streamifyResponse` function is provided by default.
|
|
42
|
+
const NoGlobalAwsLambda =
|
|
43
|
+
process.env.AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA === "1" ||
|
|
44
|
+
process.env.AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA === "true"
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Break the full handler string into two pieces, the module root and the actual
|
|
48
|
+
* handler string.
|
|
49
|
+
* Given './somepath/something/module.nestedobj.handler' this returns
|
|
50
|
+
* ['./somepath/something', 'module.nestedobj.handler']
|
|
51
|
+
*/
|
|
52
|
+
function _moduleRootAndHandler(fullHandlerString) {
|
|
53
|
+
const handlerString = path.basename(fullHandlerString)
|
|
54
|
+
const moduleRoot = fullHandlerString.substring(
|
|
55
|
+
0,
|
|
56
|
+
fullHandlerString.indexOf(handlerString),
|
|
57
|
+
)
|
|
58
|
+
return [moduleRoot, handlerString]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Split the handler string into two pieces: the module name and the path to
|
|
63
|
+
* the handler function.
|
|
64
|
+
*/
|
|
65
|
+
function _splitHandlerString(handler) {
|
|
66
|
+
const match = handler.match(FUNCTION_EXPR)
|
|
67
|
+
if (!match || match.length !== 3) {
|
|
68
|
+
throw new MalformedHandlerName("Bad handler")
|
|
205
69
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
70
|
+
return [match[1], match[2]] // [module, function-path]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the user's handler function from the module.
|
|
75
|
+
*/
|
|
76
|
+
function _resolveHandler(object, nestedProperty) {
|
|
77
|
+
return nestedProperty.split(".").reduce((nested, key) => {
|
|
78
|
+
return nested && nested[key]
|
|
79
|
+
}, object)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function _tryRequireFile(file, extension) {
|
|
83
|
+
const pathRequireFile = file + (extension || "")
|
|
84
|
+
verbose("Try loading as commonjs:", pathRequireFile)
|
|
85
|
+
return fs.existsSync(pathRequireFile) ? require(pathRequireFile) : undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function _tryAwaitImport(file, extension) {
|
|
89
|
+
const pathAwaitImport = file + (extension || "")
|
|
90
|
+
verbose("Try loading as esmodule:", pathAwaitImport)
|
|
91
|
+
|
|
92
|
+
if (fs.existsSync(pathAwaitImport)) {
|
|
93
|
+
// eslint-disable-next-line no-return-await
|
|
94
|
+
return await import(pathToFileURL(pathAwaitImport).href)
|
|
210
95
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
96
|
+
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function _hasFolderPackageJsonTypeModule(folder) {
|
|
101
|
+
// Check if package.json exists, return true if type === "module" in package json.
|
|
102
|
+
// If there is no package.json, and there is a node_modules, return false.
|
|
103
|
+
// Check parent folder otherwise, if there is one.
|
|
104
|
+
if (folder.endsWith("/node_modules")) {
|
|
105
|
+
return false
|
|
218
106
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (pkg.type === "module") {
|
|
229
|
-
verbose(`'type: module' detected in ${pj}`)
|
|
230
|
-
return true
|
|
231
|
-
}
|
|
232
|
-
verbose(`'type: module' not detected in ${pj}`)
|
|
233
|
-
return false
|
|
107
|
+
|
|
108
|
+
const pj = path.join(folder, "/package.json")
|
|
109
|
+
if (fs.existsSync(pj)) {
|
|
110
|
+
try {
|
|
111
|
+
const pkg = JSON.parse(fs.readFileSync(pj))
|
|
112
|
+
if (pkg) {
|
|
113
|
+
if (pkg.type === "module") {
|
|
114
|
+
verbose("type: module detected in", pj)
|
|
115
|
+
return true
|
|
234
116
|
}
|
|
235
|
-
|
|
236
|
-
console.warn(
|
|
237
|
-
`${pj} cannot be read, it will be ignored for ES module detection purposes.`,
|
|
238
|
-
e,
|
|
239
|
-
)
|
|
117
|
+
verbose("type: module not detected in", pj)
|
|
240
118
|
return false
|
|
241
119
|
}
|
|
242
|
-
}
|
|
243
|
-
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// eslint-disable-next-line no-console
|
|
122
|
+
console.warn(
|
|
123
|
+
`${pj} cannot be read, it will be ignored for ES module detection purposes.`,
|
|
124
|
+
e,
|
|
125
|
+
)
|
|
244
126
|
return false
|
|
245
127
|
}
|
|
246
|
-
return _hasFolderPackageJsonTypeModule(path.resolve(folder, ".."))
|
|
247
128
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
: false
|
|
129
|
+
|
|
130
|
+
if (folder === "/") {
|
|
131
|
+
// We have reached root without finding either a package.json or a node_modules.
|
|
132
|
+
return false
|
|
253
133
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
134
|
+
|
|
135
|
+
return _hasFolderPackageJsonTypeModule(path.resolve(folder, ".."))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _hasPackageJsonTypeModule(file) {
|
|
139
|
+
// File must have a .js extension
|
|
140
|
+
const jsPath = `${file}.js`
|
|
141
|
+
return fs.existsSync(jsPath)
|
|
142
|
+
? _hasFolderPackageJsonTypeModule(path.resolve(path.dirname(jsPath)))
|
|
143
|
+
: false
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Attempt to load the user's module.
|
|
148
|
+
* Attempts to directly resolve the module relative to the application root,
|
|
149
|
+
* then falls back to the more general require().
|
|
150
|
+
*/
|
|
151
|
+
async function _tryRequire(appRoot, moduleRoot, module) {
|
|
152
|
+
verbose(
|
|
153
|
+
"Try loading as commonjs: ",
|
|
154
|
+
module,
|
|
155
|
+
" with paths: ,",
|
|
156
|
+
appRoot,
|
|
157
|
+
moduleRoot,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const lambdaStylePath = path.resolve(appRoot, moduleRoot, module)
|
|
161
|
+
|
|
162
|
+
// Extensionless files are loaded via require.
|
|
163
|
+
const extensionless = _tryRequireFile(lambdaStylePath)
|
|
164
|
+
if (extensionless) {
|
|
165
|
+
return extensionless
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// If package.json type != module, .js files are loaded via require.
|
|
169
|
+
const pjHasModule = _hasPackageJsonTypeModule(lambdaStylePath)
|
|
170
|
+
if (!pjHasModule) {
|
|
171
|
+
const loaded = _tryRequireFile(lambdaStylePath, ".js")
|
|
278
172
|
if (loaded) {
|
|
279
173
|
return loaded
|
|
280
174
|
}
|
|
281
|
-
verbose(
|
|
282
|
-
"Try loading as commonjs: ",
|
|
283
|
-
module2,
|
|
284
|
-
" with path(s): ",
|
|
285
|
-
appRoot,
|
|
286
|
-
moduleRoot,
|
|
287
|
-
)
|
|
288
|
-
const nodeStylePath = require.resolve(module2, {
|
|
289
|
-
paths: [appRoot, moduleRoot],
|
|
290
|
-
})
|
|
291
|
-
return require(nodeStylePath)
|
|
292
175
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
} catch (e) {
|
|
306
|
-
if (e instanceof SyntaxError) {
|
|
307
|
-
throw new UserCodeSyntaxError(e)
|
|
308
|
-
} else if (e.code !== void 0 && e.code === "MODULE_NOT_FOUND") {
|
|
309
|
-
verbose("globalPaths", JSON.stringify(require("module").globalPaths))
|
|
310
|
-
throw new ImportModuleError(e)
|
|
311
|
-
} else {
|
|
312
|
-
throw e
|
|
313
|
-
}
|
|
314
|
-
}
|
|
176
|
+
|
|
177
|
+
// If still not loaded, try .js, .mjs, .cjs and .ts in that order.
|
|
178
|
+
// Files ending with .js are loaded as ES modules when the nearest parent package.json
|
|
179
|
+
// file contains a top-level field "type" with a value of "module".
|
|
180
|
+
// https://nodejs.org/api/packages.html#packages_type
|
|
181
|
+
const loaded =
|
|
182
|
+
(pjHasModule && (await _tryAwaitImport(lambdaStylePath, ".js"))) ||
|
|
183
|
+
(await _tryAwaitImport(lambdaStylePath, ".mjs")) ||
|
|
184
|
+
_tryRequireFile(lambdaStylePath, ".cjs") ||
|
|
185
|
+
tsxRequire(`${lambdaStylePath}.ts`, `${lambdaStylePath}.ts`)
|
|
186
|
+
if (loaded) {
|
|
187
|
+
return loaded
|
|
315
188
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
189
|
+
|
|
190
|
+
verbose(
|
|
191
|
+
"Try loading as commonjs: ",
|
|
192
|
+
module,
|
|
193
|
+
" with path(s): ",
|
|
194
|
+
appRoot,
|
|
195
|
+
moduleRoot,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
// Why not just require(module)?
|
|
199
|
+
// Because require() is relative to __dirname, not process.cwd(). And the
|
|
200
|
+
// runtime implementation is not located in /var/task
|
|
201
|
+
// This won't work (yet) for esModules as import.meta.resolve is still experimental
|
|
202
|
+
// See: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent
|
|
203
|
+
const nodeStylePath = require.resolve(module, {
|
|
204
|
+
paths: [appRoot, moduleRoot],
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
return require(nodeStylePath)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Load the user's application or throw a descriptive error.
|
|
212
|
+
* @throws Runtime errors in two cases
|
|
213
|
+
* 1 - UserCodeSyntaxError if there's a syntax error while loading the module
|
|
214
|
+
* 2 - ImportModuleError if the module cannot be found
|
|
215
|
+
*/
|
|
216
|
+
async function _loadUserApp(appRoot, moduleRoot, module) {
|
|
217
|
+
if (!NoGlobalAwsLambda) {
|
|
218
|
+
globalThis.awslambda = {
|
|
219
|
+
HttpResponseStream,
|
|
220
|
+
streamifyResponse: (handler, options) => {
|
|
221
|
+
// eslint-disable-next-line no-param-reassign
|
|
222
|
+
handler[HANDLER_STREAMING] = STREAM_RESPONSE
|
|
223
|
+
if (typeof options?.highWaterMark === "number") {
|
|
224
|
+
// eslint-disable-next-line no-param-reassign
|
|
225
|
+
handler[HANDLER_HIGHWATERMARK] = Number.parseInt(
|
|
226
|
+
options.highWaterMark,
|
|
227
|
+
10,
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
return handler
|
|
231
|
+
},
|
|
321
232
|
}
|
|
322
233
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
return await _tryRequire(appRoot, moduleRoot, module)
|
|
237
|
+
} catch (e) {
|
|
238
|
+
if (e instanceof SyntaxError) {
|
|
239
|
+
throw new UserCodeSyntaxError(e)
|
|
240
|
+
} else if (e.code !== undefined && e.code === "MODULE_NOT_FOUND") {
|
|
241
|
+
verbose("globalPaths", JSON.stringify(require("node:module").globalPaths))
|
|
242
|
+
throw new ImportModuleError(e)
|
|
243
|
+
} else {
|
|
244
|
+
throw e
|
|
333
245
|
}
|
|
334
|
-
throw new MalformedStreamingHandler("Only response streaming is supported.")
|
|
335
246
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if (!handlerFunc) {
|
|
344
|
-
throw new HandlerNotFound(
|
|
345
|
-
`${fullHandlerString} is undefined or not exported`,
|
|
346
|
-
)
|
|
347
|
-
}
|
|
348
|
-
if (typeof handlerFunc !== "function") {
|
|
349
|
-
throw new HandlerNotFound(`${fullHandlerString} is not a function`)
|
|
350
|
-
}
|
|
351
|
-
return handlerFunc
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function _throwIfInvalidHandler(fullHandlerString) {
|
|
250
|
+
if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) {
|
|
251
|
+
throw new MalformedHandlerName(
|
|
252
|
+
`'${fullHandlerString}' is not a valid handler name. Use absolute paths when specifying root directories in handler names.`,
|
|
253
|
+
)
|
|
352
254
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function _isHandlerStreaming(handler) {
|
|
258
|
+
if (
|
|
259
|
+
handler[HANDLER_STREAMING] === undefined ||
|
|
260
|
+
handler[HANDLER_STREAMING] === null ||
|
|
261
|
+
handler[HANDLER_STREAMING] === false
|
|
262
|
+
) {
|
|
263
|
+
return false
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (handler[HANDLER_STREAMING] === STREAM_RESPONSE) {
|
|
267
|
+
return STREAM_RESPONSE
|
|
268
|
+
}
|
|
269
|
+
throw new MalformedStreamingHandler("Only response streaming is supported.")
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function _highWaterMark(handler) {
|
|
273
|
+
if (
|
|
274
|
+
handler[HANDLER_HIGHWATERMARK] === undefined ||
|
|
275
|
+
handler[HANDLER_HIGHWATERMARK] === null ||
|
|
276
|
+
handler[HANDLER_HIGHWATERMARK] === false
|
|
277
|
+
) {
|
|
278
|
+
return undefined
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const hwm = Number.parseInt(handler[HANDLER_HIGHWATERMARK], 10)
|
|
282
|
+
return Number.isNaN(hwm) ? undefined : hwm
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Load the user's function with the approot and the handler string.
|
|
287
|
+
* @param appRoot {string}
|
|
288
|
+
* The path to the application root.
|
|
289
|
+
* @param handlerString {string}
|
|
290
|
+
* The user-provided handler function in the form 'module.function'.
|
|
291
|
+
* @return userFuction {function}
|
|
292
|
+
* The user's handler function. This function will be passed the event body,
|
|
293
|
+
* the context object, and the callback function.
|
|
294
|
+
* @throws In five cases:-
|
|
295
|
+
* 1 - if the handler string is incorrectly formatted an error is thrown
|
|
296
|
+
* 2 - if the module referenced by the handler cannot be loaded
|
|
297
|
+
* 3 - if the function in the handler does not exist in the module
|
|
298
|
+
* 4 - if a property with the same name, but isn't a function, exists on the
|
|
299
|
+
* module
|
|
300
|
+
* 5 - the handler includes illegal character sequences (like relative paths
|
|
301
|
+
* for traversing up the filesystem '..')
|
|
302
|
+
* Errors for scenarios known by the runtime, will be wrapped by Runtime.* errors.
|
|
303
|
+
*/
|
|
304
|
+
module.exports.load = async function (appRoot, fullHandlerString) {
|
|
305
|
+
_throwIfInvalidHandler(fullHandlerString)
|
|
306
|
+
|
|
307
|
+
const [moduleRoot, moduleAndHandler] =
|
|
308
|
+
_moduleRootAndHandler(fullHandlerString)
|
|
309
|
+
const [module, handlerPath] = _splitHandlerString(moduleAndHandler)
|
|
310
|
+
|
|
311
|
+
const userApp = await _loadUserApp(appRoot, moduleRoot, module)
|
|
312
|
+
const handlerFunc = _resolveHandler(userApp, handlerPath)
|
|
313
|
+
|
|
314
|
+
if (!handlerFunc) {
|
|
315
|
+
throw new HandlerNotFound(
|
|
316
|
+
`${fullHandlerString} is undefined or not exported`,
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (typeof handlerFunc !== "function") {
|
|
321
|
+
throw new HandlerNotFound(`${fullHandlerString} is not a function`)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return handlerFunc
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
module.exports.isHandlerFunction = function (value) {
|
|
328
|
+
return typeof value === "function"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
module.exports.getHandlerMetadata = function (handlerFunc) {
|
|
332
|
+
return {
|
|
333
|
+
highWaterMark: _highWaterMark(handlerFunc),
|
|
334
|
+
streaming: _isHandlerStreaming(handlerFunc),
|
|
357
335
|
}
|
|
358
|
-
|
|
359
|
-
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports.STREAM_RESPONSE = STREAM_RESPONSE
|