serverless-offline 13.5.1 → 14.0.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/package.json +16 -14
- package/src/config/supportedRuntimes.js +7 -1
- 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/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 +312 -334
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/VerboseLog.js +54 -0
- package/src/utils/index.js +8 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dedicatedTo": "Blue, a great migrating bird.",
|
|
3
3
|
"name": "serverless-offline",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "14.0.0",
|
|
5
5
|
"description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@aws-sdk/client-lambda": "^3.509.0",
|
|
81
81
|
"@hapi/boom": "^10.0.1",
|
|
82
82
|
"@hapi/h2o2": "^10.0.4",
|
|
83
|
-
"@hapi/hapi": "^21.3.
|
|
83
|
+
"@hapi/hapi": "^21.3.10",
|
|
84
84
|
"array-unflat-js": "^0.1.3",
|
|
85
85
|
"boxen": "^7.1.1",
|
|
86
86
|
"chalk": "^5.3.0",
|
|
@@ -89,33 +89,35 @@
|
|
|
89
89
|
"fs-extra": "^11.2.0",
|
|
90
90
|
"is-wsl": "^3.1.0",
|
|
91
91
|
"java-invoke-local": "0.0.6",
|
|
92
|
-
"jose": "^5.
|
|
92
|
+
"jose": "^5.6.3",
|
|
93
93
|
"js-string-escape": "^1.0.1",
|
|
94
|
-
"jsonpath-plus": "^
|
|
94
|
+
"jsonpath-plus": "^9.0.0",
|
|
95
95
|
"jsonschema": "^1.4.1",
|
|
96
96
|
"jszip": "^3.10.1",
|
|
97
97
|
"luxon": "^3.4.4",
|
|
98
98
|
"node-schedule": "^2.1.1",
|
|
99
99
|
"p-memoize": "^7.1.1",
|
|
100
|
+
"tree-kill": "^1.2.2",
|
|
101
|
+
"tsx": "^4.16.2",
|
|
100
102
|
"velocityjs": "^2.0.6",
|
|
101
|
-
"ws": "^8.
|
|
103
|
+
"ws": "^8.18.0"
|
|
102
104
|
},
|
|
103
105
|
"devDependencies": {
|
|
104
106
|
"@istanbuljs/esm-loader-hook": "^0.2.0",
|
|
105
|
-
"archiver": "^
|
|
106
|
-
"commit-and-tag-version": "^12.
|
|
107
|
-
"eslint": "^8.
|
|
107
|
+
"archiver": "^7.0.1",
|
|
108
|
+
"commit-and-tag-version": "^12.4.1",
|
|
109
|
+
"eslint": "^8.57.0",
|
|
108
110
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
109
111
|
"eslint-config-prettier": "^9.1.0",
|
|
110
112
|
"eslint-plugin-import": "^2.29.1",
|
|
111
113
|
"eslint-plugin-prettier": "^5.1.3",
|
|
112
|
-
"eslint-plugin-unicorn": "^
|
|
113
|
-
"mocha": "^10.
|
|
114
|
-
"nyc": "^
|
|
115
|
-
"prettier": "^3.2
|
|
116
|
-
"serverless": "^
|
|
114
|
+
"eslint-plugin-unicorn": "^54.0.0",
|
|
115
|
+
"mocha": "^10.6.0",
|
|
116
|
+
"nyc": "^17.0.0",
|
|
117
|
+
"prettier": "^3.3.2",
|
|
118
|
+
"serverless": "^4.1.18"
|
|
117
119
|
},
|
|
118
120
|
"peerDependencies": {
|
|
119
|
-
"serverless": "^
|
|
121
|
+
"serverless": "^4.0.0"
|
|
120
122
|
}
|
|
121
123
|
}
|
|
@@ -31,6 +31,7 @@ export const supportedRuntimesArchitecture = {
|
|
|
31
31
|
provided: [X86_64],
|
|
32
32
|
dotnet6: [ARM64, X86_64],
|
|
33
33
|
"provided.al2": [ARM64, X86_64],
|
|
34
|
+
"provided.al2023": [ARM64, X86_64],
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// GO
|
|
@@ -48,7 +49,11 @@ export const supportedNodejs = new Set([
|
|
|
48
49
|
])
|
|
49
50
|
|
|
50
51
|
// PROVIDED
|
|
51
|
-
export const supportedProvided = new Set([
|
|
52
|
+
export const supportedProvided = new Set([
|
|
53
|
+
"provided",
|
|
54
|
+
"provided.al2",
|
|
55
|
+
"provided.al2023",
|
|
56
|
+
])
|
|
52
57
|
|
|
53
58
|
// PYTHON
|
|
54
59
|
export const supportedPython = new Set([
|
|
@@ -57,6 +62,7 @@ export const supportedPython = new Set([
|
|
|
57
62
|
"python3.9",
|
|
58
63
|
"python3.10",
|
|
59
64
|
"python3.11",
|
|
65
|
+
"python3.12",
|
|
60
66
|
])
|
|
61
67
|
|
|
62
68
|
// RUBY
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
function parseResource(resource) {
|
|
2
|
-
const [, region, accountId, restApiId, path] =
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const [, region = "*", accountId = "*", restApiId = "*", path = "*"] =
|
|
3
|
+
resource.match(
|
|
4
|
+
/arn:aws:execute-api:([^\s:]+)(?::([^\s:]+))?(?::([^\s/:]+))?(?:\/(.*))?/,
|
|
5
|
+
)
|
|
5
6
|
|
|
6
7
|
return {
|
|
7
8
|
accountId,
|
|
@@ -26,10 +27,6 @@ export default function authMatchPolicyResource(policyResource, resource) {
|
|
|
26
27
|
return true
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
if (policyResource === "arn:aws:execute-api:*:*:*") {
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
30
|
if (policyResource.includes("*") || policyResource.includes("?")) {
|
|
34
31
|
// Policy contains a wildcard resource
|
|
35
32
|
|
|
@@ -61,7 +58,7 @@ export default function authMatchPolicyResource(policyResource, resource) {
|
|
|
61
58
|
// for the requested resource and the resource defined in the policy
|
|
62
59
|
// Need to create a regex replacing ? with one character and * with any number of characters
|
|
63
60
|
const regExp = new RegExp(
|
|
64
|
-
parsedPolicyResource.path.replaceAll("*", ".*").replaceAll("?", ".")
|
|
61
|
+
`${parsedPolicyResource.path.replaceAll("*", ".*").replaceAll("?", ".")}$`,
|
|
65
62
|
)
|
|
66
63
|
|
|
67
64
|
return regExp.test(parsedResource.path)
|
|
@@ -295,7 +295,7 @@ export default class HttpServer {
|
|
|
295
295
|
return null
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
|
|
298
|
+
let authFunctionName = this.#extractAuthFunctionName(endpoint)
|
|
299
299
|
|
|
300
300
|
if (!authFunctionName) {
|
|
301
301
|
return null
|
|
@@ -303,16 +303,32 @@ export default class HttpServer {
|
|
|
303
303
|
|
|
304
304
|
log.notice(`Configuring Authorization: ${path} ${authFunctionName}`)
|
|
305
305
|
|
|
306
|
+
const standardFunctionExists =
|
|
307
|
+
this.#serverless.service.functions &&
|
|
308
|
+
this.#serverless.service.functions[authFunctionName]
|
|
309
|
+
const serverlessAuthorizerOptions =
|
|
310
|
+
this.#serverless.service.provider.httpApi &&
|
|
311
|
+
this.#serverless.service.provider.httpApi.authorizers &&
|
|
312
|
+
this.#serverless.service.provider.httpApi.authorizers[authFunctionName]
|
|
313
|
+
|
|
314
|
+
if (
|
|
315
|
+
!standardFunctionExists &&
|
|
316
|
+
endpoint.isHttpApi &&
|
|
317
|
+
serverlessAuthorizerOptions &&
|
|
318
|
+
serverlessAuthorizerOptions.functionName
|
|
319
|
+
) {
|
|
320
|
+
log.notice(
|
|
321
|
+
`Redirecting authorizer function: ${authFunctionName} to ${serverlessAuthorizerOptions.functionName}`,
|
|
322
|
+
)
|
|
323
|
+
authFunctionName = serverlessAuthorizerOptions.functionName
|
|
324
|
+
}
|
|
325
|
+
|
|
306
326
|
const authFunction = this.#serverless.service.getFunction(authFunctionName)
|
|
307
327
|
|
|
308
328
|
if (!authFunction) {
|
|
309
329
|
log.error(`Authorization function ${authFunctionName} does not exist`)
|
|
310
330
|
return null
|
|
311
331
|
}
|
|
312
|
-
const serverlessAuthorizerOptions =
|
|
313
|
-
this.#serverless.service.provider.httpApi &&
|
|
314
|
-
this.#serverless.service.provider.httpApi.authorizers &&
|
|
315
|
-
this.#serverless.service.provider.httpApi.authorizers[authFunctionName]
|
|
316
332
|
|
|
317
333
|
const authorizerOptions = {
|
|
318
334
|
enableSimpleResponses:
|
|
@@ -326,7 +342,8 @@ export default class HttpServer {
|
|
|
326
342
|
? serverlessAuthorizerOptions?.payloadVersion || "2.0"
|
|
327
343
|
: "1.0",
|
|
328
344
|
resultTtlInSeconds:
|
|
329
|
-
serverlessAuthorizerOptions?.resultTtlInSeconds
|
|
345
|
+
serverlessAuthorizerOptions?.resultTtlInSeconds ?? "300",
|
|
346
|
+
type: endpoint.isHttpApi ? serverlessAuthorizerOptions?.type : undefined,
|
|
330
347
|
}
|
|
331
348
|
|
|
332
349
|
if (
|
|
@@ -339,11 +356,10 @@ export default class HttpServer {
|
|
|
339
356
|
return null
|
|
340
357
|
}
|
|
341
358
|
|
|
342
|
-
if (typeof endpoint.authorizer
|
|
343
|
-
authorizerOptions.name = authFunctionName
|
|
344
|
-
} else {
|
|
359
|
+
if (typeof endpoint.authorizer !== "string") {
|
|
345
360
|
assign(authorizerOptions, endpoint.authorizer)
|
|
346
361
|
}
|
|
362
|
+
authorizerOptions.name = authFunctionName
|
|
347
363
|
|
|
348
364
|
if (
|
|
349
365
|
!authorizerOptions.identitySource &&
|
|
@@ -443,7 +459,7 @@ export default class HttpServer {
|
|
|
443
459
|
request.payload = request.payload && request.payload.toString(encoding)
|
|
444
460
|
request.rawPayload = request.payload
|
|
445
461
|
|
|
446
|
-
//
|
|
462
|
+
// incoming request message
|
|
447
463
|
log.notice()
|
|
448
464
|
|
|
449
465
|
log.notice()
|
|
@@ -5,7 +5,7 @@ import { log } from "../../utils/log.js"
|
|
|
5
5
|
const { isArray } = Array
|
|
6
6
|
const { now } = Date
|
|
7
7
|
|
|
8
|
-
export default function
|
|
8
|
+
export default function createJWTAuthScheme(jwtOptions) {
|
|
9
9
|
const authorizerName = jwtOptions.name
|
|
10
10
|
|
|
11
11
|
const identitySourceMatch = /^\$request.header.((?:\w+-?)+\w+)$/.exec(
|
|
@@ -43,7 +43,7 @@ export default function createAuthScheme(jwtOptions) {
|
|
|
43
43
|
return Boom.unauthorized("JWT Token expired")
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const { aud, iss, scope, client_id: clientId } = claims
|
|
46
|
+
const { aud, iss, scope, scp, client_id: clientId } = claims
|
|
47
47
|
if (iss !== jwtOptions.issuerUrl) {
|
|
48
48
|
log.notice(`JWT Token not from correct issuer url`)
|
|
49
49
|
|
|
@@ -68,13 +68,13 @@ export default function createAuthScheme(jwtOptions) {
|
|
|
68
68
|
|
|
69
69
|
let scopes = null
|
|
70
70
|
if (jwtOptions.scopes && jwtOptions.scopes.length > 0) {
|
|
71
|
-
if (!scope) {
|
|
71
|
+
if (!scope && !scp) {
|
|
72
72
|
log.notice(`JWT Token missing valid scope`)
|
|
73
73
|
|
|
74
74
|
return Boom.forbidden("JWT Token missing valid scope")
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
scopes = scope.split(" ")
|
|
77
|
+
scopes = scp || scope.split(" ")
|
|
78
78
|
if (scopes.every((s) => !jwtOptions.scopes.includes(s))) {
|
|
79
79
|
log.notice(`JWT Token missing valid scope`)
|
|
80
80
|
|
|
@@ -85,7 +85,6 @@ export default function createAuthScheme(jwtOptions) {
|
|
|
85
85
|
log.notice(`JWT Token validated`)
|
|
86
86
|
|
|
87
87
|
// Set the credentials for the rest of the pipeline
|
|
88
|
-
// return resolve(
|
|
89
88
|
return h.authenticated({
|
|
90
89
|
credentials: {
|
|
91
90
|
claims,
|
|
@@ -134,8 +134,8 @@ export default class LambdaProxyIntegrationEvent {
|
|
|
134
134
|
if (token) {
|
|
135
135
|
try {
|
|
136
136
|
claims = decodeJwt(token)
|
|
137
|
-
if (claims.scope) {
|
|
138
|
-
scopes = claims.scope.split(" ")
|
|
137
|
+
if (claims.scp || claims.scope) {
|
|
138
|
+
scopes = claims.scp || claims.scope.split(" ")
|
|
139
139
|
// In AWS HTTP Api the scope property is removed from the decoded JWT
|
|
140
140
|
// I'm leaving this property because I'm not sure how all of the authorizers
|
|
141
141
|
// for AWS REST Api handle JWT.
|
|
@@ -120,8 +120,8 @@ export default class LambdaProxyIntegrationEventV2 {
|
|
|
120
120
|
if (token) {
|
|
121
121
|
try {
|
|
122
122
|
claims = decodeJwt(token)
|
|
123
|
-
if (claims.scope) {
|
|
124
|
-
scopes = claims.scope.split(" ")
|
|
123
|
+
if (claims.scp || claims.scope) {
|
|
124
|
+
scopes = claims.scp || claims.scope.split(" ")
|
|
125
125
|
// In AWS HTTP Api the scope property is removed from the decoded JWT
|
|
126
126
|
// I'm leaving this property because I'm not sure how all of the authorizers
|
|
127
127
|
// for AWS REST Api handle JWT.
|
|
@@ -10,7 +10,7 @@ const { assign, entries, fromEntries } = Object
|
|
|
10
10
|
|
|
11
11
|
function escapeJavaScript(x) {
|
|
12
12
|
if (typeof x === "string") {
|
|
13
|
-
return jsEscapeString(x).replaceAll(
|
|
13
|
+
return jsEscapeString(x).replaceAll(String.raw`\n`, "\n") // See #26,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
if (isPlainObject(x)) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* This code was copied from:
|
|
6
|
+
* https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/Errors.js
|
|
7
|
+
*
|
|
8
|
+
* Defines custom error types throwable by the runtime.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
"use strict"
|
|
12
|
+
|
|
13
|
+
const errorClasses = [
|
|
14
|
+
class ImportModuleError extends Error {},
|
|
15
|
+
class HandlerNotFound extends Error {},
|
|
16
|
+
class MalformedHandlerName extends Error {},
|
|
17
|
+
class UserCodeSyntaxError extends Error {},
|
|
18
|
+
class MalformedStreamingHandler extends Error {},
|
|
19
|
+
class InvalidStreamingOperation extends Error {},
|
|
20
|
+
class UnhandledPromiseRejection extends Error {
|
|
21
|
+
constructor(reason, promise) {
|
|
22
|
+
super(reason)
|
|
23
|
+
this.reason = reason
|
|
24
|
+
this.promise = promise
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
errorClasses.forEach((e) => {
|
|
30
|
+
module.exports[e.name] = e
|
|
31
|
+
e.prototype.name = `Runtime.${e.name}`
|
|
32
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
/* eslint-disable no-param-reassign */
|
|
3
|
+
/**
|
|
4
|
+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* This code was copied from:
|
|
7
|
+
* https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/HttpResponseStream.js
|
|
8
|
+
*
|
|
9
|
+
* HttpResponseStream is NOT used by the runtime.
|
|
10
|
+
* It is only exposed in the `awslambda` variable for customers to use.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict"
|
|
14
|
+
|
|
15
|
+
const METADATA_PRELUDE_CONTENT_TYPE =
|
|
16
|
+
"application/vnd.awslambda.http-integration-response"
|
|
17
|
+
const DELIMITER_LEN = 8
|
|
18
|
+
|
|
19
|
+
// Implements the application/vnd.awslambda.http-integration-response content type.
|
|
20
|
+
class HttpResponseStream {
|
|
21
|
+
static from(underlyingStream, prelude) {
|
|
22
|
+
underlyingStream.setContentType(METADATA_PRELUDE_CONTENT_TYPE)
|
|
23
|
+
|
|
24
|
+
// JSON.stringify is required. NULL byte is not allowed in metadataPrelude.
|
|
25
|
+
const metadataPrelude = JSON.stringify(prelude)
|
|
26
|
+
|
|
27
|
+
underlyingStream._onBeforeFirstWrite = (write) => {
|
|
28
|
+
write(metadataPrelude)
|
|
29
|
+
|
|
30
|
+
// Write 8 null bytes after the JSON prelude.
|
|
31
|
+
write(new Uint8Array(DELIMITER_LEN))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return underlyingStream
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports.HttpResponseStream = HttpResponseStream
|
|
@@ -1,359 +1,337 @@
|
|
|
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
|
+
console.warn(
|
|
122
|
+
`${pj} cannot be read, it will be ignored for ES module detection purposes.`,
|
|
123
|
+
e,
|
|
124
|
+
)
|
|
244
125
|
return false
|
|
245
126
|
}
|
|
246
|
-
return _hasFolderPackageJsonTypeModule(path.resolve(folder, ".."))
|
|
247
127
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
: false
|
|
128
|
+
|
|
129
|
+
if (folder === "/") {
|
|
130
|
+
// We have reached root without finding either a package.json or a node_modules.
|
|
131
|
+
return false
|
|
253
132
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
133
|
+
|
|
134
|
+
return _hasFolderPackageJsonTypeModule(path.resolve(folder, ".."))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _hasPackageJsonTypeModule(file) {
|
|
138
|
+
// File must have a .js extension
|
|
139
|
+
const jsPath = `${file}.js`
|
|
140
|
+
return fs.existsSync(jsPath)
|
|
141
|
+
? _hasFolderPackageJsonTypeModule(path.resolve(path.dirname(jsPath)))
|
|
142
|
+
: false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Attempt to load the user's module.
|
|
147
|
+
* Attempts to directly resolve the module relative to the application root,
|
|
148
|
+
* then falls back to the more general require().
|
|
149
|
+
*/
|
|
150
|
+
async function _tryRequire(appRoot, moduleRoot, module) {
|
|
151
|
+
verbose(
|
|
152
|
+
"Try loading as commonjs: ",
|
|
153
|
+
module,
|
|
154
|
+
" with paths: ,",
|
|
155
|
+
appRoot,
|
|
156
|
+
moduleRoot,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const lambdaStylePath = path.resolve(appRoot, moduleRoot, module)
|
|
160
|
+
|
|
161
|
+
// Extensionless files are loaded via require.
|
|
162
|
+
const extensionless = _tryRequireFile(lambdaStylePath)
|
|
163
|
+
if (extensionless) {
|
|
164
|
+
return extensionless
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If package.json type != module, .js files are loaded via require.
|
|
168
|
+
const pjHasModule = _hasPackageJsonTypeModule(lambdaStylePath)
|
|
169
|
+
if (!pjHasModule) {
|
|
170
|
+
const loaded = _tryRequireFile(lambdaStylePath, ".js")
|
|
278
171
|
if (loaded) {
|
|
279
172
|
return loaded
|
|
280
173
|
}
|
|
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
174
|
}
|
|
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
|
-
}
|
|
175
|
+
|
|
176
|
+
// If still not loaded, try .js, .mjs, .cjs and .ts in that order.
|
|
177
|
+
// Files ending with .js are loaded as ES modules when the nearest parent package.json
|
|
178
|
+
// file contains a top-level field "type" with a value of "module".
|
|
179
|
+
// https://nodejs.org/api/packages.html#packages_type
|
|
180
|
+
const loaded =
|
|
181
|
+
(pjHasModule && (await _tryAwaitImport(lambdaStylePath, ".js"))) ||
|
|
182
|
+
(await _tryAwaitImport(lambdaStylePath, ".mjs")) ||
|
|
183
|
+
_tryRequireFile(lambdaStylePath, ".cjs") ||
|
|
184
|
+
tsxRequire(`${lambdaStylePath}.ts`, `${lambdaStylePath}.ts`)
|
|
185
|
+
if (loaded) {
|
|
186
|
+
return loaded
|
|
315
187
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
188
|
+
|
|
189
|
+
verbose(
|
|
190
|
+
"Try loading as commonjs: ",
|
|
191
|
+
module,
|
|
192
|
+
" with path(s): ",
|
|
193
|
+
appRoot,
|
|
194
|
+
moduleRoot,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// Why not just require(module)?
|
|
198
|
+
// Because require() is relative to __dirname, not process.cwd(). And the
|
|
199
|
+
// runtime implementation is not located in /var/task
|
|
200
|
+
// This won't work (yet) for esModules as import.meta.resolve is still experimental
|
|
201
|
+
// See: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent
|
|
202
|
+
const nodeStylePath = require.resolve(module, {
|
|
203
|
+
paths: [appRoot, moduleRoot],
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
return require(nodeStylePath)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Load the user's application or throw a descriptive error.
|
|
211
|
+
* @throws Runtime errors in two cases
|
|
212
|
+
* 1 - UserCodeSyntaxError if there's a syntax error while loading the module
|
|
213
|
+
* 2 - ImportModuleError if the module cannot be found
|
|
214
|
+
*/
|
|
215
|
+
async function _loadUserApp(appRoot, moduleRoot, module) {
|
|
216
|
+
if (!NoGlobalAwsLambda) {
|
|
217
|
+
globalThis.awslambda = {
|
|
218
|
+
HttpResponseStream,
|
|
219
|
+
streamifyResponse: (handler, options) => {
|
|
220
|
+
// eslint-disable-next-line no-param-reassign
|
|
221
|
+
handler[HANDLER_STREAMING] = STREAM_RESPONSE
|
|
222
|
+
if (typeof options?.highWaterMark === "number") {
|
|
223
|
+
// eslint-disable-next-line no-param-reassign
|
|
224
|
+
handler[HANDLER_HIGHWATERMARK] = Number.parseInt(
|
|
225
|
+
options.highWaterMark,
|
|
226
|
+
10,
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
return handler
|
|
230
|
+
},
|
|
321
231
|
}
|
|
322
232
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
return await _tryRequire(appRoot, moduleRoot, module)
|
|
236
|
+
} catch (e) {
|
|
237
|
+
if (e instanceof SyntaxError) {
|
|
238
|
+
throw new UserCodeSyntaxError(e)
|
|
239
|
+
} else if (e.code !== undefined && e.code === "MODULE_NOT_FOUND") {
|
|
240
|
+
verbose("globalPaths", JSON.stringify(require("node:module").globalPaths))
|
|
241
|
+
throw new ImportModuleError(e)
|
|
242
|
+
} else {
|
|
243
|
+
throw e
|
|
333
244
|
}
|
|
334
|
-
throw new MalformedStreamingHandler("Only response streaming is supported.")
|
|
335
245
|
}
|
|
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
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function _throwIfInvalidHandler(fullHandlerString) {
|
|
249
|
+
if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) {
|
|
250
|
+
throw new MalformedHandlerName(
|
|
251
|
+
`'${fullHandlerString}' is not a valid handler name. Use absolute paths when specifying root directories in handler names.`,
|
|
252
|
+
)
|
|
352
253
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function _isHandlerStreaming(handler) {
|
|
257
|
+
if (
|
|
258
|
+
handler[HANDLER_STREAMING] === undefined ||
|
|
259
|
+
handler[HANDLER_STREAMING] === null ||
|
|
260
|
+
handler[HANDLER_STREAMING] === false
|
|
261
|
+
) {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (handler[HANDLER_STREAMING] === STREAM_RESPONSE) {
|
|
266
|
+
return STREAM_RESPONSE
|
|
267
|
+
}
|
|
268
|
+
throw new MalformedStreamingHandler("Only response streaming is supported.")
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function _highWaterMark(handler) {
|
|
272
|
+
if (
|
|
273
|
+
handler[HANDLER_HIGHWATERMARK] === undefined ||
|
|
274
|
+
handler[HANDLER_HIGHWATERMARK] === null ||
|
|
275
|
+
handler[HANDLER_HIGHWATERMARK] === false
|
|
276
|
+
) {
|
|
277
|
+
return undefined
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const hwm = Number.parseInt(handler[HANDLER_HIGHWATERMARK], 10)
|
|
281
|
+
return Number.isNaN(hwm) ? undefined : hwm
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Load the user's function with the approot and the handler string.
|
|
286
|
+
* @param appRoot {string}
|
|
287
|
+
* The path to the application root.
|
|
288
|
+
* @param handlerString {string}
|
|
289
|
+
* The user-provided handler function in the form 'module.function'.
|
|
290
|
+
* @return userFuction {function}
|
|
291
|
+
* The user's handler function. This function will be passed the event body,
|
|
292
|
+
* the context object, and the callback function.
|
|
293
|
+
* @throws In five cases:-
|
|
294
|
+
* 1 - if the handler string is incorrectly formatted an error is thrown
|
|
295
|
+
* 2 - if the module referenced by the handler cannot be loaded
|
|
296
|
+
* 3 - if the function in the handler does not exist in the module
|
|
297
|
+
* 4 - if a property with the same name, but isn't a function, exists on the
|
|
298
|
+
* module
|
|
299
|
+
* 5 - the handler includes illegal character sequences (like relative paths
|
|
300
|
+
* for traversing up the filesystem '..')
|
|
301
|
+
* Errors for scenarios known by the runtime, will be wrapped by Runtime.* errors.
|
|
302
|
+
*/
|
|
303
|
+
module.exports.load = async function (appRoot, fullHandlerString) {
|
|
304
|
+
_throwIfInvalidHandler(fullHandlerString)
|
|
305
|
+
|
|
306
|
+
const [moduleRoot, moduleAndHandler] =
|
|
307
|
+
_moduleRootAndHandler(fullHandlerString)
|
|
308
|
+
const [module, handlerPath] = _splitHandlerString(moduleAndHandler)
|
|
309
|
+
|
|
310
|
+
const userApp = await _loadUserApp(appRoot, moduleRoot, module)
|
|
311
|
+
const handlerFunc = _resolveHandler(userApp, handlerPath)
|
|
312
|
+
|
|
313
|
+
if (!handlerFunc) {
|
|
314
|
+
throw new HandlerNotFound(
|
|
315
|
+
`${fullHandlerString} is undefined or not exported`,
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (typeof handlerFunc !== "function") {
|
|
320
|
+
throw new HandlerNotFound(`${fullHandlerString} is not a function`)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return handlerFunc
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
module.exports.isHandlerFunction = function (value) {
|
|
327
|
+
return typeof value === "function"
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports.getHandlerMetadata = function (handlerFunc) {
|
|
331
|
+
return {
|
|
332
|
+
highWaterMark: _highWaterMark(handlerFunc),
|
|
333
|
+
streaming: _isHandlerStreaming(handlerFunc),
|
|
357
334
|
}
|
|
358
|
-
|
|
359
|
-
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module.exports.STREAM_RESPONSE = STREAM_RESPONSE
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* eslint-disable prefer-rest-params */
|
|
2
|
+
/* eslint-disable func-names */
|
|
3
|
+
/**
|
|
4
|
+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
"use strict"
|
|
8
|
+
|
|
9
|
+
const process = require("node:process")
|
|
10
|
+
|
|
11
|
+
const EnvVarName = "AWS_LAMBDA_RUNTIME_VERBOSE"
|
|
12
|
+
const Tag = "RUNTIME"
|
|
13
|
+
const Verbosity = (() => {
|
|
14
|
+
if (!process.env[EnvVarName]) {
|
|
15
|
+
return 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const verbosity = Number.parseInt(process.env[EnvVarName], 10)
|
|
20
|
+
// eslint-disable-next-line unicorn/no-nested-ternary
|
|
21
|
+
return verbosity < 0 ? 0 : verbosity > 3 ? 3 : verbosity
|
|
22
|
+
} catch {
|
|
23
|
+
return 0
|
|
24
|
+
}
|
|
25
|
+
})()
|
|
26
|
+
|
|
27
|
+
exports.logger = function (category) {
|
|
28
|
+
return {
|
|
29
|
+
verbose() {
|
|
30
|
+
if (Verbosity >= 1) {
|
|
31
|
+
const args = [...arguments].map((arg) =>
|
|
32
|
+
typeof arg === "function" ? arg() : arg,
|
|
33
|
+
)
|
|
34
|
+
Reflect.apply(console.log, null, [Tag, category, ...args])
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
vverbose() {
|
|
38
|
+
if (Verbosity >= 2) {
|
|
39
|
+
const args = [...arguments].map((arg) =>
|
|
40
|
+
typeof arg === "function" ? arg() : arg,
|
|
41
|
+
)
|
|
42
|
+
Reflect.apply(console.log, null, [Tag, category, ...args])
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
vvverbose() {
|
|
46
|
+
if (Verbosity >= 3) {
|
|
47
|
+
const args = [...arguments].map((arg) =>
|
|
48
|
+
typeof arg === "function" ? arg() : arg,
|
|
49
|
+
)
|
|
50
|
+
Reflect.apply(console.log, null, [Tag, category, ...args])
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -21,13 +21,20 @@ export { generateAlbHapiPath } from "./generateHapiPath.js"
|
|
|
21
21
|
const { isArray } = Array
|
|
22
22
|
const { keys } = Object
|
|
23
23
|
|
|
24
|
+
const possibleBinaryContentTypes = [
|
|
25
|
+
"application/octet-stream",
|
|
26
|
+
"multipart/form-data",
|
|
27
|
+
]
|
|
28
|
+
|
|
24
29
|
// Detect the toString encoding from the request headers content-type
|
|
25
30
|
// enhance if further content types need to be non utf8 encoded.
|
|
26
31
|
export function detectEncoding(request) {
|
|
27
32
|
const contentType = request.headers["content-type"]
|
|
28
33
|
|
|
29
34
|
return typeof contentType === "string" &&
|
|
30
|
-
|
|
35
|
+
possibleBinaryContentTypes.some((possibleBinaryContentType) =>
|
|
36
|
+
contentType.includes(possibleBinaryContentType),
|
|
37
|
+
)
|
|
31
38
|
? "binary"
|
|
32
39
|
: "utf8"
|
|
33
40
|
}
|