serverless-offline 12.0.4 → 13.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/README.md +6 -0
- package/package.json +25 -27
- package/src/ServerlessOffline.js +7 -5
- package/src/config/supportedRuntimes.js +4 -5
- package/src/events/alb/Alb.js +0 -6
- package/src/events/alb/HttpServer.js +3 -7
- package/src/events/authMatchPolicyResource.js +1 -1
- package/src/events/http/Endpoint.js +1 -1
- package/src/events/http/HttpServer.js +14 -10
- package/src/events/http/createAuthScheme.js +47 -36
- package/src/events/http/javaHelpers.js +0 -7
- package/src/events/http/lambda-events/VelocityContext.js +2 -2
- package/src/events/schedule/ScheduleEvent.js +1 -1
- package/src/index.js +0 -11
- package/src/lambda/LambdaFunction.js +1 -1
- package/src/lambda/handler-runner/docker-runner/DockerContainer.js +1 -1
- package/src/lambda/routes/invocations/InvocationsController.js +1 -1
- package/src/utils/generateHapiPath.js +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,12 @@ To do so, it starts an HTTP server that handles the request's lifecycle like API
|
|
|
38
38
|
|
|
39
39
|
This plugin is updated by its users, I just do maintenance and ensure that PRs are relevant to the community. In other words, if you [find a bug or want a new feature](https://github.com/dherault/serverless-offline/issues), please help us by becoming one of the [contributors](https://github.com/dherault/serverless-offline/graphs/contributors) :v: ! See the [contributing section](#contributing).
|
|
40
40
|
|
|
41
|
+
## Looking for maintainers ⚠️
|
|
42
|
+
|
|
43
|
+
Applications welcome! This package is used by thousands of people daily. Yet we lack a maintainer! If you think you'll be a good fit for the role, please send me an email at dherault/at/gmail.com.
|
|
44
|
+
|
|
45
|
+
See https://github.com/dherault/serverless-offline/issues/1704.
|
|
46
|
+
|
|
41
47
|
## Documentation
|
|
42
48
|
|
|
43
49
|
- [Installation](#installation)
|
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": "13.0.0",
|
|
5
5
|
"description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
],
|
|
52
52
|
"author": "David Hérault <dherault@gmail.com> (https://github.com/dherault)",
|
|
53
53
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
54
|
+
"node": ">=18.12.0"
|
|
55
55
|
},
|
|
56
56
|
"standard-version": {
|
|
57
57
|
"skip": {
|
|
@@ -78,49 +78,47 @@
|
|
|
78
78
|
]
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@aws-sdk/client-lambda": "^3.
|
|
82
|
-
"@hapi/boom": "^10.0.
|
|
83
|
-
"@hapi/h2o2": "^10.0.
|
|
84
|
-
"@hapi/hapi": "^21.
|
|
85
|
-
"@serverless/utils": "^6.
|
|
81
|
+
"@aws-sdk/client-lambda": "^3.414.0",
|
|
82
|
+
"@hapi/boom": "^10.0.1",
|
|
83
|
+
"@hapi/h2o2": "^10.0.4",
|
|
84
|
+
"@hapi/hapi": "^21.3.2",
|
|
85
|
+
"@serverless/utils": "^6.15.0",
|
|
86
86
|
"array-unflat-js": "^0.1.3",
|
|
87
|
-
"boxen": "^7.
|
|
88
|
-
"chalk": "^5.
|
|
87
|
+
"boxen": "^7.1.1",
|
|
88
|
+
"chalk": "^5.3.0",
|
|
89
89
|
"desm": "^1.3.0",
|
|
90
|
-
"execa": "^
|
|
91
|
-
"fs-extra": "^11.1.
|
|
92
|
-
"is-wsl": "^
|
|
90
|
+
"execa": "^8.0.1",
|
|
91
|
+
"fs-extra": "^11.1.1",
|
|
92
|
+
"is-wsl": "^3.0.0",
|
|
93
93
|
"java-invoke-local": "0.0.6",
|
|
94
|
-
"jose": "^4.
|
|
94
|
+
"jose": "^4.14.6",
|
|
95
95
|
"js-string-escape": "^1.0.1",
|
|
96
96
|
"jsonpath-plus": "^7.2.0",
|
|
97
97
|
"jsonschema": "^1.4.1",
|
|
98
98
|
"jszip": "^3.10.1",
|
|
99
99
|
"luxon": "^3.2.0",
|
|
100
|
-
"node-
|
|
101
|
-
"node-schedule": "^2.1.0",
|
|
102
|
-
"object.hasown": "^1.1.2",
|
|
100
|
+
"node-schedule": "^2.1.1",
|
|
103
101
|
"p-memoize": "^7.1.1",
|
|
104
|
-
"p-retry": "^
|
|
102
|
+
"p-retry": "^6.0.0",
|
|
105
103
|
"velocityjs": "^2.0.6",
|
|
106
|
-
"ws": "^8.
|
|
104
|
+
"ws": "^8.14.2"
|
|
107
105
|
},
|
|
108
106
|
"devDependencies": {
|
|
109
107
|
"@istanbuljs/esm-loader-hook": "^0.2.0",
|
|
110
|
-
"archiver": "^
|
|
111
|
-
"eslint": "^8.
|
|
108
|
+
"archiver": "^6.0.1",
|
|
109
|
+
"eslint": "^8.49.0",
|
|
112
110
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
113
|
-
"eslint-config-prettier": "^
|
|
114
|
-
"eslint-plugin-import": "^2.
|
|
115
|
-
"eslint-plugin-prettier": "^
|
|
116
|
-
"eslint-plugin-unicorn": "^
|
|
111
|
+
"eslint-config-prettier": "^9.0.0",
|
|
112
|
+
"eslint-plugin-import": "^2.28.1",
|
|
113
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
114
|
+
"eslint-plugin-unicorn": "^48.0.1",
|
|
117
115
|
"git-list-updated": "^1.2.1",
|
|
118
116
|
"husky": "^8.0.3",
|
|
119
|
-
"lint-staged": "^
|
|
117
|
+
"lint-staged": "^14.0.1",
|
|
120
118
|
"mocha": "^10.2.0",
|
|
121
119
|
"nyc": "^15.1.0",
|
|
122
|
-
"prettier": "^
|
|
123
|
-
"serverless": "^3.
|
|
120
|
+
"prettier": "^3.0.3",
|
|
121
|
+
"serverless": "^3.35.2",
|
|
124
122
|
"standard-version": "^9.5.0"
|
|
125
123
|
},
|
|
126
124
|
"peerDependencies": {
|
package/src/ServerlessOffline.js
CHANGED
|
@@ -107,8 +107,10 @@ export default class ServerlessOffline {
|
|
|
107
107
|
const eventModules = []
|
|
108
108
|
|
|
109
109
|
if (this.#lambda) {
|
|
110
|
-
eventModules.push(
|
|
111
|
-
|
|
110
|
+
eventModules.push(
|
|
111
|
+
this.#lambda.cleanup(),
|
|
112
|
+
this.#lambda.stop(SERVER_SHUTDOWN_TIMEOUT),
|
|
113
|
+
)
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
if (this.#alb) {
|
|
@@ -259,13 +261,13 @@ export default class ServerlessOffline {
|
|
|
259
261
|
|
|
260
262
|
// Parse CORS options
|
|
261
263
|
this.#options.corsAllowHeaders = this.#options.corsAllowHeaders
|
|
262
|
-
.
|
|
264
|
+
.replaceAll(' ', '')
|
|
263
265
|
.split(',')
|
|
264
266
|
this.#options.corsAllowOrigin = this.#options.corsAllowOrigin
|
|
265
|
-
.
|
|
267
|
+
.replaceAll(' ', '')
|
|
266
268
|
.split(',')
|
|
267
269
|
this.#options.corsExposedHeaders = this.#options.corsExposedHeaders
|
|
268
|
-
.
|
|
270
|
+
.replaceAll(' ', '')
|
|
269
271
|
.split(',')
|
|
270
272
|
|
|
271
273
|
this.#options.corsConfig = {
|
|
@@ -4,18 +4,16 @@
|
|
|
4
4
|
// .NET CORE
|
|
5
5
|
// export const supportedDotnetcore = new Set([
|
|
6
6
|
// 'dotnet6',
|
|
7
|
-
// 'dotnetcore3.1',
|
|
8
7
|
// ])
|
|
9
8
|
|
|
10
9
|
// GO
|
|
11
10
|
export const supportedGo = new Set(['go1.x'])
|
|
12
11
|
|
|
13
12
|
// JAVA
|
|
14
|
-
export const supportedJava = new Set(['java8', 'java8.al2', 'java11'])
|
|
13
|
+
export const supportedJava = new Set(['java8', 'java8.al2', 'java11', 'java17'])
|
|
15
14
|
|
|
16
15
|
// NODE.JS
|
|
17
16
|
export const supportedNodejs = new Set([
|
|
18
|
-
'nodejs12.x',
|
|
19
17
|
'nodejs14.x',
|
|
20
18
|
'nodejs16.x',
|
|
21
19
|
'nodejs18.x',
|
|
@@ -26,14 +24,15 @@ export const supportedProvided = new Set(['provided', 'provided.al2'])
|
|
|
26
24
|
|
|
27
25
|
// PYTHON
|
|
28
26
|
export const supportedPython = new Set([
|
|
29
|
-
'python3.6',
|
|
30
27
|
'python3.7',
|
|
31
28
|
'python3.8',
|
|
32
29
|
'python3.9',
|
|
30
|
+
'python3.10',
|
|
31
|
+
'python3.11',
|
|
33
32
|
])
|
|
34
33
|
|
|
35
34
|
// RUBY
|
|
36
|
-
export const supportedRuby = new Set(['ruby2.7'])
|
|
35
|
+
export const supportedRuby = new Set(['ruby2.7', 'ruby3.2'])
|
|
37
36
|
|
|
38
37
|
// deprecated runtimes
|
|
39
38
|
// https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
|
package/src/events/alb/Alb.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { log } from '@serverless/utils/log.js'
|
|
2
1
|
import AlbEventDefinition from './AlbEventDefinition.js'
|
|
3
2
|
import HttpServer from './HttpServer.js'
|
|
4
3
|
|
|
@@ -15,11 +14,6 @@ export default class Alb {
|
|
|
15
14
|
this.#lambda = lambda
|
|
16
15
|
this.#options = options
|
|
17
16
|
this.#serverless = serverless
|
|
18
|
-
|
|
19
|
-
log.warning(`
|
|
20
|
-
Application Load Balancer (ALB) support in serverless-offline is experimental.
|
|
21
|
-
Please file an issue for any bugs, missing features or other feedback: https://github.com/dherault/serverless-offline/issues
|
|
22
|
-
`)
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
start() {
|
|
@@ -105,13 +105,9 @@ export default class HttpServer {
|
|
|
105
105
|
if (request.method === 'options') {
|
|
106
106
|
response.statusCode = 200
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
} else {
|
|
112
|
-
response.headers['access-control-expose-headers'] =
|
|
113
|
-
'content-type, content-length, etag'
|
|
114
|
-
}
|
|
108
|
+
response.headers['access-control-expose-headers'] =
|
|
109
|
+
request.headers['access-control-expose-headers'] ||
|
|
110
|
+
'content-type, content-length, etag'
|
|
115
111
|
response.headers['access-control-max-age'] = 60 * 10
|
|
116
112
|
|
|
117
113
|
if (request.headers['access-control-request-headers']) {
|
|
@@ -61,7 +61,7 @@ export default function authMatchPolicyResource(policyResource, resource) {
|
|
|
61
61
|
// for the requested resource and the resource defined in the policy
|
|
62
62
|
// Need to create a regex replacing ? with one character and * with any number of characters
|
|
63
63
|
const regExp = new RegExp(
|
|
64
|
-
parsedPolicyResource.path.
|
|
64
|
+
parsedPolicyResource.path.replaceAll('*', '.*').replaceAll('?', '.'),
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
return regExp.test(parsedResource.path)
|
|
@@ -17,7 +17,7 @@ const defaultResponseTemplate = readFileSync(
|
|
|
17
17
|
|
|
18
18
|
function getResponseContentType(fep) {
|
|
19
19
|
if (fep.response && fep.response.headers['Content-Type']) {
|
|
20
|
-
return fep.response.headers['Content-Type'].
|
|
20
|
+
return fep.response.headers['Content-Type'].replaceAll(/'/gm, '')
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
return 'application/json'
|
|
@@ -164,13 +164,9 @@ export default class HttpServer {
|
|
|
164
164
|
if (request.method === 'options') {
|
|
165
165
|
response.statusCode = 200
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} else {
|
|
171
|
-
response.headers['access-control-expose-headers'] =
|
|
172
|
-
'content-type, content-length, etag'
|
|
173
|
-
}
|
|
167
|
+
response.headers['access-control-expose-headers'] =
|
|
168
|
+
request.headers['access-control-expose-headers'] ||
|
|
169
|
+
'content-type, content-length, etag'
|
|
174
170
|
response.headers['access-control-max-age'] = 60 * 10
|
|
175
171
|
|
|
176
172
|
if (request.headers['access-control-request-headers']) {
|
|
@@ -323,9 +319,7 @@ export default class HttpServer {
|
|
|
323
319
|
(endpoint.isHttpApi &&
|
|
324
320
|
serverlessAuthorizerOptions?.enableSimpleResponses) ||
|
|
325
321
|
false,
|
|
326
|
-
identitySource:
|
|
327
|
-
serverlessAuthorizerOptions?.identitySource ||
|
|
328
|
-
'method.request.header.Authorization',
|
|
322
|
+
identitySource: serverlessAuthorizerOptions?.identitySource,
|
|
329
323
|
identityValidationExpression:
|
|
330
324
|
serverlessAuthorizerOptions?.identityValidationExpression || '(.*)',
|
|
331
325
|
payloadVersion: endpoint.isHttpApi
|
|
@@ -351,6 +345,16 @@ export default class HttpServer {
|
|
|
351
345
|
assign(authorizerOptions, endpoint.authorizer)
|
|
352
346
|
}
|
|
353
347
|
|
|
348
|
+
if (
|
|
349
|
+
!authorizerOptions.identitySource &&
|
|
350
|
+
!(
|
|
351
|
+
authorizerOptions.type === 'request' &&
|
|
352
|
+
authorizerOptions.resultTtlInSeconds === 0
|
|
353
|
+
)
|
|
354
|
+
) {
|
|
355
|
+
authorizerOptions.identitySource = 'method.request.header.Authorization'
|
|
356
|
+
}
|
|
357
|
+
|
|
354
358
|
// Create a unique scheme per endpoint
|
|
355
359
|
// This allows the methodArn on the event property to be set appropriately
|
|
356
360
|
const authKey = `${functionKey}-${authFunctionName}-${method}-${path}`
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
|
|
14
14
|
const IDENTITY_SOURCE_TYPE_HEADER = 'header'
|
|
15
15
|
const IDENTITY_SOURCE_TYPE_QUERYSTRING = 'querystring'
|
|
16
|
+
const IDENTITY_SOURCE_TYPE_NONE = 'none'
|
|
16
17
|
|
|
17
18
|
export default function createAuthScheme(authorizerOptions, provider, lambda) {
|
|
18
19
|
const authFunName = authorizerOptions.name
|
|
@@ -65,38 +66,50 @@ export default function createAuthScheme(authorizerOptions, provider, lambda) {
|
|
|
65
66
|
const methodArn = `arn:aws:execute-api:${provider.region}:${accountId}:${apiId}/${provider.stage}/${httpMethod}${resourcePath}`
|
|
66
67
|
|
|
67
68
|
let authorization
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
switch (identitySourceType) {
|
|
70
|
+
case IDENTITY_SOURCE_TYPE_HEADER: {
|
|
71
|
+
const headers = request.raw.req.headers ?? {}
|
|
72
|
+
authorization = headers[identitySourceField]
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
case IDENTITY_SOURCE_TYPE_QUERYSTRING: {
|
|
76
|
+
const queryStringParameters = parseQueryStringParameters(url) ?? {}
|
|
77
|
+
authorization = queryStringParameters[identitySourceField]
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
case IDENTITY_SOURCE_TYPE_NONE: {
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
default: {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`No Authorization source has been specified. This should never happen. (λ: ${authFunName})`,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
let finalAuthorization
|
|
91
|
+
if (identitySourceType !== IDENTITY_SOURCE_TYPE_NONE) {
|
|
92
|
+
if (authorization === undefined) {
|
|
93
|
+
log.error(
|
|
94
|
+
`Identity Source is null for ${identitySourceType} ${identitySourceField} (λ: ${authFunName})`,
|
|
95
|
+
)
|
|
96
|
+
return Boom.unauthorized(
|
|
97
|
+
'User is not authorized to access this resource',
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const identityValidationExpression = new RegExp(
|
|
102
|
+
authorizerOptions.identityValidationExpression,
|
|
83
103
|
)
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
const matchedAuthorization =
|
|
105
|
+
identityValidationExpression.test(authorization)
|
|
106
|
+
finalAuthorization = matchedAuthorization ? authorization : ''
|
|
107
|
+
|
|
108
|
+
log.debug(
|
|
109
|
+
`Retrieved ${identitySourceField} ${identitySourceType} "${finalAuthorization}"`,
|
|
86
110
|
)
|
|
87
111
|
}
|
|
88
112
|
|
|
89
|
-
const identityValidationExpression = new RegExp(
|
|
90
|
-
authorizerOptions.identityValidationExpression,
|
|
91
|
-
)
|
|
92
|
-
const matchedAuthorization =
|
|
93
|
-
identityValidationExpression.test(authorization)
|
|
94
|
-
const finalAuthorization = matchedAuthorization ? authorization : ''
|
|
95
|
-
|
|
96
|
-
log.debug(
|
|
97
|
-
`Retrieved ${identitySourceField} ${identitySourceType} "${finalAuthorization}"`,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
113
|
if (authorizerOptions.payloadVersion === '1.0') {
|
|
101
114
|
event = {
|
|
102
115
|
...event,
|
|
@@ -148,17 +161,10 @@ export default function createAuthScheme(authorizerOptions, provider, lambda) {
|
|
|
148
161
|
|
|
149
162
|
// methodArn is the ARN of the function we are running we are authorizing access to (or not)
|
|
150
163
|
// Account ID and API ID are not simulated
|
|
151
|
-
|
|
152
|
-
event
|
|
153
|
-
...event,
|
|
154
|
-
type: 'REQUEST',
|
|
155
|
-
}
|
|
156
|
-
} else {
|
|
164
|
+
event = {
|
|
165
|
+
...event,
|
|
157
166
|
// This is safe since type: 'TOKEN' cannot have payload format 2.0
|
|
158
|
-
|
|
159
|
-
...event,
|
|
160
|
-
type: 'TOKEN',
|
|
161
|
-
}
|
|
167
|
+
type: authorizerOptions.type === 'request' ? 'REQUEST' : 'TOKEN',
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
const lambdaFunction = lambda.get(authFunName)
|
|
@@ -296,5 +302,10 @@ export default function createAuthScheme(authorizerOptions, provider, lambda) {
|
|
|
296
302
|
)
|
|
297
303
|
}
|
|
298
304
|
|
|
305
|
+
if (authorizerOptions.resultTtlInSeconds === 0) {
|
|
306
|
+
identitySourceType = IDENTITY_SOURCE_TYPE_NONE
|
|
307
|
+
return finalizeAuthScheme()
|
|
308
|
+
}
|
|
309
|
+
|
|
299
310
|
return finalizeAuthScheme()
|
|
300
311
|
}
|
|
@@ -20,10 +20,6 @@ function javaMatches(value) {
|
|
|
20
20
|
return this.match(new RegExp(value, 'm'))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function javaReplaceAll(oldValue, newValue) {
|
|
24
|
-
return this.replace(new RegExp(oldValue, 'gm'), newValue)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
23
|
function javaReplaceFirst(oldValue, newValue) {
|
|
28
24
|
return this.replace(new RegExp(oldValue, 'm'), newValue)
|
|
29
25
|
}
|
|
@@ -74,7 +70,6 @@ const {
|
|
|
74
70
|
equalsIgnoreCase,
|
|
75
71
|
matches,
|
|
76
72
|
regionMatches,
|
|
77
|
-
replaceAll,
|
|
78
73
|
replaceFirst,
|
|
79
74
|
},
|
|
80
75
|
} = String
|
|
@@ -85,7 +80,6 @@ export default function runInPollutedScope(runScope) {
|
|
|
85
80
|
prototype.equalsIgnoreCase = javaEqualsIgnoreCase
|
|
86
81
|
prototype.matches = javaMatches
|
|
87
82
|
prototype.regionMatches = javaRegionMatches
|
|
88
|
-
prototype.replaceAll = javaReplaceAll
|
|
89
83
|
prototype.replaceFirst = javaReplaceFirst
|
|
90
84
|
|
|
91
85
|
const result = runScope()
|
|
@@ -95,7 +89,6 @@ export default function runInPollutedScope(runScope) {
|
|
|
95
89
|
prototype.equalsIgnoreCase = equalsIgnoreCase
|
|
96
90
|
prototype.matches = matches
|
|
97
91
|
prototype.regionMatches = regionMatches
|
|
98
|
-
prototype.replaceAll = replaceAll
|
|
99
92
|
prototype.replaceFirst = replaceFirst
|
|
100
93
|
|
|
101
94
|
return result
|
|
@@ -14,7 +14,7 @@ const { assign, entries, fromEntries } = Object
|
|
|
14
14
|
|
|
15
15
|
function escapeJavaScript(x) {
|
|
16
16
|
if (typeof x === 'string') {
|
|
17
|
-
return jsEscapeString(x).
|
|
17
|
+
return jsEscapeString(x).replaceAll('\\n', '\n') // See #26,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
if (isPlainObject(x)) {
|
|
@@ -136,7 +136,7 @@ export default class VelocityContext {
|
|
|
136
136
|
Buffer.from(x.toString(), 'binary').toString('base64'),
|
|
137
137
|
escapeJavaScript,
|
|
138
138
|
parseJson: parse,
|
|
139
|
-
urlDecode: (x) => decodeURIComponent(x.
|
|
139
|
+
urlDecode: (x) => decodeURIComponent(x.replaceAll('+', ' ')),
|
|
140
140
|
urlEncode: encodeURI,
|
|
141
141
|
},
|
|
142
142
|
}
|
|
@@ -16,7 +16,7 @@ export default class ScheduleEvent {
|
|
|
16
16
|
source = 'aws.events'
|
|
17
17
|
|
|
18
18
|
// format of aws displaying the time, e.g.: 2020-02-09T14:13:57Z
|
|
19
|
-
time = new Date().toISOString().
|
|
19
|
+
time = new Date().toISOString().replaceAll(/\.(.*)(?=Z)/g, '')
|
|
20
20
|
|
|
21
21
|
version = '0'
|
|
22
22
|
|
package/src/index.js
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
|
-
// TODO remove with node.js v16.9+ support
|
|
2
|
-
import 'object.hasown/auto'
|
|
3
|
-
|
|
4
|
-
// install global fetch
|
|
5
|
-
// TODO remove `node-fetch` module and use global built-in with node.js v18+ support
|
|
6
|
-
if (globalThis.fetch === undefined) {
|
|
7
|
-
const { default: fetch, Headers } = await import('node-fetch')
|
|
8
|
-
globalThis.fetch = fetch
|
|
9
|
-
globalThis.Headers = Headers
|
|
10
|
-
}
|
|
11
|
-
|
|
12
1
|
export { default } from './ServerlessOffline.js'
|
|
@@ -250,7 +250,7 @@ export default class LambdaFunction {
|
|
|
250
250
|
entries(zip.files).map(async ([filename, jsZipObj]) => {
|
|
251
251
|
const fileData = await jsZipObj.async('nodebuffer')
|
|
252
252
|
if (filename.endsWith('/')) {
|
|
253
|
-
return
|
|
253
|
+
return undefined
|
|
254
254
|
}
|
|
255
255
|
await ensureDir(join(this.#codeDir, dirname(filename)))
|
|
256
256
|
return writeFile(join(this.#codeDir, filename), fileData, {
|
|
@@ -313,7 +313,7 @@ export default class DockerContainer {
|
|
|
313
313
|
entries(zip.files).map(async ([filename, jsZipObj]) => {
|
|
314
314
|
const fileData = await jsZipObj.async('nodebuffer')
|
|
315
315
|
if (filename.endsWith(sep)) {
|
|
316
|
-
return
|
|
316
|
+
return undefined
|
|
317
317
|
}
|
|
318
318
|
await ensureDir(join(layerDir, dirname(filename)))
|
|
319
319
|
return writeFile(join(layerDir, filename), fileData, {
|
|
@@ -12,7 +12,7 @@ export default class InvocationsController {
|
|
|
12
12
|
const functionNames = this.#lambda.listFunctionNames()
|
|
13
13
|
if (functionNames.length === 0 || !functionNames.includes(functionName)) {
|
|
14
14
|
log.error(
|
|
15
|
-
`Attempt to invoke function '${functionName}' failed. Function does not
|
|
15
|
+
`Attempt to invoke function '${functionName}' failed. Function does not exist.`,
|
|
16
16
|
)
|
|
17
17
|
// Conforms to the actual response from AWS Lambda when invoking a non-existent
|
|
18
18
|
// function. Details on the error are provided in the Payload.Message key
|