serverless-offline 14.7.2 → 14.7.4
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 +12 -12
- package/package.json +7 -7
- package/src/config/supportedRuntimes.js +2 -0
- package/src/events/alb/HttpServer.js +1 -1
- package/src/events/http/HttpServer.js +2 -7
- package/src/events/websocket/WebSocketClients.js +1 -10
- package/src/lambda/handler-runner/HandlerRunner.js +12 -2
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +2 -2
- package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +1 -0
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ Default: localhost
|
|
|
155
155
|
|
|
156
156
|
#### dockerHostServicePath
|
|
157
157
|
|
|
158
|
-
Defines service path
|
|
158
|
+
Defines the service path used by SLS running inside a Docker container.
|
|
159
159
|
|
|
160
160
|
#### dockerNetwork
|
|
161
161
|
|
|
@@ -284,14 +284,14 @@ By default you can send your requests to `http://localhost:3000/`. Please note t
|
|
|
284
284
|
|
|
285
285
|
- You'll need to restart the plugin if you modify your `serverless.yml` or any of the default velocity template files.
|
|
286
286
|
- When no Content-Type header is set on a request, API Gateway defaults to `application/json`, and so does the plugin.
|
|
287
|
-
But if you send an `application/x-www-form-urlencoded` or a `multipart/form-data` body with an `application/json` (or no) Content-Type, API Gateway won't parse your data (you'll get the ugly raw as input), whereas the plugin will answer 400 (malformed JSON).
|
|
287
|
+
But if you send an `application/x-www-form-urlencoded` or a `multipart/form-data` body with an `application/json` (or no) Content-Type, API Gateway won't parse your data (you'll get the ugly raw data as input), whereas the plugin will answer 400 (malformed JSON).
|
|
288
288
|
Please consider explicitly setting your requests' Content-Type and using separate templates.
|
|
289
289
|
|
|
290
290
|
## Run modes
|
|
291
291
|
|
|
292
292
|
### node.js
|
|
293
293
|
|
|
294
|
-
Lambda handlers with `serverless-offline` for the `node.js` runtime can run in different execution modes
|
|
294
|
+
Lambda handlers with `serverless-offline` for the `node.js` runtime can run in different execution modes, each with its own pros and cons. They are currently mutually exclusive and it's not possible to use a combination, e.g. use `in-process` for one Lambda, and `worker-threads` for another. It is planned to combine the flags into one single flag in the future and also add support for combining run modes.
|
|
295
295
|
|
|
296
296
|
#### worker-threads (default)
|
|
297
297
|
|
|
@@ -466,7 +466,7 @@ If you're using least-privilege principals for your AWS roles, this policy shoul
|
|
|
466
466
|
}
|
|
467
467
|
```
|
|
468
468
|
|
|
469
|
-
Once you run a function that boots up the Docker container, it'll look through the layers for that function, download them
|
|
469
|
+
Once you run a function that boots up the Docker container, it'll look through the layers for that function, download them to your layers folder, and save a hash of your layers so it can be re-used in the future. You'll only need to re-download your layers if they change in the future. If you want your layers to re-download, simply remove your layers folder.
|
|
470
470
|
|
|
471
471
|
You should then be able to invoke functions as normal, and they're executed against the layers in your docker container.
|
|
472
472
|
|
|
@@ -490,11 +490,11 @@ When running Docker Lambda inside another Docker container, you may need to over
|
|
|
490
490
|
|
|
491
491
|
#### dockerNetwork
|
|
492
492
|
|
|
493
|
-
When running Docker Lambda inside another Docker container, you may need to override network that Docker Lambda connects to in order to communicate with other containers.
|
|
493
|
+
When running Docker Lambda inside another Docker container, you may need to override the network that Docker Lambda connects to in order to communicate with other containers.
|
|
494
494
|
|
|
495
495
|
#### dockerReadOnly
|
|
496
496
|
|
|
497
|
-
For certain programming languages and frameworks, it's desirable to be able to write to the filesystem for things like testing with local SQLite databases, or other testing-only modifications. For this, you can set `dockerReadOnly: false`, and this will allow local filesystem modifications. This does not strictly mimic AWS Lambda, as Lambda has a
|
|
497
|
+
For certain programming languages and frameworks, it's desirable to be able to write to the filesystem for things like testing with local SQLite databases, or other testing-only modifications. For this, you can set `dockerReadOnly: false`, and this will allow local filesystem modifications. This does not strictly mimic AWS Lambda, as Lambda has a read-only filesystem, so this should be used as a last resort.
|
|
498
498
|
|
|
499
499
|
#### layersDir
|
|
500
500
|
|
|
@@ -506,7 +506,7 @@ By default layers are downloaded on a per-project basis, however, if you want to
|
|
|
506
506
|
|
|
507
507
|
As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method.
|
|
508
508
|
|
|
509
|
-
Serverless-offline will emulate the behaviour of APIG and create a random token that's printed on the screen. With this token you can access your private methods adding `x-api-key: generatedToken` to your request header. All
|
|
509
|
+
Serverless-offline will emulate the behaviour of APIG and create a random token that's printed on the screen. With this token you can access your private methods by adding `x-api-key: generatedToken` to your request header. All API keys will share the same token.
|
|
510
510
|
|
|
511
511
|
### Custom authorizers
|
|
512
512
|
|
|
@@ -524,7 +524,7 @@ The Custom authorizer is passed an `event` object as below:
|
|
|
524
524
|
|
|
525
525
|
The `methodArn` does not include the Account id or API id.
|
|
526
526
|
|
|
527
|
-
The plugin only supports retrieving
|
|
527
|
+
The plugin only supports retrieving tokens from headers. You can configure the header as below:
|
|
528
528
|
|
|
529
529
|
```js
|
|
530
530
|
"authorizer": {
|
|
@@ -775,7 +775,7 @@ There's support for [websocketsApiRouteSelectionExpression](https://docs.aws.ama
|
|
|
775
775
|
|
|
776
776
|
## Debug process
|
|
777
777
|
|
|
778
|
-
The Serverless offline plugin will respond to the overall framework settings and output additional information to the console in debug mode. In order to do this you will have to set the `SLS_DEBUG`
|
|
778
|
+
The Serverless offline plugin will respond to the overall framework settings and output additional information to the console in debug mode. In order to do this you will have to set the `SLS_DEBUG` environment variable. You can run the following in the command line to switch to debug mode execution.
|
|
779
779
|
|
|
780
780
|
> Unix: `export SLS_DEBUG=*`
|
|
781
781
|
|
|
@@ -789,9 +789,9 @@ Initial installation:
|
|
|
789
789
|
For each debug run:
|
|
790
790
|
`node-debug sls offline`
|
|
791
791
|
|
|
792
|
-
The system will start in wait status. This will also automatically start the
|
|
792
|
+
The system will start in wait status. This will also automatically start the Chrome browser and wait for you to set breakpoints for inspection. Set the breakpoints as needed and, then, click the play button for the debugging to continue.
|
|
793
793
|
|
|
794
|
-
Depending on the breakpoint, you may need to call the URL path for your function in separate browser window for your serverless function to be run and made available for debugging.
|
|
794
|
+
Depending on the breakpoint, you may need to call the URL path for your function in a separate browser window for your serverless function to be run and made available for debugging.
|
|
795
795
|
|
|
796
796
|
### Interactive Debugging with Visual Studio Code (VSC)
|
|
797
797
|
|
|
@@ -832,7 +832,7 @@ Example:
|
|
|
832
832
|
}
|
|
833
833
|
```
|
|
834
834
|
|
|
835
|
-
In VSC, you can
|
|
835
|
+
In VSC, you can then add breakpoints to your code. To start a debug session you can either start your script in `package.json` by clicking the hovering debug IntelliSense icon or by going to your debug pane and selecting the Debug Serverless Offline configuration.
|
|
836
836
|
|
|
837
837
|
## Resource permissions and AWS profile
|
|
838
838
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-offline",
|
|
3
|
-
"version": "14.7.
|
|
3
|
+
"version": "14.7.4",
|
|
4
4
|
"description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"hideCredit": true
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aws-sdk/client-lambda": "^3.
|
|
61
|
+
"@aws-sdk/client-lambda": "^3.1069.0",
|
|
62
62
|
"@hapi/boom": "^10.0.1",
|
|
63
63
|
"@hapi/h2o2": "^10.0.4",
|
|
64
64
|
"@hapi/hapi": "^21.4.9",
|
|
@@ -80,24 +80,24 @@
|
|
|
80
80
|
"node-schedule": "^2.1.1",
|
|
81
81
|
"p-memoize": "^7.1.1",
|
|
82
82
|
"tree-kill": "^1.2.2",
|
|
83
|
-
"tsx": "^4.22.
|
|
83
|
+
"tsx": "^4.22.4",
|
|
84
84
|
"velocityjs": "^2.1.6",
|
|
85
85
|
"ws": "^8.21.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@istanbuljs/esm-loader-hook": "^0.3.0",
|
|
89
89
|
"archiver": "^7.0.1",
|
|
90
|
-
"auto-changelog": "^2.
|
|
90
|
+
"auto-changelog": "^2.6.0",
|
|
91
91
|
"eslint": "^8.57.0",
|
|
92
92
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
93
93
|
"eslint-config-prettier": "^9.1.0",
|
|
94
94
|
"eslint-plugin-import": "^2.32.0",
|
|
95
|
-
"eslint-plugin-prettier": "^5.5.
|
|
95
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
96
96
|
"eslint-plugin-unicorn": "^54.0.0",
|
|
97
97
|
"mocha": "^11.7.6",
|
|
98
98
|
"nyc": "^17.0.0",
|
|
99
|
-
"prettier": "^3.8.
|
|
100
|
-
"serverless": "^4.
|
|
99
|
+
"prettier": "^3.8.4",
|
|
100
|
+
"serverless": "^4.37.0"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
103
|
"serverless": "^4.0.0"
|
|
@@ -25,6 +25,7 @@ export const supportedRuntimesArchitecture = {
|
|
|
25
25
|
"ruby3.2": [ARM64, X86_64],
|
|
26
26
|
"ruby3.3": [ARM64, X86_64],
|
|
27
27
|
"ruby3.4": [ARM64, X86_64],
|
|
28
|
+
"ruby4.0": [ARM64, X86_64],
|
|
28
29
|
java8: [X86_64],
|
|
29
30
|
"java8.al2": [ARM64, X86_64],
|
|
30
31
|
java11: [ARM64, X86_64],
|
|
@@ -74,6 +75,7 @@ export const supportedRuby = new Set([
|
|
|
74
75
|
"ruby3.2",
|
|
75
76
|
"ruby3.3",
|
|
76
77
|
"ruby3.4",
|
|
78
|
+
"ruby4.0",
|
|
77
79
|
])
|
|
78
80
|
|
|
79
81
|
export const supportedRuntimes = new Set([
|
|
@@ -168,7 +168,7 @@ export default class HttpServer {
|
|
|
168
168
|
return async (request, h) => {
|
|
169
169
|
const requestPath = this.#options.noPrependStageInUrl
|
|
170
170
|
? request.path
|
|
171
|
-
: request.path.
|
|
171
|
+
: request.path.slice(`/${stage}`.length)
|
|
172
172
|
|
|
173
173
|
// Payload processing
|
|
174
174
|
const encoding = detectEncoding(request)
|
|
@@ -451,7 +451,7 @@ export default class HttpServer {
|
|
|
451
451
|
const requestPath =
|
|
452
452
|
endpoint.isHttpApi || this.#options.noPrependStageInUrl
|
|
453
453
|
? request.path
|
|
454
|
-
: request.path.
|
|
454
|
+
: request.path.slice(`/${stage}`.length)
|
|
455
455
|
|
|
456
456
|
// payload processing
|
|
457
457
|
const encoding = detectEncoding(request)
|
|
@@ -537,14 +537,9 @@ export default class HttpServer {
|
|
|
537
537
|
|
|
538
538
|
if (
|
|
539
539
|
contentTypesThatRequirePayloadParsing.includes(contentType) &&
|
|
540
|
-
request.payload
|
|
541
|
-
request.payload.length > 1
|
|
540
|
+
request.payload
|
|
542
541
|
) {
|
|
543
542
|
try {
|
|
544
|
-
if (!request.payload || request.payload.length === 0) {
|
|
545
|
-
request.payload = "{}"
|
|
546
|
-
}
|
|
547
|
-
|
|
548
543
|
request.payload = parse(request.payload)
|
|
549
544
|
} catch (err) {
|
|
550
545
|
log.debug("error in converting request.payload to JSON:", err)
|
|
@@ -273,17 +273,8 @@ export default class WebSocketClients {
|
|
|
273
273
|
log.debug(`Error in route handler '${route.functionKey}'`, err)
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
const authorizerData = this.#webSocketAuthorizersCache.get(connectionId)
|
|
277
|
-
let authorizedEvent
|
|
278
|
-
|
|
279
|
-
if (authorizerData) {
|
|
280
|
-
authorizedEvent = event
|
|
281
|
-
authorizedEvent.requestContext.identity = authorizerData.identity
|
|
282
|
-
authorizedEvent.requestContext.authorizer = authorizerData.authorizer
|
|
283
|
-
}
|
|
284
|
-
|
|
285
276
|
const lambdaFunction = this.#lambda.get(route.functionKey)
|
|
286
|
-
lambdaFunction.setEvent(
|
|
277
|
+
lambdaFunction.setEvent(event)
|
|
287
278
|
|
|
288
279
|
try {
|
|
289
280
|
const { body } = await lambdaFunction.runHandler()
|
|
@@ -98,9 +98,19 @@ export default class HandlerRunner {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// () => Promise<void>
|
|
101
|
-
cleanup() {
|
|
101
|
+
async cleanup() {
|
|
102
102
|
// TODO console.log('handler runner cleanup')
|
|
103
|
-
|
|
103
|
+
const runner = this.#runner
|
|
104
|
+
|
|
105
|
+
// drop the reference so the next run() lazily recreates a fresh runner.
|
|
106
|
+
// this is essential after a timeout: the worker thread is terminated by
|
|
107
|
+
// cleanup() but the (now dead) runner would otherwise be reused, wedging
|
|
108
|
+
// every subsequent invocation. see https://github.com/dherault/serverless-offline/issues/1896
|
|
109
|
+
this.#runner = null
|
|
110
|
+
|
|
111
|
+
if (runner != null) {
|
|
112
|
+
await runner.cleanup()
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
async run(event, context) {
|
|
@@ -8,7 +8,7 @@ import { log } from "../../../utils/log.js"
|
|
|
8
8
|
import { splitHandlerPathAndName } from "../../../utils/index.js"
|
|
9
9
|
|
|
10
10
|
const { parse, stringify } = JSON
|
|
11
|
-
const {
|
|
11
|
+
const { hasOwn } = Object
|
|
12
12
|
|
|
13
13
|
export default class PythonRunner {
|
|
14
14
|
static #payloadIdentifier = "__offline_payload__"
|
|
@@ -47,7 +47,7 @@ export default class PythonRunner {
|
|
|
47
47
|
handlerName,
|
|
48
48
|
],
|
|
49
49
|
{
|
|
50
|
-
env:
|
|
50
|
+
env: { ...process.env, ...this.#env },
|
|
51
51
|
shell: true,
|
|
52
52
|
},
|
|
53
53
|
)
|