serverless-offline 9.1.1 → 9.1.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 CHANGED
@@ -152,7 +152,6 @@ custom:
152
152
  serverless-offline:
153
153
  httpsProtocol: 'dev-certs'
154
154
  httpPort: 4000
155
- stageVariables:
156
155
  foo: 'bar'
157
156
  ```
158
157
 
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.1.1",
4
+ "version": "9.1.4",
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,8 @@
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.1184.0",
197
+ "@serverless/utils": "^6.7.0",
198
+ "aws-sdk": "^2.1187.0",
198
199
  "boxen": "^7.0.0",
199
200
  "chalk": "^5.0.1",
200
201
  "execa": "^6.1.0",
@@ -204,9 +205,9 @@
204
205
  "jsonpath-plus": "^7.0.0",
205
206
  "jsonschema": "^1.4.1",
206
207
  "jsonwebtoken": "^8.5.1",
207
- "jszip": "^3.10.0",
208
+ "jszip": "^3.10.1",
208
209
  "luxon": "^3.0.1",
209
- "node-fetch": "^3.2.9",
210
+ "node-fetch": "^3.2.10",
210
211
  "node-schedule": "^2.1.0",
211
212
  "object.hasown": "^1.1.1",
212
213
  "p-memoize": "^7.1.0",
@@ -216,7 +217,7 @@
216
217
  },
217
218
  "devDependencies": {
218
219
  "archiver": "^5.3.1",
219
- "eslint": "^8.20.0",
220
+ "eslint": "^8.21.0",
220
221
  "eslint-config-airbnb-base": "^15.0.0",
221
222
  "eslint-config-prettier": "^8.5.0",
222
223
  "eslint-plugin-import": "^2.25.4",
@@ -230,7 +231,6 @@
230
231
  "standard-version": "^9.5.0"
231
232
  },
232
233
  "peerDependencies": {
233
- "@serverless/utils": "^6.7.0",
234
234
  "serverless": "^3.2.0"
235
235
  }
236
236
  }
@@ -168,7 +168,7 @@ export default class ServerlessOffline {
168
168
 
169
169
  this.#http = new Http(this.#serverless, this.#options, this.#lambda)
170
170
 
171
- await this.#http.registerPlugins()
171
+ await this.#http.createServer()
172
172
 
173
173
  this.#http.create(events)
174
174
 
@@ -236,12 +236,8 @@ export default class ServerlessOffline {
236
236
  .replace(/\s/g, '')
237
237
  .split(',')
238
238
 
239
- if (this.#options.corsDisallowCredentials) {
240
- this.#options.corsAllowCredentials = false
241
- }
242
-
243
239
  this.#options.corsConfig = {
244
- credentials: this.#options.corsAllowCredentials,
240
+ credentials: !this.#options.corsDisallowCredentials,
245
241
  exposedHeaders: this.#options.corsExposedHeaders,
246
242
  headers: this.#options.corsAllowHeaders,
247
243
  origin: this.#options.corsAllowOrigin,
@@ -2,9 +2,9 @@ import { createApiKey } from '../utils/index.js'
2
2
 
3
3
  export default {
4
4
  apiKey: createApiKey(),
5
- corsAllowCredentials: true, // TODO no CLI option
6
5
  corsAllowHeaders: 'accept,content-type,x-api-key,authorization',
7
6
  corsAllowOrigin: '*',
7
+ corsDisallowCredentials: true,
8
8
  corsExposedHeaders: 'WWW-Authenticate,Server-Authorization',
9
9
  disableCookieValidation: false,
10
10
  disableScheduledEvents: false,
@@ -17,6 +17,10 @@ export default class Http {
17
17
  return this.#httpServer.stop(timeout)
18
18
  }
19
19
 
20
+ async createServer() {
21
+ await this.#httpServer.createServer()
22
+ }
23
+
20
24
  #createEvent(functionKey, rawHttpEventDefinition, handler) {
21
25
  const httpEvent = new HttpEventDefinition(rawHttpEventDefinition)
22
26
 
@@ -39,10 +43,6 @@ export default class Http {
39
43
  this.#httpServer.create404Route()
40
44
  }
41
45
 
42
- registerPlugins() {
43
- return this.#httpServer.registerPlugins()
44
- }
45
-
46
46
  // TEMP FIXME quick fix to expose gateway server for testing, look for better solution
47
47
  getServer() {
48
48
  return this.#httpServer.getServer()
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from 'node:buffer'
2
- import { readFileSync } from 'node:fs'
2
+ import { readFile } from 'node:fs/promises'
3
3
  import { createRequire } from 'node:module'
4
4
  import { join, resolve } from 'node:path'
5
5
  import { exit } from 'node:process'
@@ -47,7 +47,9 @@ export default class HttpServer {
47
47
  this.#lambda = lambda
48
48
  this.#options = options
49
49
  this.#serverless = serverless
50
+ }
50
51
 
52
+ async createServer() {
51
53
  const {
52
54
  enforceSecureCookies,
53
55
  host,
@@ -80,14 +82,20 @@ export default class HttpServer {
80
82
  // HTTPS support
81
83
  if (typeof httpsProtocol === 'string' && httpsProtocol.length > 0) {
82
84
  serverOptions.tls = {
83
- cert: readFileSync(resolve(httpsProtocol, 'cert.pem'), 'ascii'),
84
- key: readFileSync(resolve(httpsProtocol, 'key.pem'), 'ascii'),
85
+ cert: readFile(resolve(httpsProtocol, 'cert.pem'), 'ascii'),
86
+ key: readFile(resolve(httpsProtocol, 'key.pem'), 'ascii'),
85
87
  }
86
88
  }
87
89
 
88
90
  // Hapijs server creation
89
91
  this.#server = new Server(serverOptions)
90
92
 
93
+ try {
94
+ await this.#server.register([h2o2])
95
+ } catch (err) {
96
+ log.error(err)
97
+ }
98
+
91
99
  // Enable CORS preflight response
92
100
  this.#server.ext('onPreResponse', (request, h) => {
93
101
  if (request.headers.origin) {
@@ -205,14 +213,6 @@ export default class HttpServer {
205
213
  })
206
214
  }
207
215
 
208
- async registerPlugins() {
209
- try {
210
- await this.#server.register([h2o2])
211
- } catch (err) {
212
- log.error(err)
213
- }
214
- }
215
-
216
216
  #logPluginIssue() {
217
217
  log.notice(
218
218
  'If you think this is an issue with the plugin please submit it, thanks!\nhttps://github.com/dherault/serverless-offline/issues',
@@ -260,7 +260,7 @@ export default class HttpServer {
260
260
  log.debug(`Creating Authorization scheme for ${authKey}`)
261
261
 
262
262
  // Create the Auth Scheme for the endpoint
263
- const scheme = createJWTAuthScheme(jwtSettings, this)
263
+ const scheme = createJWTAuthScheme(jwtSettings)
264
264
 
265
265
  // Set the auth scheme and strategy on the server
266
266
  this.#server.auth.scheme(authSchemeName, scheme)
@@ -338,6 +338,7 @@ export default class HttpServer {
338
338
  * /tests/integration/custom-authentication
339
339
  */
340
340
  const customizations = this.#serverless.service.custom
341
+
341
342
  if (
342
343
  customizations &&
343
344
  customizations.offline?.customAuthenticationProvider
@@ -350,11 +351,13 @@ export default class HttpServer {
350
351
  )
351
352
 
352
353
  const strategy = provider(endpoint, functionKey, method, path)
354
+
353
355
  this.#server.auth.scheme(
354
356
  strategy.scheme,
355
357
  strategy.getAuthenticateFunction,
356
358
  )
357
359
  this.#server.auth.strategy(strategy.name, strategy.scheme)
360
+
358
361
  return strategy.name
359
362
  }
360
363
 
@@ -363,164 +366,44 @@ export default class HttpServer {
363
366
  ? null
364
367
  : this.#configureJWTAuthorization(endpoint, functionKey, method, path) ||
365
368
  this.#configureAuthorization(endpoint, functionKey, method, path)
369
+
366
370
  return authStrategyName
367
371
  }
368
372
 
369
- createRoutes(functionKey, httpEvent, handler) {
370
- const [handlerPath] = splitHandlerPathAndName(handler)
371
-
372
- let method
373
- let path
374
- let hapiPath
375
-
376
- if (httpEvent.isHttpApi) {
377
- if (httpEvent.routeKey === '$default') {
378
- method = 'ANY'
379
- path = httpEvent.routeKey
380
- hapiPath = '/{default*}'
381
- } else {
382
- ;[method, path] = httpEvent.routeKey.split(' ')
383
- hapiPath = generateHapiPath(
384
- path,
385
- {
386
- ...this.#options,
387
- noPrependStageInUrl: true, // Serverless always uses the $default stage
388
- },
389
- this.#serverless,
390
- )
391
- }
392
- } else {
393
- method = httpEvent.method.toUpperCase()
394
- ;({ path } = httpEvent)
395
- hapiPath = generateHapiPath(path, this.#options, this.#serverless)
396
- }
397
-
398
- const endpoint = new Endpoint(
399
- join(this.#serverless.config.servicePath, handlerPath),
400
- httpEvent,
401
- ).generate()
402
-
403
- const stage = endpoint.isHttpApi
404
- ? '$default'
405
- : this.#options.stage || this.#serverless.service.provider.stage
406
- const protectedRoutes = []
407
-
408
- if (httpEvent.private) {
409
- protectedRoutes.push(`${method}#${hapiPath}`)
410
- }
411
-
412
- const { host, httpPort, httpsProtocol } = this.#options
413
- const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`
414
-
415
- this.#terminalInfo.push({
416
- invokePath: `/2015-03-31/functions/${functionKey}/invocations`,
417
- method,
418
- path: hapiPath,
419
- server,
420
- stage:
421
- endpoint.isHttpApi || this.#options.noPrependStageInUrl ? null : stage,
422
- })
423
-
424
- const authStrategyName = this.#setAuthorizationStrategy(
373
+ #createHapiHandler(params) {
374
+ const {
375
+ additionalRequestContext,
425
376
  endpoint,
426
377
  functionKey,
378
+ hapiMethod,
379
+ hapiPath,
427
380
  method,
428
- path,
429
- )
430
-
431
- let cors = null
432
- if (endpoint.cors) {
433
- cors = {
434
- credentials:
435
- endpoint.cors.credentials || this.#options.corsConfig.credentials,
436
- exposedHeaders: this.#options.corsConfig.exposedHeaders,
437
- headers: endpoint.cors.headers || this.#options.corsConfig.headers,
438
- origin: endpoint.cors.origins || this.#options.corsConfig.origin,
439
- }
440
- } else if (
441
- this.#serverless.service.provider.httpApi &&
442
- this.#serverless.service.provider.httpApi.cors
443
- ) {
444
- const httpApiCors = getHttpApiCorsConfig(
445
- this.#serverless.service.provider.httpApi.cors,
446
- this,
447
- )
448
- cors = {
449
- credentials: httpApiCors.allowCredentials,
450
- exposedHeaders: httpApiCors.exposedResponseHeaders || [],
451
- headers: httpApiCors.allowedHeaders || [],
452
- maxAge: httpApiCors.maxAge,
453
- origin: httpApiCors.allowedOrigins || [],
454
- }
455
- }
456
-
457
- const hapiMethod = method === 'ANY' ? '*' : method
458
-
459
- const state = this.#options.disableCookieValidation
460
- ? {
461
- failAction: 'ignore',
462
- parse: false,
463
- }
464
- : {
465
- failAction: 'error',
466
- parse: true,
467
- }
468
-
469
- const hapiOptions = {
470
- auth: authStrategyName,
471
- cors,
472
- state,
473
- timeout: { socket: false },
474
- }
381
+ protectedRoute,
382
+ stage,
383
+ } = params
475
384
 
476
- // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
477
- // for more details, check https://github.com/dherault/serverless-offline/issues/204
478
- if (hapiMethod === 'HEAD') {
479
- log.notice(
480
- 'HEAD method event detected. Skipping HAPI server route mapping',
481
- )
482
-
483
- return
484
- }
485
-
486
- if (hapiMethod !== 'HEAD' && hapiMethod !== 'GET') {
487
- // maxBytes: Increase request size from 1MB default limit to 10MB.
488
- // Cf AWS API GW payload limits.
489
- hapiOptions.payload = {
490
- maxBytes: 1024 * 1024 * 10,
491
- parse: false,
492
- }
493
- }
494
-
495
- const additionalRequestContext = {}
496
- if (httpEvent.operationId) {
497
- additionalRequestContext.operationName = httpEvent.operationId
498
- }
499
-
500
- hapiOptions.tags = ['api']
501
-
502
- const hapiHandler = async (request, h) => {
385
+ return async (request, h) => {
503
386
  const requestPath =
504
387
  endpoint.isHttpApi || this.#options.noPrependStageInUrl
505
388
  ? request.path
506
389
  : request.path.substr(`/${stage}`.length)
507
390
 
508
- // Payload processing
391
+ // payload processing
509
392
  const encoding = detectEncoding(request)
510
393
 
511
394
  request.payload = request.payload && request.payload.toString(encoding)
512
395
  request.rawPayload = request.payload
513
396
 
514
- // Incomming request message
397
+ // incomming request message
515
398
  log.notice()
516
399
 
517
400
  log.notice()
518
401
  log.notice(`${method} ${request.path} (λ: ${functionKey})`)
519
402
 
520
- // Check for APIKey
403
+ // check for APIKey
521
404
  if (
522
- (protectedRoutes.includes(`${hapiMethod}#${hapiPath}`) ||
523
- protectedRoutes.includes(`ANY#${hapiPath}`)) &&
405
+ (protectedRoute === `${hapiMethod}#${hapiPath}` ||
406
+ protectedRoute === `ANY#${hapiPath}`) &&
524
407
  !this.#options.noAuth
525
408
  ) {
526
409
  const errorResponse = () =>
@@ -641,24 +524,18 @@ export default class HttpServer {
641
524
  event = request.payload || {}
642
525
  }
643
526
  } else if (integration === 'AWS_PROXY') {
644
- const stageVariables = this.#serverless.service.custom
645
- ? this.#serverless.service.custom.stageVariables
646
- : null
647
-
648
527
  const lambdaProxyIntegrationEvent =
649
528
  endpoint.isHttpApi && endpoint.payload === '2.0'
650
529
  ? new LambdaProxyIntegrationEventV2(
651
530
  request,
652
531
  stage,
653
532
  endpoint.routeKey,
654
- stageVariables,
655
533
  additionalRequestContext,
656
534
  )
657
535
  : new LambdaProxyIntegrationEvent(
658
536
  request,
659
537
  stage,
660
538
  requestPath,
661
- stageVariables,
662
539
  endpoint.isHttpApi ? endpoint.routeKey : null,
663
540
  additionalRequestContext,
664
541
  )
@@ -710,8 +587,7 @@ export default class HttpServer {
710
587
 
711
588
  const errorMessage = (err.message || err).toString()
712
589
 
713
- const re = /\[(\d{3})]/
714
- const found = errorMessage.match(re)
590
+ const found = errorMessage.match(/\[(\d{3})]/)
715
591
 
716
592
  if (found && found.length > 1) {
717
593
  ;[, errorStatusCode] = found
@@ -862,7 +738,9 @@ export default class HttpServer {
862
738
  ).getContext()
863
739
 
864
740
  result = renderVelocityTemplateObject(
865
- { root: responseTemplate },
741
+ {
742
+ root: responseTemplate,
743
+ },
866
744
  reponseContext,
867
745
  ).root
868
746
  } catch (error) {
@@ -971,7 +849,9 @@ export default class HttpServer {
971
849
  headerValue.forEach((value) => {
972
850
  // it looks like Hapi doesn't support multiple headers with the same name,
973
851
  // appending values is the closest we can come to the AWS behavior.
974
- response.header(headerKey, value, { append: true })
852
+ response.header(headerKey, value, {
853
+ append: true,
854
+ })
975
855
  })
976
856
  }
977
857
  })
@@ -1009,7 +889,6 @@ export default class HttpServer {
1009
889
  }
1010
890
  }
1011
891
 
1012
- // Log response
1013
892
  let whatToLog = result
1014
893
 
1015
894
  try {
@@ -1024,9 +903,152 @@ export default class HttpServer {
1024
903
  }
1025
904
  }
1026
905
 
1027
- // Bon voyage!
1028
906
  return response
1029
907
  }
908
+ }
909
+
910
+ createRoutes(functionKey, httpEvent, handler) {
911
+ const [handlerPath] = splitHandlerPathAndName(handler)
912
+
913
+ let method
914
+ let path
915
+ let hapiPath
916
+
917
+ if (httpEvent.isHttpApi) {
918
+ if (httpEvent.routeKey === '$default') {
919
+ method = 'ANY'
920
+ path = httpEvent.routeKey
921
+ hapiPath = '/{default*}'
922
+ } else {
923
+ ;[method, path] = httpEvent.routeKey.split(' ')
924
+ hapiPath = generateHapiPath(
925
+ path,
926
+ {
927
+ ...this.#options,
928
+ noPrependStageInUrl: true, // Serverless always uses the $default stage
929
+ },
930
+ this.#serverless,
931
+ )
932
+ }
933
+ } else {
934
+ method = httpEvent.method.toUpperCase()
935
+ ;({ path } = httpEvent)
936
+ hapiPath = generateHapiPath(path, this.#options, this.#serverless)
937
+ }
938
+
939
+ const endpoint = new Endpoint(
940
+ join(this.#serverless.config.servicePath, handlerPath),
941
+ httpEvent,
942
+ ).generate()
943
+
944
+ const stage = endpoint.isHttpApi
945
+ ? '$default'
946
+ : this.#options.stage || this.#serverless.service.provider.stage
947
+
948
+ const protectedRoute = httpEvent.private
949
+ ? `${method}#${hapiPath}`
950
+ : undefined
951
+
952
+ const { host, httpPort, httpsProtocol } = this.#options
953
+ const server = `${httpsProtocol ? 'https' : 'http'}://${host}:${httpPort}`
954
+
955
+ this.#terminalInfo.push({
956
+ invokePath: `/2015-03-31/functions/${functionKey}/invocations`,
957
+ method,
958
+ path: hapiPath,
959
+ server,
960
+ stage:
961
+ endpoint.isHttpApi || this.#options.noPrependStageInUrl ? null : stage,
962
+ })
963
+
964
+ const authStrategyName = this.#setAuthorizationStrategy(
965
+ endpoint,
966
+ functionKey,
967
+ method,
968
+ path,
969
+ )
970
+
971
+ let cors = null
972
+ if (endpoint.cors) {
973
+ cors = {
974
+ credentials:
975
+ endpoint.cors.credentials || this.#options.corsConfig.credentials,
976
+ exposedHeaders: this.#options.corsConfig.exposedHeaders,
977
+ headers: endpoint.cors.headers || this.#options.corsConfig.headers,
978
+ origin: endpoint.cors.origins || this.#options.corsConfig.origin,
979
+ }
980
+ } else if (
981
+ this.#serverless.service.provider.httpApi &&
982
+ this.#serverless.service.provider.httpApi.cors
983
+ ) {
984
+ const httpApiCors = getHttpApiCorsConfig(
985
+ this.#serverless.service.provider.httpApi.cors,
986
+ this,
987
+ )
988
+ cors = {
989
+ credentials: httpApiCors.allowCredentials,
990
+ exposedHeaders: httpApiCors.exposedResponseHeaders || [],
991
+ headers: httpApiCors.allowedHeaders || [],
992
+ maxAge: httpApiCors.maxAge,
993
+ origin: httpApiCors.allowedOrigins || [],
994
+ }
995
+ }
996
+
997
+ const hapiMethod = method === 'ANY' ? '*' : method
998
+
999
+ const state = this.#options.disableCookieValidation
1000
+ ? {
1001
+ failAction: 'ignore',
1002
+ parse: false,
1003
+ }
1004
+ : {
1005
+ failAction: 'error',
1006
+ parse: true,
1007
+ }
1008
+
1009
+ const hapiOptions = {
1010
+ auth: authStrategyName,
1011
+ cors,
1012
+ state,
1013
+ timeout: { socket: false },
1014
+ }
1015
+
1016
+ // skip HEAD routes as hapi will fail with 'Method name not allowed: HEAD ...'
1017
+ // for more details, check https://github.com/dherault/serverless-offline/issues/204
1018
+ if (hapiMethod === 'HEAD') {
1019
+ log.notice(
1020
+ 'HEAD method event detected. Skipping HAPI server route mapping',
1021
+ )
1022
+
1023
+ return
1024
+ }
1025
+
1026
+ if (hapiMethod !== 'HEAD' && hapiMethod !== 'GET') {
1027
+ // maxBytes: Increase request size from 1MB default limit to 10MB.
1028
+ // Cf AWS API GW payload limits.
1029
+ hapiOptions.payload = {
1030
+ maxBytes: 1024 * 1024 * 10,
1031
+ parse: false,
1032
+ }
1033
+ }
1034
+
1035
+ const additionalRequestContext = {}
1036
+ if (httpEvent.operationId) {
1037
+ additionalRequestContext.operationName = httpEvent.operationId
1038
+ }
1039
+
1040
+ hapiOptions.tags = ['api']
1041
+
1042
+ const hapiHandler = this.#createHapiHandler({
1043
+ additionalRequestContext,
1044
+ endpoint,
1045
+ functionKey,
1046
+ hapiMethod,
1047
+ hapiPath,
1048
+ method,
1049
+ protectedRoute,
1050
+ stage,
1051
+ })
1030
1052
 
1031
1053
  this.#server.route({
1032
1054
  handler: hapiHandler,
@@ -30,22 +30,12 @@ export default class LambdaProxyIntegrationEvent {
30
30
 
31
31
  #stage = null
32
32
 
33
- #stageVariables = null
34
-
35
- constructor(
36
- request,
37
- stage,
38
- path,
39
- stageVariables,
40
- routeKey,
41
- additionalRequestContext,
42
- ) {
33
+ constructor(request, stage, path, routeKey, additionalRequestContext) {
43
34
  this.#additionalRequestContext = additionalRequestContext || {}
44
35
  this.#path = path
45
36
  this.#routeKey = routeKey
46
37
  this.#request = request
47
38
  this.#stage = stage
48
- this.#stageVariables = stageVariables
49
39
  }
50
40
 
51
41
  create() {
@@ -227,7 +217,7 @@ export default class LambdaProxyIntegrationEvent {
227
217
  stage: this.#stage,
228
218
  },
229
219
  resource,
230
- stageVariables: this.#stageVariables,
220
+ stageVariables: null,
231
221
  }
232
222
  }
233
223
  }
@@ -24,20 +24,11 @@ export default class LambdaProxyIntegrationEventV2 {
24
24
 
25
25
  #stage = null
26
26
 
27
- #stageVariables = null
28
-
29
- constructor(
30
- request,
31
- stage,
32
- routeKey,
33
- stageVariables,
34
- additionalRequestContext,
35
- ) {
27
+ constructor(request, stage, routeKey, additionalRequestContext) {
36
28
  this.#additionalRequestContext = additionalRequestContext || {}
37
29
  this.#routeKey = routeKey
38
30
  this.#request = request
39
31
  this.#stage = stage
40
- this.#stageVariables = stageVariables
41
32
  }
42
33
 
43
34
  create() {
@@ -183,7 +174,7 @@ export default class LambdaProxyIntegrationEventV2 {
183
174
  timeEpoch: requestTimeEpoch,
184
175
  },
185
176
  routeKey: this.#routeKey,
186
- stageVariables: this.#stageVariables,
177
+ stageVariables: null,
187
178
  version: '2.0',
188
179
  }
189
180
  }
@@ -39,9 +39,9 @@ export default class Schedule {
39
39
  const cron = this.#convertExpressionToCron(entry)
40
40
 
41
41
  log.notice(
42
- `Scheduling [${functionKey}] cron: [${cron}] input: ${stringify(
43
- input,
44
- )}`,
42
+ `Scheduling [${functionKey}] cron: [${cron}]${
43
+ input ? ` input: ${stringify(input)}` : ''
44
+ }`,
45
45
  )
46
46
 
47
47
  nodeSchedule.scheduleJob(cron, async () => {
@@ -7,9 +7,9 @@ import { execa } from 'execa'
7
7
 
8
8
  const { parse, stringify } = JSON
9
9
 
10
- const PAYLOAD_IDENTIFIER = 'offline_payload'
11
-
12
10
  export default class GoRunner {
11
+ static #payloadIdentifier = 'offline_payload'
12
+
13
13
  #codeDir = null
14
14
 
15
15
  #env = null
@@ -51,12 +51,10 @@ export default class GoRunner {
51
51
  let payload
52
52
 
53
53
  for (const item of value.split(EOL)) {
54
- if (item.indexOf(PAYLOAD_IDENTIFIER) === -1) {
55
- logs.push(item)
56
- } else if (item.indexOf(PAYLOAD_IDENTIFIER) !== -1) {
54
+ if (item.includes(GoRunner.#payloadIdentifier)) {
57
55
  try {
58
56
  const {
59
- offline_payload: { success, error },
57
+ [GoRunner.#payloadIdentifier]: { error, success },
60
58
  } = parse(item)
61
59
 
62
60
  if (success) {
@@ -67,6 +65,8 @@ export default class GoRunner {
67
65
  } catch {
68
66
  // @ignore
69
67
  }
68
+ } else {
69
+ logs.push(item)
70
70
  }
71
71
  }
72
72
 
@@ -50,7 +50,6 @@ export default class InProcessRunner {
50
50
  let handler
51
51
 
52
52
  try {
53
- // const { [this.#handlerName]: handler } = await import(this.#handlerPath)
54
53
  // eslint-disable-next-line import/no-dynamic-require
55
54
  ;({ [this.#handlerName]: handler } = require(this.#handlerPath))
56
55
  } catch (err) {
@@ -87,15 +86,21 @@ export default class InProcessRunner {
87
86
  // create new immutable object
88
87
  const lambdaContext = {
89
88
  ...context,
90
- done: (err, data) => callback(err, data),
91
- fail: (err) => callback(err),
89
+ done(err, data) {
90
+ callback(err, data)
91
+ },
92
+ fail(err) {
93
+ callback(err)
94
+ },
92
95
  getRemainingTimeInMillis() {
93
96
  const timeLeft = executionTimeout - performance.now()
94
97
 
95
98
  // just return 0 for now if we are beyond alotted time (timeout)
96
99
  return timeLeft > 0 ? floor(timeLeft) : 0
97
100
  },
98
- succeed: (res) => callback(null, res),
101
+ succeed(res) {
102
+ callback(null, res)
103
+ },
99
104
  }
100
105
 
101
106
  let result
@@ -4,9 +4,11 @@ import { log } from '@serverless/utils/log.js'
4
4
  import { invokeJavaLocal } from 'java-invoke-local'
5
5
 
6
6
  const { parse, stringify } = JSON
7
- const { has } = Reflect
7
+ const { hasOwn } = Object
8
8
 
9
9
  export default class JavaRunner {
10
+ static #payloadIdentifier = '__offline_payload__'
11
+
10
12
  #deployPackage = null
11
13
 
12
14
  #env = null
@@ -45,9 +47,9 @@ export default class JavaRunner {
45
47
  if (
46
48
  json &&
47
49
  typeof json === 'object' &&
48
- has(json, '__offline_payload__')
50
+ hasOwn(json, JavaRunner.#payloadIdentifier)
49
51
  ) {
50
- return json.__offline_payload__
52
+ return json[JavaRunner.#payloadIdentifier]
51
53
  }
52
54
  }
53
55
 
@@ -7,12 +7,13 @@ import { fileURLToPath } from 'node:url'
7
7
  import { log } from '@serverless/utils/log.js'
8
8
 
9
9
  const { parse, stringify } = JSON
10
- const { assign } = Object
11
- const { has } = Reflect
10
+ const { assign, hasOwn } = Object
12
11
 
13
12
  const __dirname = dirname(fileURLToPath(import.meta.url))
14
13
 
15
14
  export default class PythonRunner {
15
+ static #payloadIdentifier = '__offline_payload__'
16
+
16
17
  #env = null
17
18
 
18
19
  #handlerName = null
@@ -83,9 +84,9 @@ export default class PythonRunner {
83
84
  if (
84
85
  json &&
85
86
  typeof json === 'object' &&
86
- has(json, '__offline_payload__')
87
+ hasOwn(json, PythonRunner.#payloadIdentifier)
87
88
  ) {
88
- payload = json.__offline_payload__
89
+ payload = json[PythonRunner.#payloadIdentifier]
89
90
  // everything else is print(), logging, ...
90
91
  } else {
91
92
  log.notice(item)
@@ -6,11 +6,13 @@ import { log } from '@serverless/utils/log.js'
6
6
  import { execa } from 'execa'
7
7
 
8
8
  const { parse, stringify } = JSON
9
- const { has } = Reflect
9
+ const { hasOwn } = Object
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url))
12
12
 
13
13
  export default class RubyRunner {
14
+ static #payloadIdentifier = '__offline_payload__'
15
+
14
16
  #env = null
15
17
 
16
18
  #handlerName = null
@@ -47,9 +49,9 @@ export default class RubyRunner {
47
49
  if (
48
50
  json &&
49
51
  typeof json === 'object' &&
50
- has(json, '__offline_payload__')
52
+ hasOwn(json, RubyRunner.#payloadIdentifier)
51
53
  ) {
52
- payload = json.__offline_payload__
54
+ payload = json[RubyRunner.#payloadIdentifier]
53
55
  } else {
54
56
  log.notice(item)
55
57
  }