serverless-offline 13.3.2 → 13.3.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.
Files changed (112) hide show
  1. package/README.md +17 -17
  2. package/package.json +19 -17
  3. package/src/ServerlessOffline.js +42 -42
  4. package/src/config/colors.js +9 -9
  5. package/src/config/commandOptions.js +61 -61
  6. package/src/config/constants.js +5 -5
  7. package/src/config/defaultOptions.js +6 -6
  8. package/src/config/index.js +4 -4
  9. package/src/config/supportedRuntimes.js +18 -18
  10. package/src/errors/index.js +1 -1
  11. package/src/events/alb/Alb.js +2 -2
  12. package/src/events/alb/AlbEventDefinition.js +2 -2
  13. package/src/events/alb/HttpServer.js +54 -54
  14. package/src/events/alb/index.js +1 -1
  15. package/src/events/alb/lambda-events/LambdaAlbRequestEvent.js +2 -2
  16. package/src/events/alb/lambda-events/index.js +1 -1
  17. package/src/events/authCanExecuteResource.js +3 -3
  18. package/src/events/authFunctionNameExtractor.js +9 -9
  19. package/src/events/authMatchPolicyResource.js +8 -8
  20. package/src/events/authValidateContext.js +11 -11
  21. package/src/events/http/Endpoint.js +26 -26
  22. package/src/events/http/Http.js +2 -2
  23. package/src/events/http/HttpEventDefinition.js +2 -2
  24. package/src/events/http/HttpServer.js +157 -158
  25. package/src/events/http/OfflineEndpoint.js +7 -7
  26. package/src/events/http/authJWTSettingsExtractor.js +2 -2
  27. package/src/events/http/createAuthScheme.js +29 -27
  28. package/src/events/http/createJWTAuthScheme.js +12 -12
  29. package/src/events/http/index.js +1 -1
  30. package/src/events/http/javaHelpers.js +2 -2
  31. package/src/events/http/lambda-events/LambdaIntegrationEvent.js +5 -5
  32. package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +47 -47
  33. package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +27 -27
  34. package/src/events/http/lambda-events/VelocityContext.js +28 -32
  35. package/src/events/http/lambda-events/index.js +4 -4
  36. package/src/events/http/lambda-events/renderVelocityTemplateObject.js +13 -13
  37. package/src/events/http/parseResources.js +6 -6
  38. package/src/events/http/payloadSchemaValidator.js +2 -2
  39. package/src/events/schedule/Schedule.js +21 -21
  40. package/src/events/schedule/ScheduleEvent.js +7 -7
  41. package/src/events/schedule/ScheduleEventDefinition.js +1 -1
  42. package/src/events/schedule/index.js +1 -1
  43. package/src/events/websocket/HttpServer.js +9 -9
  44. package/src/events/websocket/WebSocket.js +4 -4
  45. package/src/events/websocket/WebSocketClients.js +29 -29
  46. package/src/events/websocket/WebSocketEventDefinition.js +1 -1
  47. package/src/events/websocket/WebSocketServer.js +9 -9
  48. package/src/events/websocket/http-routes/_catchAll/catchAllRoute.js +3 -3
  49. package/src/events/websocket/http-routes/_catchAll/index.js +1 -1
  50. package/src/events/websocket/http-routes/connections/ConnectionsController.js +1 -1
  51. package/src/events/websocket/http-routes/connections/connectionsRoutes.js +6 -6
  52. package/src/events/websocket/http-routes/connections/index.js +1 -1
  53. package/src/events/websocket/http-routes/index.js +2 -2
  54. package/src/events/websocket/index.js +1 -1
  55. package/src/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +5 -5
  56. package/src/events/websocket/lambda-events/WebSocketConnectEvent.js +7 -6
  57. package/src/events/websocket/lambda-events/WebSocketDisconnectEvent.js +5 -5
  58. package/src/events/websocket/lambda-events/WebSocketEvent.js +2 -2
  59. package/src/events/websocket/lambda-events/WebSocketRequestContext.js +12 -11
  60. package/src/events/websocket/lambda-events/index.js +4 -4
  61. package/src/index.js +1 -1
  62. package/src/lambda/HttpServer.js +11 -11
  63. package/src/lambda/Lambda.js +2 -2
  64. package/src/lambda/LambdaContext.js +1 -1
  65. package/src/lambda/LambdaFunction.js +34 -34
  66. package/src/lambda/LambdaFunctionPool.js +3 -3
  67. package/src/lambda/handler-runner/HandlerRunner.js +12 -12
  68. package/src/lambda/handler-runner/docker-runner/DockerContainer.js +59 -59
  69. package/src/lambda/handler-runner/docker-runner/DockerImage.js +6 -6
  70. package/src/lambda/handler-runner/docker-runner/DockerRunner.js +2 -2
  71. package/src/lambda/handler-runner/docker-runner/index.js +1 -1
  72. package/src/lambda/handler-runner/go-runner/GoRunner.js +34 -34
  73. package/src/lambda/handler-runner/go-runner/index.js +1 -1
  74. package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +7 -7
  75. package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js +52 -52
  76. package/src/lambda/handler-runner/in-process-runner/index.js +1 -1
  77. package/src/lambda/handler-runner/index.js +1 -1
  78. package/src/lambda/handler-runner/java-runner/JavaRunner.js +13 -13
  79. package/src/lambda/handler-runner/java-runner/index.js +1 -1
  80. package/src/lambda/handler-runner/python-runner/PythonRunner.js +21 -21
  81. package/src/lambda/handler-runner/python-runner/index.js +1 -1
  82. package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +11 -11
  83. package/src/lambda/handler-runner/ruby-runner/index.js +1 -1
  84. package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +6 -6
  85. package/src/lambda/handler-runner/worker-thread-runner/index.js +1 -1
  86. package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +4 -4
  87. package/src/lambda/index.js +1 -1
  88. package/src/lambda/routes/index.js +2 -2
  89. package/src/lambda/routes/invocations/InvocationsController.js +11 -11
  90. package/src/lambda/routes/invocations/index.js +1 -1
  91. package/src/lambda/routes/invocations/invocationsRoute.js +15 -15
  92. package/src/lambda/routes/invoke-async/index.js +1 -1
  93. package/src/lambda/routes/invoke-async/invokeAsyncRoute.js +6 -6
  94. package/src/utils/checkDockerDaemon.js +8 -8
  95. package/src/utils/checkGoVersion.js +3 -3
  96. package/src/utils/createApiKey.js +2 -2
  97. package/src/utils/detectExecutable.js +2 -2
  98. package/src/utils/formatToClfTime.js +2 -2
  99. package/src/utils/generateHapiPath.js +8 -8
  100. package/src/utils/getApiKeysValues.js +2 -2
  101. package/src/utils/getHttpApiCorsConfig.js +11 -11
  102. package/src/utils/getRawQueryParams.js +2 -2
  103. package/src/utils/index.js +25 -26
  104. package/src/utils/jsonPath.js +1 -1
  105. package/src/utils/logRoutes.js +13 -13
  106. package/src/utils/parseHeaders.js +1 -1
  107. package/src/utils/parseMultiValueHeaders.js +1 -1
  108. package/src/utils/parseMultiValueQueryStringParameters.js +1 -1
  109. package/src/utils/parseQueryStringParameters.js +1 -1
  110. package/src/utils/parseQueryStringParametersForPayloadV2.js +1 -1
  111. package/src/utils/splitHandlerPathAndName.js +3 -3
  112. package/src/utils/createUniqueId.js +0 -5
@@ -1,26 +1,26 @@
1
- import { Buffer } from 'node:buffer'
2
- import { readFile } from 'node:fs/promises'
3
- import { createRequire } from 'node:module'
4
- import { join, resolve } from 'node:path'
5
- import { exit } from 'node:process'
6
- import h2o2 from '@hapi/h2o2'
7
- import { Server } from '@hapi/hapi'
8
- import { log } from '@serverless/utils/log.js'
9
- import authFunctionNameExtractor from '../authFunctionNameExtractor.js'
10
- import authJWTSettingsExtractor from './authJWTSettingsExtractor.js'
11
- import createAuthScheme from './createAuthScheme.js'
12
- import createJWTAuthScheme from './createJWTAuthScheme.js'
13
- import Endpoint from './Endpoint.js'
1
+ import { Buffer } from "node:buffer"
2
+ import { readFile } from "node:fs/promises"
3
+ import { createRequire } from "node:module"
4
+ import { join, resolve } from "node:path"
5
+ import { exit } from "node:process"
6
+ import h2o2 from "@hapi/h2o2"
7
+ import { Server } from "@hapi/hapi"
8
+ import { log } from "@serverless/utils/log.js"
9
+ import authFunctionNameExtractor from "../authFunctionNameExtractor.js"
10
+ import authJWTSettingsExtractor from "./authJWTSettingsExtractor.js"
11
+ import createAuthScheme from "./createAuthScheme.js"
12
+ import createJWTAuthScheme from "./createJWTAuthScheme.js"
13
+ import Endpoint from "./Endpoint.js"
14
14
  import {
15
15
  LambdaIntegrationEvent,
16
16
  LambdaProxyIntegrationEvent,
17
17
  renderVelocityTemplateObject,
18
18
  VelocityContext,
19
- } from './lambda-events/index.js'
20
- import LambdaProxyIntegrationEventV2 from './lambda-events/LambdaProxyIntegrationEventV2.js'
21
- import parseResources from './parseResources.js'
22
- import payloadSchemaValidator from './payloadSchemaValidator.js'
23
- import logRoutes from '../../utils/logRoutes.js'
19
+ } from "./lambda-events/index.js"
20
+ import LambdaProxyIntegrationEventV2 from "./lambda-events/LambdaProxyIntegrationEventV2.js"
21
+ import parseResources from "./parseResources.js"
22
+ import payloadSchemaValidator from "./payloadSchemaValidator.js"
23
+ import logRoutes from "../../utils/logRoutes.js"
24
24
  import {
25
25
  createApiKey,
26
26
  detectEncoding,
@@ -29,7 +29,7 @@ import {
29
29
  getHttpApiCorsConfig,
30
30
  jsonPath,
31
31
  splitHandlerPathAndName,
32
- } from '../../utils/index.js'
32
+ } from "../../utils/index.js"
33
33
 
34
34
  const { parse, stringify } = JSON
35
35
  const { assign, entries, keys } = Object
@@ -57,8 +57,8 @@ export default class HttpServer {
57
57
 
58
58
  async #loadCerts(httpsProtocol) {
59
59
  const [cert, key] = await Promise.all([
60
- readFile(resolve(httpsProtocol, 'cert.pem'), 'utf8'),
61
- readFile(resolve(httpsProtocol, 'key.pem'), 'utf8'),
60
+ readFile(resolve(httpsProtocol, "cert.pem"), "utf8"),
61
+ readFile(resolve(httpsProtocol, "key.pem"), "utf8"),
62
62
  ])
63
63
 
64
64
  return {
@@ -104,7 +104,7 @@ export default class HttpServer {
104
104
  }
105
105
 
106
106
  // Enable CORS preflight response
107
- this.#server.ext('onPreResponse', (request, h) => {
107
+ this.#server.ext("onPreResponse", (request, h) => {
108
108
  if (request.headers.origin) {
109
109
  const response = request.response.isBoom
110
110
  ? request.response.output
@@ -123,11 +123,11 @@ export default class HttpServer {
123
123
  this,
124
124
  )
125
125
 
126
- if (request.method === 'options') {
126
+ if (request.method === "options") {
127
127
  response.statusCode = 204
128
128
  const allowAllOrigins =
129
129
  httpApiCors.allowedOrigins.length === 1 &&
130
- httpApiCors.allowedOrigins[0] === '*'
130
+ httpApiCors.allowedOrigins[0] === "*"
131
131
  if (
132
132
  !allowAllOrigins &&
133
133
  !httpApiCors.allowedOrigins.includes(request.headers.origin)
@@ -136,47 +136,47 @@ export default class HttpServer {
136
136
  }
137
137
  }
138
138
 
139
- response.headers['access-control-allow-origin'] =
139
+ response.headers["access-control-allow-origin"] =
140
140
  request.headers.origin
141
141
  if (httpApiCors.allowCredentials) {
142
- response.headers['access-control-allow-credentials'] = 'true'
142
+ response.headers["access-control-allow-credentials"] = "true"
143
143
  }
144
144
  if (httpApiCors.maxAge) {
145
- response.headers['access-control-max-age'] = httpApiCors.maxAge
145
+ response.headers["access-control-max-age"] = httpApiCors.maxAge
146
146
  }
147
147
  if (httpApiCors.exposedResponseHeaders) {
148
- response.headers['access-control-expose-headers'] =
149
- httpApiCors.exposedResponseHeaders.join(',')
148
+ response.headers["access-control-expose-headers"] =
149
+ httpApiCors.exposedResponseHeaders.join(",")
150
150
  }
151
151
  if (httpApiCors.allowedMethods) {
152
- response.headers['access-control-allow-methods'] =
153
- httpApiCors.allowedMethods.join(',')
152
+ response.headers["access-control-allow-methods"] =
153
+ httpApiCors.allowedMethods.join(",")
154
154
  }
155
155
  if (httpApiCors.allowedHeaders) {
156
- response.headers['access-control-allow-headers'] =
157
- httpApiCors.allowedHeaders.join(',')
156
+ response.headers["access-control-allow-headers"] =
157
+ httpApiCors.allowedHeaders.join(",")
158
158
  }
159
159
  } else {
160
- response.headers['access-control-allow-origin'] =
160
+ response.headers["access-control-allow-origin"] =
161
161
  request.headers.origin
162
- response.headers['access-control-allow-credentials'] = 'true'
162
+ response.headers["access-control-allow-credentials"] = "true"
163
163
 
164
- if (request.method === 'options') {
164
+ if (request.method === "options") {
165
165
  response.statusCode = 200
166
166
 
167
- response.headers['access-control-expose-headers'] =
168
- request.headers['access-control-expose-headers'] ||
169
- 'content-type, content-length, etag'
170
- response.headers['access-control-max-age'] = 60 * 10
167
+ response.headers["access-control-expose-headers"] =
168
+ request.headers["access-control-expose-headers"] ||
169
+ "content-type, content-length, etag"
170
+ response.headers["access-control-max-age"] = 60 * 10
171
171
 
172
- if (request.headers['access-control-request-headers']) {
173
- response.headers['access-control-allow-headers'] =
174
- request.headers['access-control-request-headers']
172
+ if (request.headers["access-control-request-headers"]) {
173
+ response.headers["access-control-allow-headers"] =
174
+ request.headers["access-control-request-headers"]
175
175
  }
176
176
 
177
- if (request.headers['access-control-request-method']) {
178
- response.headers['access-control-allow-methods'] =
179
- request.headers['access-control-request-method']
177
+ if (request.headers["access-control-request-method"]) {
178
+ response.headers["access-control-allow-methods"] =
179
+ request.headers["access-control-request-method"]
180
180
  }
181
181
  }
182
182
 
@@ -206,7 +206,7 @@ export default class HttpServer {
206
206
  }
207
207
 
208
208
  // TODO move the following block
209
- const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`
209
+ const server = `${httpsProtocol ? "https" : "http"}://${host}:${httpPort}`
210
210
 
211
211
  log.notice(`Server ready: ${server} 🚀`)
212
212
  }
@@ -220,7 +220,7 @@ export default class HttpServer {
220
220
 
221
221
  #logPluginIssue() {
222
222
  log.notice(
223
- 'If you think this is an issue with the plugin please submit it, thanks!\nhttps://github.com/dherault/serverless-offline/issues',
223
+ "If you think this is an issue with the plugin please submit it, thanks!\nhttps://github.com/dherault/serverless-offline/issues",
224
224
  )
225
225
  log.notice()
226
226
  }
@@ -243,7 +243,7 @@ export default class HttpServer {
243
243
  // right now _configureJWTAuthorization only handles AWS HttpAPI Gateway JWT
244
244
  // authorizers that are defined in the serverless file
245
245
  if (
246
- this.#serverless.service.provider.name !== 'aws' ||
246
+ this.#serverless.service.provider.name !== "aws" ||
247
247
  !endpoint.isHttpApi
248
248
  ) {
249
249
  return null
@@ -253,8 +253,8 @@ export default class HttpServer {
253
253
  (endpoint.authorizer.name &&
254
254
  this.#serverless.service.provider?.httpApi?.authorizers?.[
255
255
  endpoint.authorizer.name
256
- ]?.type === 'request') ||
257
- endpoint.authorizer.type === 'request'
256
+ ]?.type === "request") ||
257
+ endpoint.authorizer.type === "request"
258
258
  ) {
259
259
  return null
260
260
  }
@@ -321,17 +321,17 @@ export default class HttpServer {
321
321
  false,
322
322
  identitySource: serverlessAuthorizerOptions?.identitySource,
323
323
  identityValidationExpression:
324
- serverlessAuthorizerOptions?.identityValidationExpression || '(.*)',
324
+ serverlessAuthorizerOptions?.identityValidationExpression || "(.*)",
325
325
  payloadVersion: endpoint.isHttpApi
326
- ? serverlessAuthorizerOptions?.payloadVersion || '2.0'
327
- : '1.0',
326
+ ? serverlessAuthorizerOptions?.payloadVersion || "2.0"
327
+ : "1.0",
328
328
  resultTtlInSeconds:
329
- serverlessAuthorizerOptions?.resultTtlInSeconds || '300',
329
+ serverlessAuthorizerOptions?.resultTtlInSeconds || "300",
330
330
  }
331
331
 
332
332
  if (
333
333
  authorizerOptions.enableSimpleResponses &&
334
- authorizerOptions.payloadVersion === '1.0'
334
+ authorizerOptions.payloadVersion === "1.0"
335
335
  ) {
336
336
  log.error(
337
337
  `Cannot create Authorization function '${authFunctionName}' if payloadVersion is '1.0' and enableSimpleResponses is true`,
@@ -339,7 +339,7 @@ export default class HttpServer {
339
339
  return null
340
340
  }
341
341
 
342
- if (typeof endpoint.authorizer === 'string') {
342
+ if (typeof endpoint.authorizer === "string") {
343
343
  authorizerOptions.name = authFunctionName
344
344
  } else {
345
345
  assign(authorizerOptions, endpoint.authorizer)
@@ -348,11 +348,11 @@ export default class HttpServer {
348
348
  if (
349
349
  !authorizerOptions.identitySource &&
350
350
  !(
351
- authorizerOptions.type === 'request' &&
351
+ authorizerOptions.type === "request" &&
352
352
  authorizerOptions.resultTtlInSeconds === 0
353
353
  )
354
354
  ) {
355
- authorizerOptions.identitySource = 'method.request.header.Authorization'
355
+ authorizerOptions.identitySource = "method.request.header.Authorization"
356
356
  }
357
357
 
358
358
  // Create a unique scheme per endpoint
@@ -391,7 +391,7 @@ export default class HttpServer {
391
391
  customizations &&
392
392
  customizations.offline?.customAuthenticationProvider
393
393
  ) {
394
- const root = resolve(this.#serverless.serviceDir, 'require-resolver')
394
+ const root = resolve(this.#serverless.serviceDir, "require-resolver")
395
395
  const customRequire = createRequire(root)
396
396
 
397
397
  const provider = customRequire(
@@ -457,13 +457,13 @@ export default class HttpServer {
457
457
  const errorResponse = () =>
458
458
  h
459
459
  .response({
460
- message: 'Forbidden',
460
+ message: "Forbidden",
461
461
  })
462
462
  .code(403)
463
- .header('x-amzn-ErrorType', 'ForbiddenException')
464
- .type('application/json')
463
+ .header("x-amzn-ErrorType", "ForbiddenException")
464
+ .type("application/json")
465
465
 
466
- const apiKey = request.headers['x-api-key']
466
+ const apiKey = request.headers["x-api-key"]
467
467
 
468
468
  if (apiKey) {
469
469
  if (!this.#apiKeysValues.has(apiKey)) {
@@ -495,26 +495,26 @@ export default class HttpServer {
495
495
  }
496
496
 
497
497
  const response = h.response()
498
- const contentType = request.mime || 'application/json' // default content type
498
+ const contentType = request.mime || "application/json" // default content type
499
499
 
500
500
  const { integration, requestTemplates } = endpoint
501
501
 
502
502
  // default request template to '' if we don't have a definition pushed in from serverless or endpoint
503
503
  const requestTemplate =
504
- requestTemplates !== undefined && integration === 'AWS'
504
+ requestTemplates !== undefined && integration === "AWS"
505
505
  ? requestTemplates[contentType]
506
- : ''
506
+ : ""
507
507
 
508
508
  const schemas =
509
509
  endpoint?.request?.schemas === undefined
510
- ? ''
510
+ ? ""
511
511
  : endpoint.request.schemas[contentType]
512
512
 
513
513
  // https://hapijs.com/api#route-configuration doesn't seem to support selectively parsing
514
514
  // so we have to do it ourselves
515
515
  const contentTypesThatRequirePayloadParsing = [
516
- 'application/json',
517
- 'application/vnd.api+json',
516
+ "application/json",
517
+ "application/vnd.api+json",
518
518
  ]
519
519
 
520
520
  if (
@@ -524,22 +524,22 @@ export default class HttpServer {
524
524
  ) {
525
525
  try {
526
526
  if (!request.payload || request.payload.length === 0) {
527
- request.payload = '{}'
527
+ request.payload = "{}"
528
528
  }
529
529
 
530
530
  request.payload = parse(request.payload)
531
531
  } catch (err) {
532
- log.debug('error in converting request.payload to JSON:', err)
532
+ log.debug("error in converting request.payload to JSON:", err)
533
533
  }
534
534
  }
535
535
 
536
- log.debug('contentType:', contentType)
537
- log.debug('requestTemplate:', requestTemplate)
538
- log.debug('payload:', request.payload)
536
+ log.debug("contentType:", contentType)
537
+ log.debug("requestTemplate:", requestTemplate)
538
+ log.debug("payload:", request.payload)
539
539
 
540
540
  /* REQUEST PAYLOAD SCHEMA VALIDATION */
541
541
  if (schemas) {
542
- log.debug('schemas:', schemas)
542
+ log.debug("schemas:", schemas)
543
543
 
544
544
  try {
545
545
  payloadSchemaValidator(schemas, request.payload)
@@ -552,10 +552,10 @@ export default class HttpServer {
552
552
 
553
553
  let event = {}
554
554
 
555
- if (integration === 'AWS') {
555
+ if (integration === "AWS") {
556
556
  if (requestTemplate) {
557
557
  try {
558
- log.debug('_____ REQUEST TEMPLATE PROCESSING _____')
558
+ log.debug("_____ REQUEST TEMPLATE PROCESSING _____")
559
559
 
560
560
  event = new LambdaIntegrationEvent(
561
561
  request,
@@ -570,12 +570,12 @@ export default class HttpServer {
570
570
  err,
571
571
  )
572
572
  }
573
- } else if (typeof request.payload === 'object') {
573
+ } else if (typeof request.payload === "object") {
574
574
  event = request.payload || {}
575
575
  }
576
- } else if (integration === 'AWS_PROXY') {
576
+ } else if (integration === "AWS_PROXY") {
577
577
  const lambdaProxyIntegrationEvent =
578
- endpoint.isHttpApi && endpoint.payload === '2.0'
578
+ endpoint.isHttpApi && endpoint.payload === "2.0"
579
579
  ? new LambdaProxyIntegrationEventV2(
580
580
  request,
581
581
  stage,
@@ -593,7 +593,7 @@ export default class HttpServer {
593
593
  event = lambdaProxyIntegrationEvent.create()
594
594
  }
595
595
 
596
- log.debug('event:', event)
596
+ log.debug("event:", event)
597
597
 
598
598
  const lambdaFunction = this.#lambda.get(functionKey)
599
599
 
@@ -611,15 +611,15 @@ export default class HttpServer {
611
611
  // const processResponse = (err, data) => {
612
612
  // Everything in this block happens once the lambda function has resolved
613
613
 
614
- log.debug('_____ HANDLER RESOLVED _____')
614
+ log.debug("_____ HANDLER RESOLVED _____")
615
615
 
616
- let responseName = 'default'
616
+ let responseName = "default"
617
617
  const { contentHandling, responseContentType } = endpoint
618
618
 
619
619
  /* RESPONSE SELECTION (among endpoint's possible responses) */
620
620
 
621
621
  // Failure handling
622
- let errorStatusCode = '502'
622
+ let errorStatusCode = "502"
623
623
 
624
624
  if (err) {
625
625
  const errorMessage = (err.message || err).toString()
@@ -629,7 +629,7 @@ export default class HttpServer {
629
629
  if (found && found.length > 1) {
630
630
  ;[, errorStatusCode] = found
631
631
  } else {
632
- errorStatusCode = '502'
632
+ errorStatusCode = "502"
633
633
  }
634
634
 
635
635
  // Mocks Lambda errors
@@ -643,7 +643,7 @@ export default class HttpServer {
643
643
 
644
644
  for (const [key, value] of entries(endpoint.responses)) {
645
645
  if (
646
- key !== 'default' &&
646
+ key !== "default" &&
647
647
  `^${value.selectionPattern || key}$`.test(errorMessage)
648
648
  ) {
649
649
  responseName = key
@@ -654,14 +654,13 @@ export default class HttpServer {
654
654
 
655
655
  log.debug(`Using response '${responseName}'`)
656
656
 
657
- const chosenResponse = endpoint.responses[responseName]
658
-
657
+ const chosenResponse = endpoint.responses?.[responseName] ?? {}
659
658
  /* RESPONSE PARAMETERS PROCCESSING */
660
659
 
661
660
  const { responseParameters } = chosenResponse
662
661
 
663
662
  if (responseParameters) {
664
- log.debug('_____ RESPONSE PARAMETERS PROCCESSING _____')
663
+ log.debug("_____ RESPONSE PARAMETERS PROCCESSING _____")
665
664
  log.debug(
666
665
  `Found ${
667
666
  keys(responseParameters).length
@@ -670,27 +669,27 @@ export default class HttpServer {
670
669
 
671
670
  // responseParameters use the following shape: "key": "value"
672
671
  entries(responseParameters).forEach(([key, value]) => {
673
- const keyArray = key.split('.') // eg: "method.response.header.location"
674
- const valueArray = value.split('.') // eg: "integration.response.body.redirect.url"
672
+ const keyArray = key.split(".") // eg: "method.response.header.location"
673
+ const valueArray = value.split(".") // eg: "integration.response.body.redirect.url"
675
674
 
676
675
  log.debug(`Processing responseParameter "${key}": "${value}"`)
677
676
 
678
677
  // For now the plugin only supports modifying headers
679
- if (key.startsWith('method.response.header') && keyArray[3]) {
680
- const headerName = keyArray.slice(3).join('.')
678
+ if (key.startsWith("method.response.header") && keyArray[3]) {
679
+ const headerName = keyArray.slice(3).join(".")
681
680
  let headerValue
682
681
 
683
- log.debug('Found header in left-hand:', headerName)
682
+ log.debug("Found header in left-hand:", headerName)
684
683
 
685
- if (value.startsWith('integration.response')) {
686
- if (valueArray[2] === 'body') {
687
- log.debug('Found body in right-hand')
684
+ if (value.startsWith("integration.response")) {
685
+ if (valueArray[2] === "body") {
686
+ log.debug("Found body in right-hand")
688
687
 
689
688
  headerValue = valueArray[3]
690
- ? jsonPath(result, valueArray.slice(3).join('.'))
689
+ ? jsonPath(result, valueArray.slice(3).join("."))
691
690
  : result
692
691
 
693
- headerValue = headerValue == null ? '' : String(headerValue)
692
+ headerValue = headerValue == null ? "" : String(headerValue)
694
693
  } else {
695
694
  log.notice()
696
695
 
@@ -706,7 +705,7 @@ export default class HttpServer {
706
705
  headerValue = /^'.*'$/.test(value) ? value.slice(1, -1) : value // See #34
707
706
  }
708
707
  // Applies the header;
709
- if (headerValue === '') {
708
+ if (headerValue === "") {
710
709
  log.warning(
711
710
  `Empty value for responseParameter "${key}": "${value}", it won't be set`,
712
711
  )
@@ -733,13 +732,13 @@ export default class HttpServer {
733
732
 
734
733
  let statusCode = 200
735
734
 
736
- if (integration === 'AWS') {
735
+ if (integration === "AWS") {
737
736
  const endpointResponseHeaders =
738
737
  (endpoint.response && endpoint.response.headers) || {}
739
738
 
740
739
  entries(endpointResponseHeaders)
741
740
  .filter(
742
- ([, value]) => typeof value === 'string' && /^'.*?'$/.test(value),
741
+ ([, value]) => typeof value === "string" && /^'.*?'$/.test(value),
743
742
  )
744
743
  .forEach(([key, value]) => response.header(key, value.slice(1, -1)))
745
744
 
@@ -749,14 +748,14 @@ export default class HttpServer {
749
748
  const { responseTemplates } = chosenResponse
750
749
 
751
750
  if (
752
- typeof responseTemplates === 'object' &&
751
+ typeof responseTemplates === "object" &&
753
752
  keys(responseTemplates).length > 0
754
753
  ) {
755
754
  // BAD IMPLEMENTATION: first key in responseTemplates
756
755
  const responseTemplate = responseTemplates[responseContentType]
757
756
 
758
- if (responseTemplate && responseTemplate !== '\n') {
759
- log.debug('_____ RESPONSE TEMPLATE PROCCESSING _____')
757
+ if (responseTemplate && responseTemplate !== "\n") {
758
+ log.debug("_____ RESPONSE TEMPLATE PROCCESSING _____")
760
759
  log.debug(`Using responseTemplate '${responseContentType}'`)
761
760
 
762
761
  try {
@@ -794,34 +793,34 @@ export default class HttpServer {
794
793
  log.warning(`No statusCode found for response "${responseName}".`)
795
794
  }
796
795
 
797
- response.header('Content-Type', responseContentType, {
796
+ response.header("Content-Type", responseContentType, {
798
797
  override: false, // Maybe a responseParameter set it already. See #34
799
798
  })
800
799
 
801
800
  response.statusCode = statusCode
802
801
 
803
- if (contentHandling === 'CONVERT_TO_BINARY') {
804
- response.encoding = 'binary'
805
- response.source = Buffer.from(result, 'base64')
806
- response.variety = 'buffer'
807
- } else if (typeof result === 'string') {
802
+ if (contentHandling === "CONVERT_TO_BINARY") {
803
+ response.encoding = "binary"
804
+ response.source = Buffer.from(result, "base64")
805
+ response.variety = "buffer"
806
+ } else if (typeof result === "string") {
808
807
  response.source = stringify(result)
809
808
  } else {
810
809
  response.source = result
811
810
  }
812
- } else if (integration === 'AWS_PROXY') {
811
+ } else if (integration === "AWS_PROXY") {
813
812
  /* LAMBDA PROXY INTEGRATION HAPIJS RESPONSE CONFIGURATION */
814
813
 
815
814
  if (
816
815
  endpoint.isHttpApi &&
817
- endpoint.payload === '2.0' &&
818
- (typeof result === 'string' || !result.statusCode)
816
+ endpoint.payload === "2.0" &&
817
+ (typeof result === "string" || !result.statusCode)
819
818
  ) {
820
- const body = typeof result === 'string' ? result : stringify(result)
819
+ const body = typeof result === "string" ? result : stringify(result)
821
820
  result = {
822
821
  body,
823
822
  headers: {
824
- 'Content-Type': 'application/json',
823
+ "Content-Type": "application/json",
825
824
  },
826
825
  isBase64Encoded: false,
827
826
  statusCode: 200,
@@ -855,20 +854,20 @@ export default class HttpServer {
855
854
  )
856
855
  }
857
856
 
858
- log.debug('headers', headers)
857
+ log.debug("headers", headers)
859
858
 
860
859
  const parseCookies = (headerValue) => {
861
- const cookieName = headerValue.slice(0, headerValue.indexOf('='))
862
- const cookieValue = headerValue.slice(headerValue.indexOf('=') + 1)
860
+ const cookieName = headerValue.slice(0, headerValue.indexOf("="))
861
+ const cookieValue = headerValue.slice(headerValue.indexOf("=") + 1)
863
862
 
864
863
  h.state(cookieName, cookieValue, {
865
- encoding: 'none',
864
+ encoding: "none",
866
865
  strictHeader: false,
867
866
  })
868
867
  }
869
868
 
870
869
  entries(headers).forEach(([headerKey, headerValue]) => {
871
- if (headerKey.toLowerCase() === 'set-cookie') {
870
+ if (headerKey.toLowerCase() === "set-cookie") {
872
871
  headerValue.forEach(parseCookies)
873
872
  } else {
874
873
  headerValue.forEach((value) => {
@@ -883,30 +882,30 @@ export default class HttpServer {
883
882
 
884
883
  if (
885
884
  endpoint.isHttpApi &&
886
- endpoint.payload === '2.0' &&
885
+ endpoint.payload === "2.0" &&
887
886
  result.cookies
888
887
  ) {
889
888
  result.cookies.forEach(parseCookies)
890
889
  }
891
890
 
892
- response.header('Content-Type', 'application/json', {
891
+ response.header("Content-Type", "application/json", {
893
892
  duplicate: false,
894
893
  override: false,
895
894
  })
896
895
 
897
- if (typeof result === 'string') {
896
+ if (typeof result === "string") {
898
897
  response.source = stringify(result)
899
898
  } else if (result && result.body !== undefined) {
900
899
  if (result.isBase64Encoded) {
901
- response.encoding = 'binary'
902
- response.source = Buffer.from(result.body, 'base64')
903
- response.variety = 'buffer'
900
+ response.encoding = "binary"
901
+ response.source = Buffer.from(result.body, "base64")
902
+ response.variety = "buffer"
904
903
  } else {
905
- if (result && result.body && typeof result.body !== 'string') {
904
+ if (result && result.body && typeof result.body !== "string") {
906
905
  // FIXME TODO we should probably just write to console instead of returning a payload
907
906
  return this.#reply502(
908
907
  response,
909
- 'According to the API Gateway specs, the body content must be stringified. Check your Lambda response and make sure you are invoking JSON.stringify(YOUR_CONTENT) on your body object',
908
+ "According to the API Gateway specs, the body content must be stringified. Check your Lambda response and make sure you are invoking JSON.stringify(YOUR_CONTENT) on your body object",
910
909
  {},
911
910
  )
912
911
  }
@@ -951,12 +950,12 @@ export default class HttpServer {
951
950
  let hapiPath
952
951
 
953
952
  if (httpEvent.isHttpApi) {
954
- if (httpEvent.routeKey === '$default') {
955
- method = 'ANY'
953
+ if (httpEvent.routeKey === "$default") {
954
+ method = "ANY"
956
955
  path = httpEvent.routeKey
957
- hapiPath = '/{default*}'
956
+ hapiPath = "/{default*}"
958
957
  } else {
959
- ;[method, path] = httpEvent.routeKey.split(' ')
958
+ ;[method, path] = httpEvent.routeKey.split(" ")
960
959
  hapiPath = generateHapiPath(
961
960
  path,
962
961
  {
@@ -980,7 +979,7 @@ export default class HttpServer {
980
979
  ).generate()
981
980
 
982
981
  const stage = endpoint.isHttpApi
983
- ? '$default'
982
+ ? "$default"
984
983
  : this.#options.stage || this.#serverless.service.provider.stage
985
984
 
986
985
  const protectedRoute = httpEvent.private
@@ -988,7 +987,7 @@ export default class HttpServer {
988
987
  : undefined
989
988
 
990
989
  const { host, httpPort, httpsProtocol } = this.#options
991
- const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`
990
+ const server = `${httpsProtocol ? "https" : "http"}://${host}:${httpPort}`
992
991
 
993
992
  this.#terminalInfo.push({
994
993
  invokePath: `/2015-03-31/functions/${functionKey}/invocations`,
@@ -1032,15 +1031,15 @@ export default class HttpServer {
1032
1031
  }
1033
1032
  }
1034
1033
 
1035
- const hapiMethod = method === 'ANY' ? '*' : method
1034
+ const hapiMethod = method === "ANY" ? "*" : method
1036
1035
 
1037
1036
  const state = this.#options.disableCookieValidation
1038
1037
  ? {
1039
- failAction: 'ignore',
1038
+ failAction: "ignore",
1040
1039
  parse: false,
1041
1040
  }
1042
1041
  : {
1043
- failAction: 'error',
1042
+ failAction: "error",
1044
1043
  parse: true,
1045
1044
  }
1046
1045
 
@@ -1058,15 +1057,15 @@ export default class HttpServer {
1058
1057
 
1059
1058
  // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
1060
1059
  // for more details, check https://github.com/dherault/serverless-offline/issues/204
1061
- if (hapiMethod === 'HEAD') {
1060
+ if (hapiMethod === "HEAD") {
1062
1061
  log.notice(
1063
- 'HEAD method event detected. Skipping HAPI server route mapping',
1062
+ "HEAD method event detected. Skipping HAPI server route mapping",
1064
1063
  )
1065
1064
 
1066
1065
  return
1067
1066
  }
1068
1067
 
1069
- if (hapiMethod !== 'HEAD' && hapiMethod !== 'GET') {
1068
+ if (hapiMethod !== "HEAD" && hapiMethod !== "GET") {
1070
1069
  // maxBytes: Increase request size from 1MB default limit to 10MB.
1071
1070
  // Cf AWS API GW payload limits.
1072
1071
  hapiOptions.payload = {
@@ -1080,7 +1079,7 @@ export default class HttpServer {
1080
1079
  additionalRequestContext.operationName = httpEvent.operationId
1081
1080
  }
1082
1081
 
1083
- hapiOptions.tags = ['api']
1082
+ hapiOptions.tags = ["api"]
1084
1083
 
1085
1084
  const hapiHandler = this.#createHapiHandler({
1086
1085
  additionalRequestContext,
@@ -1106,14 +1105,14 @@ export default class HttpServer {
1106
1105
 
1107
1106
  log.error(error)
1108
1107
 
1109
- response.header('Content-Type', 'application/json')
1108
+ response.header("Content-Type", "application/json")
1110
1109
 
1111
1110
  response.statusCode = statusCode
1112
1111
  response.source = {
1113
1112
  errorMessage: message,
1114
1113
  errorType: error.constructor.name,
1115
1114
  offlineInfo:
1116
- 'If you believe this is an issue with serverless-offline please submit it, thanks. https://github.com/dherault/serverless-offline/issues',
1115
+ "If you believe this is an issue with serverless-offline please submit it, thanks. https://github.com/dherault/serverless-offline/issues",
1117
1116
  stackTrace: this.#getArrayStackTrace(error.stack),
1118
1117
  }
1119
1118
 
@@ -1146,7 +1145,7 @@ export default class HttpServer {
1146
1145
  log.notice()
1147
1146
 
1148
1147
  log.notice()
1149
- log.notice('Routes defined in resources:')
1148
+ log.notice("Routes defined in resources:")
1150
1149
 
1151
1150
  entries(resourceRoutes).forEach(([methodId, resourceRoutesObj]) => {
1152
1151
  const { isProxy, method, pathResource, proxyUri } = resourceRoutesObj
@@ -1178,15 +1177,15 @@ export default class HttpServer {
1178
1177
  return
1179
1178
  }
1180
1179
 
1181
- const hapiMethod = method === 'ANY' ? '*' : method
1180
+ const hapiMethod = method === "ANY" ? "*" : method
1182
1181
 
1183
1182
  const state = this.#options.disableCookieValidation
1184
1183
  ? {
1185
- failAction: 'ignore',
1184
+ failAction: "ignore",
1186
1185
  parse: false,
1187
1186
  }
1188
1187
  : {
1189
- failAction: 'error',
1188
+ failAction: "error",
1190
1189
  parse: true,
1191
1190
  }
1192
1191
 
@@ -1197,15 +1196,15 @@ export default class HttpServer {
1197
1196
 
1198
1197
  // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
1199
1198
  // for more details, check https://github.com/dherault/serverless-offline/issues/204
1200
- if (hapiMethod === 'HEAD') {
1199
+ if (hapiMethod === "HEAD") {
1201
1200
  log.notice(
1202
- 'HEAD method event detected. Skipping HAPI server route mapping',
1201
+ "HEAD method event detected. Skipping HAPI server route mapping",
1203
1202
  )
1204
1203
 
1205
1204
  return
1206
1205
  }
1207
1206
 
1208
- if (hapiMethod !== 'GET' && hapiMethod !== 'HEAD') {
1207
+ if (hapiMethod !== "GET" && hapiMethod !== "HEAD") {
1209
1208
  hapiOptions.payload = {
1210
1209
  parse: false,
1211
1210
  }
@@ -1248,14 +1247,14 @@ export default class HttpServer {
1248
1247
 
1249
1248
  create404Route() {
1250
1249
  // If a {proxy+} or $default route exists, don't conflict with it
1251
- if (this.#server.match('*', '/{p*}')) {
1250
+ if (this.#server.match("*", "/{p*}")) {
1252
1251
  return
1253
1252
  }
1254
1253
 
1255
1254
  const existingRoutes = this.#server
1256
1255
  .table()
1257
1256
  // Exclude this (404) route
1258
- .filter((route) => route.path !== '/{p*}')
1257
+ .filter((route) => route.path !== "/{p*}")
1259
1258
  // Sort by path
1260
1259
  .sort((a, b) => (a.path <= b.path ? -1 : 1))
1261
1260
  // Human-friendly result
@@ -1265,7 +1264,7 @@ export default class HttpServer {
1265
1264
  handler(request, h) {
1266
1265
  const response = h.response({
1267
1266
  currentRoute: `${request.method} - ${request.path}`,
1268
- error: 'Serverless-offline: route not found.',
1267
+ error: "Serverless-offline: route not found.",
1269
1268
  existingRoutes,
1270
1269
  statusCode: 404,
1271
1270
  })
@@ -1273,11 +1272,11 @@ export default class HttpServer {
1273
1272
 
1274
1273
  return response
1275
1274
  },
1276
- method: '*',
1275
+ method: "*",
1277
1276
  options: {
1278
1277
  cors: this.#options.corsConfig,
1279
1278
  },
1280
- path: '/{p*}',
1279
+ path: "/{p*}",
1281
1280
  }
1282
1281
 
1283
1282
  this.#server.route(route)
@@ -1286,7 +1285,7 @@ export default class HttpServer {
1286
1285
  #getArrayStackTrace(stack) {
1287
1286
  if (!stack) return null
1288
1287
 
1289
- const splittedStack = stack.split('\n')
1288
+ const splittedStack = stack.split("\n")
1290
1289
 
1291
1290
  return splittedStack
1292
1291
  .slice(