serverless-offline 9.0.0 → 9.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/README.md +9 -8
- package/package.json +3 -3
- package/src/ServerlessOffline.js +1 -4
- package/src/config/commandOptions.js +4 -0
- package/src/config/constants.js +1 -1
- package/src/config/defaultOptions.js +1 -0
- package/src/events/http/HttpServer.js +1 -36
- package/src/lambda/LambdaFunction.js +18 -17
- package/src/lambda/handler-runner/go-runner/GoRunner.js +4 -3
- package/src/utils/resolveJoins.js +0 -29
package/README.md
CHANGED
|
@@ -127,6 +127,7 @@ All CLI options are optional:
|
|
|
127
127
|
--ignoreJWTSignature When using HttpApi with a JWT authorizer, don't check the signature of the JWT token. This should only be used for local development.
|
|
128
128
|
--lambdaPort Lambda http port to listen on. Default: 3002
|
|
129
129
|
--layersDir The directory layers should be stored in. Default: ${codeDir}/.serverless-offline/layers'
|
|
130
|
+
--localEnvironment Copy local environment variables. Default: false
|
|
130
131
|
--noAuth Turns off all authorizers
|
|
131
132
|
--noPrependStageInUrl Don't prepend http routes with the stage.
|
|
132
133
|
--noStripTrailingSlashInUrl Don't strip trailing slash from http routes.
|
|
@@ -266,14 +267,14 @@ If you're using least-privilege principals for your AWS roles, this policy shoul
|
|
|
266
267
|
|
|
267
268
|
```json
|
|
268
269
|
{
|
|
269
|
-
"Version": "2012-10-17",
|
|
270
270
|
"Statement": [
|
|
271
271
|
{
|
|
272
|
-
"Effect": "Allow",
|
|
273
272
|
"Action": "lambda:GetLayerVersion",
|
|
273
|
+
"Effect": "Allow",
|
|
274
274
|
"Resource": "arn:aws:lambda:*:*:layer:*:*"
|
|
275
275
|
}
|
|
276
|
-
]
|
|
276
|
+
],
|
|
277
|
+
"Version": "2012-10-17"
|
|
277
278
|
}
|
|
278
279
|
```
|
|
279
280
|
|
|
@@ -603,13 +604,13 @@ Add a new [launch configuration](https://code.visualstudio.com/docs/editor/debug
|
|
|
603
604
|
|
|
604
605
|
```json
|
|
605
606
|
{
|
|
606
|
-
"type": "node",
|
|
607
|
-
"request": "launch",
|
|
608
|
-
"name": "Debug Serverless Offline",
|
|
609
607
|
"cwd": "${workspaceFolder}",
|
|
610
|
-
"
|
|
608
|
+
"name": "Debug Serverless Offline",
|
|
609
|
+
"request": "launch",
|
|
611
610
|
"runtimeArgs": ["run", "debug"],
|
|
612
|
-
"
|
|
611
|
+
"runtimeExecutable": "npm",
|
|
612
|
+
"sourceMaps": true,
|
|
613
|
+
"type": "node"
|
|
613
614
|
}
|
|
614
615
|
```
|
|
615
616
|
|
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": "9.
|
|
4
|
+
"version": "9.1.0",
|
|
5
5
|
"description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./src/index.js",
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"@hapi/boom": "^10.0.0",
|
|
195
195
|
"@hapi/h2o2": "^9.1.0",
|
|
196
196
|
"@hapi/hapi": "^20.2.2",
|
|
197
|
-
"aws-sdk": "^2.
|
|
197
|
+
"aws-sdk": "^2.1181.0",
|
|
198
198
|
"boxen": "^7.0.0",
|
|
199
199
|
"chalk": "^5.0.1",
|
|
200
200
|
"execa": "^6.1.0",
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
"jsonwebtoken": "^8.5.1",
|
|
207
207
|
"jszip": "^3.10.0",
|
|
208
208
|
"luxon": "^3.0.1",
|
|
209
|
-
"node-fetch": "^3.2.
|
|
209
|
+
"node-fetch": "^3.2.9",
|
|
210
210
|
"node-schedule": "^2.1.0",
|
|
211
211
|
"object.hasown": "^1.1.1",
|
|
212
212
|
"p-memoize": "^7.1.0",
|
package/src/ServerlessOffline.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import process, {
|
|
1
|
+
import process, { exit } from 'node:process'
|
|
2
2
|
import { log } from '@serverless/utils/log.js'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
import {
|
|
@@ -59,9 +59,6 @@ export default class ServerlessOffline {
|
|
|
59
59
|
|
|
60
60
|
// Entry point for the plugin (sls offline) when running 'sls offline start'
|
|
61
61
|
async start() {
|
|
62
|
-
// Put here so available everywhere, not just in handlers
|
|
63
|
-
env.IS_OFFLINE = true
|
|
64
|
-
|
|
65
62
|
this.#mergeOptions()
|
|
66
63
|
|
|
67
64
|
const { httpEvents, lambdas, scheduleEvents, webSocketEvents } =
|
|
@@ -91,6 +91,10 @@ export default {
|
|
|
91
91
|
usage:
|
|
92
92
|
'The directory layers should be stored in. Default: {codeDir}/.serverless-offline/layers',
|
|
93
93
|
},
|
|
94
|
+
localEnvironment: {
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
usage: 'Copy local environment variables. Default: false',
|
|
97
|
+
},
|
|
94
98
|
noAuth: {
|
|
95
99
|
type: 'boolean',
|
|
96
100
|
usage: 'Turns off all authorizers',
|
package/src/config/constants.js
CHANGED
|
@@ -4,7 +4,7 @@ export const BASE_URL_PLACEHOLDER = 'http://example'
|
|
|
4
4
|
|
|
5
5
|
export const CUSTOM_OPTION = 'serverless-offline'
|
|
6
6
|
|
|
7
|
-
export const DEFAULT_LAMBDA_RUNTIME = '
|
|
7
|
+
export const DEFAULT_LAMBDA_RUNTIME = 'nodejs14.x'
|
|
8
8
|
|
|
9
9
|
// https://docs.aws.amazon.com/lambda/latest/dg/limits.html
|
|
10
10
|
export const DEFAULT_LAMBDA_MEMORY_SIZE = 1024
|
|
@@ -2,7 +2,7 @@ import { Buffer } from 'node:buffer'
|
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
import { createRequire } from 'node:module'
|
|
4
4
|
import { join, resolve } from 'node:path'
|
|
5
|
-
import
|
|
5
|
+
import { exit } from 'node:process'
|
|
6
6
|
import h2o2 from '@hapi/h2o2'
|
|
7
7
|
import { Server } from '@hapi/hapi'
|
|
8
8
|
import { log } from '@serverless/utils/log.js'
|
|
@@ -35,8 +35,6 @@ const { assign, entries, keys } = Object
|
|
|
35
35
|
export default class HttpServer {
|
|
36
36
|
#lambda = null
|
|
37
37
|
|
|
38
|
-
#lastRequestOptions = null
|
|
39
|
-
|
|
40
38
|
#options = null
|
|
41
39
|
|
|
42
40
|
#serverless = null
|
|
@@ -198,17 +196,6 @@ export default class HttpServer {
|
|
|
198
196
|
const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`
|
|
199
197
|
|
|
200
198
|
log.notice(`Server ready: ${server} 🚀`)
|
|
201
|
-
log.notice()
|
|
202
|
-
log.notice('Enter "rp" to replay the last request')
|
|
203
|
-
|
|
204
|
-
process.openStdin().addListener('data', (data) => {
|
|
205
|
-
// note: data is an object, and when converted to a string it will
|
|
206
|
-
// end with a linefeed. so we (rather crudely) account for that
|
|
207
|
-
// with toString() and then trim()
|
|
208
|
-
if (data.toString().trim() === 'rp') {
|
|
209
|
-
this.#injectLastRequest()
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
199
|
}
|
|
213
200
|
|
|
214
201
|
// stops the server
|
|
@@ -513,24 +500,11 @@ export default class HttpServer {
|
|
|
513
500
|
hapiOptions.tags = ['api']
|
|
514
501
|
|
|
515
502
|
const hapiHandler = async (request, h) => {
|
|
516
|
-
// Here we go
|
|
517
|
-
// Store current request as the last one
|
|
518
|
-
this.#lastRequestOptions = {
|
|
519
|
-
headers: request.headers,
|
|
520
|
-
method: request.method,
|
|
521
|
-
payload: request.payload,
|
|
522
|
-
url: request.url.href,
|
|
523
|
-
}
|
|
524
|
-
|
|
525
503
|
const requestPath =
|
|
526
504
|
endpoint.isHttpApi || this.#options.noPrependStageInUrl
|
|
527
505
|
? request.path
|
|
528
506
|
: request.path.substr(`/${stage}`.length)
|
|
529
507
|
|
|
530
|
-
if (request.auth.credentials && request.auth.strategy) {
|
|
531
|
-
this.#lastRequestOptions.auth = request.auth
|
|
532
|
-
}
|
|
533
|
-
|
|
534
508
|
// Payload processing
|
|
535
509
|
const encoding = detectEncoding(request)
|
|
536
510
|
|
|
@@ -1257,15 +1231,6 @@ export default class HttpServer {
|
|
|
1257
1231
|
.map((line) => line.trim())
|
|
1258
1232
|
}
|
|
1259
1233
|
|
|
1260
|
-
#injectLastRequest() {
|
|
1261
|
-
if (this.#lastRequestOptions) {
|
|
1262
|
-
log.notice('Replaying HTTP last request')
|
|
1263
|
-
this.#server.inject(this.#lastRequestOptions)
|
|
1264
|
-
} else {
|
|
1265
|
-
log.notice('No last HTTP request to replay!')
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
1234
|
writeRoutesTerminal() {
|
|
1270
1235
|
logRoutes(this.#terminalInfo)
|
|
1271
1236
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { dirname, join, resolve } from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
3
4
|
import { performance } from 'node:perf_hooks'
|
|
4
5
|
import { log } from '@serverless/utils/log.js'
|
|
5
6
|
import { emptyDir, ensureDir, remove } from 'fs-extra'
|
|
6
7
|
import jszip from 'jszip'
|
|
7
8
|
import HandlerRunner from './handler-runner/index.js'
|
|
8
9
|
import LambdaContext from './LambdaContext.js'
|
|
9
|
-
import resolveJoins from '../utils/resolveJoins.js'
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_LAMBDA_MEMORY_SIZE,
|
|
12
12
|
DEFAULT_LAMBDA_RUNTIME,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '../config/index.js'
|
|
16
16
|
import { createUniqueId, splitHandlerPathAndName } from '../utils/index.js'
|
|
17
17
|
|
|
18
|
-
const { entries } = Object
|
|
18
|
+
const { entries, fromEntries } = Object
|
|
19
19
|
const { ceil } = Math
|
|
20
20
|
|
|
21
21
|
export default class LambdaFunction {
|
|
@@ -35,6 +35,8 @@ export default class LambdaFunction {
|
|
|
35
35
|
|
|
36
36
|
#functionName = null
|
|
37
37
|
|
|
38
|
+
#handler = null
|
|
39
|
+
|
|
38
40
|
#handlerRunner = null
|
|
39
41
|
|
|
40
42
|
#idleTimeStarted = null
|
|
@@ -86,6 +88,7 @@ export default class LambdaFunction {
|
|
|
86
88
|
// this._executionTimeout = null
|
|
87
89
|
this.#functionKey = functionKey
|
|
88
90
|
this.#functionName = name
|
|
91
|
+
this.#handler = handler
|
|
89
92
|
this.#memorySize = memorySize
|
|
90
93
|
this.#region = provider.region
|
|
91
94
|
this.#runtime = runtime
|
|
@@ -93,11 +96,18 @@ export default class LambdaFunction {
|
|
|
93
96
|
|
|
94
97
|
this.#verifySupportedRuntime()
|
|
95
98
|
|
|
96
|
-
const env =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const env = {
|
|
100
|
+
...(options.localEnvironment
|
|
101
|
+
? process.env
|
|
102
|
+
: // we always copy all AWS_xxxx environment variables over from local env
|
|
103
|
+
fromEntries(
|
|
104
|
+
entries(process.env).filter(([key]) => key.startsWith('AWS_')),
|
|
105
|
+
)),
|
|
106
|
+
...this.#getAwsEnvVars(),
|
|
107
|
+
...provider.environment,
|
|
108
|
+
...functionDefinition.environment,
|
|
109
|
+
IS_OFFLINE: 'true',
|
|
110
|
+
}
|
|
101
111
|
|
|
102
112
|
this.#artifact = functionDefinition.package?.artifact
|
|
103
113
|
|
|
@@ -176,6 +186,7 @@ export default class LambdaFunction {
|
|
|
176
186
|
// https://github.com/serverless/serverless/blob/v1.50.0/lib/plugins/aws/invokeLocal/index.js#L108
|
|
177
187
|
#getAwsEnvVars() {
|
|
178
188
|
return {
|
|
189
|
+
_HANDLER: this.#handler,
|
|
179
190
|
AWS_DEFAULT_REGION: this.#region,
|
|
180
191
|
AWS_LAMBDA_FUNCTION_MEMORY_SIZE: this.#memorySize,
|
|
181
192
|
AWS_LAMBDA_FUNCTION_NAME: this.#functionName,
|
|
@@ -194,16 +205,6 @@ export default class LambdaFunction {
|
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
207
|
|
|
197
|
-
#getEnv(providerEnv, functionDefinitionEnv, handler) {
|
|
198
|
-
return {
|
|
199
|
-
...this.#getAwsEnvVars(),
|
|
200
|
-
...providerEnv,
|
|
201
|
-
...functionDefinitionEnv,
|
|
202
|
-
_HANDLER: handler, // TODO is this available in AWS?
|
|
203
|
-
IS_OFFLINE: true,
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
208
|
setClientContext(clientContext) {
|
|
208
209
|
this.#clientContext = clientContext
|
|
209
210
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, rmdir, writeFile } from 'node:fs/promises'
|
|
1
|
+
import { mkdir, readFile, rm, rmdir, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { EOL } from 'node:os'
|
|
3
3
|
import { sep, resolve, parse as pathParse } from 'node:path'
|
|
4
4
|
import process, { chdir, cwd } from 'node:process'
|
|
@@ -32,6 +32,9 @@ export default class GoRunner {
|
|
|
32
32
|
|
|
33
33
|
async cleanup() {
|
|
34
34
|
try {
|
|
35
|
+
// refresh go.mod
|
|
36
|
+
await rm(this.#tmpFile)
|
|
37
|
+
execaSync('go', ['mod', 'tidy'])
|
|
35
38
|
await rmdir(this.#tmpPath, { recursive: true })
|
|
36
39
|
} catch {
|
|
37
40
|
// @ignore
|
|
@@ -154,8 +157,6 @@ export default class GoRunner {
|
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
try {
|
|
157
|
-
// refresh go.mod
|
|
158
|
-
execaSync('go', ['mod', 'tidy'])
|
|
159
160
|
chdir(this.#codeDir)
|
|
160
161
|
} catch {
|
|
161
162
|
// @ignore
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const { entries } = Object
|
|
2
|
-
|
|
3
|
-
// Used to resolve Fn::Join in environment variables
|
|
4
|
-
export default function resolveJoins(environment) {
|
|
5
|
-
if (!environment) {
|
|
6
|
-
return undefined
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const newEnv = {}
|
|
10
|
-
|
|
11
|
-
entries(environment).forEach(([key, value]) => {
|
|
12
|
-
if (!value) {
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const joinArray = value['Fn::Join']
|
|
17
|
-
const isJoin = Boolean(joinArray)
|
|
18
|
-
|
|
19
|
-
if (isJoin) {
|
|
20
|
-
const separator = joinArray[0]
|
|
21
|
-
const joined = joinArray[1].join(separator)
|
|
22
|
-
newEnv[key] = joined
|
|
23
|
-
} else {
|
|
24
|
-
newEnv[key] = value
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
return newEnv
|
|
29
|
-
}
|