serverless-offline 11.1.2 → 11.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dedicatedTo": "Blue, a great migrating bird.",
3
3
  "name": "serverless-offline",
4
- "version": "11.1.2",
4
+ "version": "11.2.0",
5
5
  "description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -85,14 +85,14 @@
85
85
  "@hapi/boom": "^10.0.0",
86
86
  "@hapi/h2o2": "^10.0.0",
87
87
  "@hapi/hapi": "^20.2.2",
88
- "@serverless/utils": "^6.8.0",
89
- "aws-sdk": "^2.1231.0",
88
+ "@serverless/utils": "^6.8.1",
89
+ "aws-sdk": "^2.1241.0",
90
90
  "boxen": "^7.0.0",
91
91
  "chalk": "^5.1.2",
92
92
  "execa": "^6.1.0",
93
93
  "fs-extra": "^10.1.0",
94
94
  "java-invoke-local": "0.0.6",
95
- "jose": "^4.10.0",
95
+ "jose": "^4.10.3",
96
96
  "js-string-escape": "^1.0.1",
97
97
  "jsonpath-plus": "^7.2.0",
98
98
  "jsonschema": "^1.4.1",
@@ -104,12 +104,12 @@
104
104
  "p-memoize": "^7.1.1",
105
105
  "p-retry": "^5.1.1",
106
106
  "velocityjs": "^2.0.6",
107
- "ws": "^8.9.0"
107
+ "ws": "^8.10.0"
108
108
  },
109
109
  "devDependencies": {
110
110
  "@istanbuljs/esm-loader-hook": "^0.2.0",
111
111
  "archiver": "^5.3.1",
112
- "eslint": "^8.25.0",
112
+ "eslint": "^8.26.0",
113
113
  "eslint-config-airbnb-base": "^15.0.0",
114
114
  "eslint-config-prettier": "^8.5.0",
115
115
  "eslint-plugin-import": "^2.25.4",
@@ -117,7 +117,7 @@
117
117
  "git-list-updated": "^1.2.1",
118
118
  "husky": "^8.0.1",
119
119
  "lint-staged": "^13.0.3",
120
- "mocha": "^10.0.0",
120
+ "mocha": "^10.1.0",
121
121
  "nyc": "^15.1.0",
122
122
  "prettier": "^2.7.1",
123
123
  "serverless": "^3.23.0",
@@ -61,8 +61,13 @@ export default class ServerlessOffline {
61
61
  async start() {
62
62
  this.#mergeOptions()
63
63
 
64
- const { httpEvents, lambdas, scheduleEvents, webSocketEvents } =
65
- this.#getEvents()
64
+ const {
65
+ httpEvents,
66
+ httpApiEvents,
67
+ lambdas,
68
+ scheduleEvents,
69
+ webSocketEvents,
70
+ } = this.#getEvents()
66
71
 
67
72
  if (lambdas.length > 0) {
68
73
  await this.#createLambda(lambdas)
@@ -70,8 +75,8 @@ export default class ServerlessOffline {
70
75
 
71
76
  const eventModules = []
72
77
 
73
- if (httpEvents.length > 0) {
74
- eventModules.push(this.#createHttp(httpEvents))
78
+ if (httpApiEvents.length > 0 || httpEvents.length > 0) {
79
+ eventModules.push(this.#createHttp([...httpApiEvents, ...httpEvents]))
75
80
  }
76
81
 
77
82
  if (scheduleEvents.length > 0) {
@@ -259,6 +264,7 @@ export default class ServerlessOffline {
259
264
  const { service } = this.#serverless
260
265
 
261
266
  const httpEvents = []
267
+ const httpApiEvents = []
262
268
  const lambdas = []
263
269
  const scheduleEvents = []
264
270
  const webSocketEvents = []
@@ -275,66 +281,75 @@ export default class ServerlessOffline {
275
281
  events.forEach((event) => {
276
282
  const { http, httpApi, schedule, websocket } = event
277
283
 
278
- if ((http || httpApi) && functionDefinition.handler) {
284
+ if (http && functionDefinition.handler) {
279
285
  const httpEvent = {
280
286
  functionKey,
281
287
  handler: functionDefinition.handler,
282
- http: http || httpApi,
288
+ http,
283
289
  }
284
290
 
285
- if (httpApi) {
286
- // Ensure definitions for 'httpApi' events are objects so that they can be marked
287
- // with an 'isHttpApi' property (they are handled differently to 'http' events)
288
- if (typeof httpEvent.http === 'string') {
289
- httpEvent.http = {
290
- routeKey: httpEvent.http === '*' ? '$default' : httpEvent.http,
291
- }
292
- } else if (typeof httpEvent.http === 'object') {
293
- if (!httpEvent.http.method) {
294
- log.warning(
295
- `Event definition is missing a method for function "${functionKey}"`,
296
- )
297
- httpEvent.http.method = ''
298
- }
299
- if (
300
- httpEvent.http.method === '*' &&
301
- httpEvent.http.path === '*'
302
- ) {
303
- httpEvent.http.routeKey = '$default'
304
- } else {
305
- const resolvedMethod =
306
- httpEvent.http.method === '*'
307
- ? 'ANY'
308
- : httpEvent.http.method.toUpperCase()
309
- httpEvent.http.routeKey = `${resolvedMethod} ${httpEvent.http.path}`
310
- }
311
- // Clear these properties to avoid confusion (they will be derived from the routeKey
312
- // when needed later)
313
- delete httpEvent.http.method
314
- delete httpEvent.http.path
315
- } else {
291
+ httpEvents.push(httpEvent)
292
+ }
293
+
294
+ if (httpApi && functionDefinition.handler) {
295
+ const httpApiEvent = {
296
+ functionKey,
297
+ handler: functionDefinition.handler,
298
+ http: httpApi,
299
+ }
300
+
301
+ // Ensure definitions for 'httpApi' events are objects so that they can be marked
302
+ // with an 'isHttpApi' property (they are handled differently to 'http' events)
303
+ if (typeof httpApiEvent.http === 'string') {
304
+ httpApiEvent.http = {
305
+ routeKey:
306
+ httpApiEvent.http === '*' ? '$default' : httpApiEvent.http,
307
+ }
308
+ } else if (typeof httpApiEvent.http === 'object') {
309
+ if (!httpApiEvent.http.method) {
316
310
  log.warning(
317
- `Event definition must be a string or object but received ${typeof httpEvent.http} for function "${functionKey}"`,
311
+ `Event definition is missing a method for function "${functionKey}"`,
318
312
  )
319
- httpEvent.http.routeKey = ''
313
+ httpApiEvent.http.method = ''
320
314
  }
321
-
322
- httpEvent.http.isHttpApi = true
323
-
324
315
  if (
325
- functionDefinition.httpApi &&
326
- functionDefinition.httpApi.payload
316
+ httpApiEvent.http.method === '*' &&
317
+ httpApiEvent.http.path === '*'
327
318
  ) {
328
- httpEvent.http.payload = functionDefinition.httpApi.payload
319
+ httpApiEvent.http.routeKey = '$default'
329
320
  } else {
330
- httpEvent.http.payload =
331
- service.provider.httpApi && service.provider.httpApi.payload
332
- ? service.provider.httpApi.payload
333
- : '2.0'
321
+ const resolvedMethod =
322
+ httpApiEvent.http.method === '*'
323
+ ? 'ANY'
324
+ : httpApiEvent.http.method.toUpperCase()
325
+ httpApiEvent.http.routeKey = `${resolvedMethod} ${httpApiEvent.http.path}`
334
326
  }
327
+ // Clear these properties to avoid confusion (they will be derived from the routeKey
328
+ // when needed later)
329
+ delete httpApiEvent.http.method
330
+ delete httpApiEvent.http.path
331
+ } else {
332
+ log.warning(
333
+ `Event definition must be a string or object but received ${typeof httpApiEvent.http} for function "${functionKey}"`,
334
+ )
335
+ httpApiEvent.http.routeKey = ''
335
336
  }
336
337
 
337
- httpEvents.push(httpEvent)
338
+ httpApiEvent.http.isHttpApi = true
339
+
340
+ if (
341
+ functionDefinition.httpApi &&
342
+ functionDefinition.httpApi.payload
343
+ ) {
344
+ httpApiEvent.http.payload = functionDefinition.httpApi.payload
345
+ } else {
346
+ httpApiEvent.http.payload =
347
+ service.provider.httpApi && service.provider.httpApi.payload
348
+ ? service.provider.httpApi.payload
349
+ : '2.0'
350
+ }
351
+
352
+ httpApiEvents.push(httpApiEvent)
338
353
  }
339
354
 
340
355
  if (schedule) {
@@ -354,6 +369,7 @@ export default class ServerlessOffline {
354
369
  })
355
370
 
356
371
  return {
372
+ httpApiEvents,
357
373
  httpEvents,
358
374
  lambdas,
359
375
  scheduleEvents,
@@ -4,8 +4,16 @@ import HttpServer from './HttpServer.js'
4
4
  export default class Http {
5
5
  #httpServer = null
6
6
 
7
+ #lambda = null
8
+
9
+ #options = null
10
+
11
+ #serverless = null
12
+
7
13
  constructor(serverless, options, lambda) {
8
- this.#httpServer = new HttpServer(serverless, options, lambda)
14
+ this.#lambda = lambda
15
+ this.#options = options
16
+ this.#serverless = serverless
9
17
  }
10
18
 
11
19
  start() {
@@ -18,6 +26,12 @@ export default class Http {
18
26
  }
19
27
 
20
28
  async createServer() {
29
+ this.#httpServer = new HttpServer(
30
+ this.#serverless,
31
+ this.#options,
32
+ this.#lambda,
33
+ )
34
+
21
35
  await this.#httpServer.createServer()
22
36
  }
23
37
 
@@ -459,12 +459,12 @@ export default class HttpServer {
459
459
 
460
460
  // default request template to '' if we don't have a definition pushed in from serverless or endpoint
461
461
  const requestTemplate =
462
- typeof requestTemplates !== 'undefined' && integration === 'AWS'
462
+ requestTemplates !== undefined && integration === 'AWS'
463
463
  ? requestTemplates[contentType]
464
464
  : ''
465
465
 
466
466
  const schemas =
467
- typeof endpoint?.request?.schemas !== 'undefined'
467
+ endpoint?.request?.schemas !== undefined
468
468
  ? endpoint.request.schemas[contentType]
469
469
  : ''
470
470
 
@@ -647,10 +647,7 @@ export default class HttpServer {
647
647
  headerValue = valueArray[3]
648
648
  ? jsonPath(result, valueArray.slice(3).join('.'))
649
649
  : result
650
- if (
651
- typeof headerValue === 'undefined' ||
652
- headerValue === null
653
- ) {
650
+ if (headerValue === undefined || headerValue === null) {
654
651
  headerValue = ''
655
652
  } else {
656
653
  headerValue = headerValue.toString()
@@ -857,7 +854,7 @@ export default class HttpServer {
857
854
 
858
855
  if (typeof result === 'string') {
859
856
  response.source = stringify(result)
860
- } else if (result && typeof result.body !== 'undefined') {
857
+ } else if (result && result.body !== undefined) {
861
858
  if (result.isBase64Encoded) {
862
859
  response.encoding = 'binary'
863
860
  response.source = Buffer.from(result.body, 'base64')
@@ -104,7 +104,7 @@ export default class LambdaProxyIntegrationEvent {
104
104
  ) {
105
105
  headers['Content-Type'] = 'application/json'
106
106
  }
107
- } else if (typeof body === 'undefined' || body === '') {
107
+ } else if (body === undefined || body === '') {
108
108
  body = null
109
109
  }
110
110
 
@@ -87,7 +87,7 @@ export default class LambdaProxyIntegrationEventV2 {
87
87
  if (!headers['content-type']) {
88
88
  headers['content-type'] = 'application/json'
89
89
  }
90
- } else if (typeof body === 'undefined' || body === '') {
90
+ } else if (body === undefined || body === '') {
91
91
  body = null
92
92
  }
93
93
 
@@ -116,6 +116,9 @@ export default class HandlerRunner {
116
116
  log.error(
117
117
  `Unhandled exception in handler '${this.#funOptions.functionKey}'.`,
118
118
  )
119
+
120
+ log.error(err)
121
+
119
122
  throw err
120
123
  }
121
124
  }
@@ -43,7 +43,7 @@ export default function invocationsRoute(lambda, options) {
43
43
  let statusCode = 200
44
44
  let functionError = null
45
45
  if (invokeResults) {
46
- const isPayloadDefined = typeof invokeResults.Payload !== 'undefined'
46
+ const isPayloadDefined = invokeResults.Payload !== undefined
47
47
  resultPayload = isPayloadDefined ? invokeResults.Payload : ''
48
48
  statusCode = invokeResults.StatusCode || 200
49
49
  functionError = invokeResults.FunctionError || null