serverless-offline 8.8.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.
Files changed (233) hide show
  1. package/README.md +12 -11
  2. package/package.json +33 -58
  3. package/src/ServerlessOffline.js +409 -0
  4. package/src/config/commandOptions.js +159 -0
  5. package/src/config/constants.js +22 -0
  6. package/{dist → src}/config/defaultOptions.js +9 -17
  7. package/src/config/index.js +4 -0
  8. package/src/config/supportedRuntimes.js +47 -0
  9. package/src/events/authCanExecuteResource.js +35 -0
  10. package/src/events/authFunctionNameExtractor.js +75 -0
  11. package/src/events/authMatchPolicyResource.js +71 -0
  12. package/src/events/authValidateContext.js +51 -0
  13. package/src/events/http/Endpoint.js +135 -0
  14. package/src/events/http/Http.js +50 -0
  15. package/src/events/http/HttpEventDefinition.js +20 -0
  16. package/src/events/http/HttpServer.js +1242 -0
  17. package/src/events/http/OfflineEndpoint.js +33 -0
  18. package/src/events/http/authJWTSettingsExtractor.js +70 -0
  19. package/src/events/http/createAuthScheme.js +176 -0
  20. package/src/events/http/createJWTAuthScheme.js +106 -0
  21. package/src/events/http/index.js +1 -0
  22. package/src/events/http/javaHelpers.js +102 -0
  23. package/src/events/http/lambda-events/LambdaIntegrationEvent.js +57 -0
  24. package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +233 -0
  25. package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +190 -0
  26. package/src/events/http/lambda-events/VelocityContext.js +147 -0
  27. package/src/events/http/lambda-events/index.js +4 -0
  28. package/src/events/http/lambda-events/renderVelocityTemplateObject.js +93 -0
  29. package/{dist → src}/events/http/parseResources.js +73 -78
  30. package/src/events/http/payloadSchemaValidator.js +13 -0
  31. package/{dist → src}/events/http/templates/offline-default.req.vm +0 -0
  32. package/{dist → src}/events/http/templates/offline-default.res.vm +0 -0
  33. package/src/events/schedule/Schedule.js +131 -0
  34. package/src/events/schedule/ScheduleEvent.js +18 -0
  35. package/src/events/schedule/ScheduleEventDefinition.js +21 -0
  36. package/src/events/schedule/index.js +1 -0
  37. package/src/events/websocket/HttpServer.js +69 -0
  38. package/src/events/websocket/WebSocket.js +52 -0
  39. package/src/events/websocket/WebSocketClients.js +462 -0
  40. package/src/events/websocket/WebSocketEventDefinition.js +18 -0
  41. package/src/events/websocket/WebSocketServer.js +73 -0
  42. package/src/events/websocket/http-routes/_catchAll/catchAllRoute.js +16 -0
  43. package/src/events/websocket/http-routes/_catchAll/index.js +1 -0
  44. package/src/events/websocket/http-routes/connections/ConnectionsController.js +28 -0
  45. package/src/events/websocket/http-routes/connections/connectionsRoutes.js +70 -0
  46. package/src/events/websocket/http-routes/connections/index.js +1 -0
  47. package/src/events/websocket/http-routes/index.js +2 -0
  48. package/src/events/websocket/index.js +1 -0
  49. package/src/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +65 -0
  50. package/src/events/websocket/lambda-events/WebSocketConnectEvent.js +68 -0
  51. package/src/events/websocket/lambda-events/WebSocketDisconnectEvent.js +31 -0
  52. package/src/events/websocket/lambda-events/WebSocketEvent.js +29 -0
  53. package/src/events/websocket/lambda-events/WebSocketRequestContext.js +67 -0
  54. package/src/events/websocket/lambda-events/index.js +4 -0
  55. package/src/index.js +12 -0
  56. package/src/lambda/HttpServer.js +108 -0
  57. package/src/lambda/Lambda.js +68 -0
  58. package/src/lambda/LambdaContext.js +33 -0
  59. package/src/lambda/LambdaFunction.js +309 -0
  60. package/src/lambda/LambdaFunctionPool.js +109 -0
  61. package/src/lambda/__tests__/LambdaContext.test.js +30 -0
  62. package/src/lambda/__tests__/LambdaFunction.test.js +196 -0
  63. package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js +47 -0
  64. package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js +46 -0
  65. package/src/lambda/__tests__/fixtures/Lambda/package.json +3 -0
  66. package/src/lambda/__tests__/fixtures/lambdaFunction.fixture.js +145 -0
  67. package/src/lambda/__tests__/fixtures/package.json +3 -0
  68. package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +42 -0
  69. package/src/lambda/handler-runner/HandlerRunner.js +136 -0
  70. package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +72 -0
  71. package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +42 -0
  72. package/src/lambda/handler-runner/child-process-runner/index.js +1 -0
  73. package/src/lambda/handler-runner/docker-runner/DockerContainer.js +417 -0
  74. package/src/lambda/handler-runner/docker-runner/DockerImage.js +35 -0
  75. package/src/lambda/handler-runner/docker-runner/DockerRunner.js +63 -0
  76. package/src/lambda/handler-runner/docker-runner/index.js +1 -0
  77. package/src/lambda/handler-runner/go-runner/GoRunner.js +167 -0
  78. package/src/lambda/handler-runner/go-runner/index.js +1 -0
  79. package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +125 -0
  80. package/src/lambda/handler-runner/in-process-runner/index.js +1 -0
  81. package/src/lambda/handler-runner/index.js +1 -0
  82. package/src/lambda/handler-runner/java-runner/JavaRunner.js +114 -0
  83. package/src/lambda/handler-runner/java-runner/index.js +1 -0
  84. package/src/lambda/handler-runner/python-runner/PythonRunner.js +138 -0
  85. package/src/lambda/handler-runner/python-runner/index.js +1 -0
  86. package/{dist → src}/lambda/handler-runner/python-runner/invoke.py +0 -0
  87. package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +107 -0
  88. package/src/lambda/handler-runner/ruby-runner/index.js +1 -0
  89. package/{dist → src}/lambda/handler-runner/ruby-runner/invoke.rb +0 -0
  90. package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +70 -0
  91. package/src/lambda/handler-runner/worker-thread-runner/index.js +1 -0
  92. package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +29 -0
  93. package/src/lambda/index.js +1 -0
  94. package/src/lambda/routes/index.js +2 -0
  95. package/src/lambda/routes/invocations/InvocationsController.js +102 -0
  96. package/src/lambda/routes/invocations/index.js +1 -0
  97. package/src/lambda/routes/invocations/invocationsRoute.js +77 -0
  98. package/src/lambda/routes/invoke-async/InvokeAsyncController.js +20 -0
  99. package/src/lambda/routes/invoke-async/index.js +1 -0
  100. package/src/lambda/routes/invoke-async/invokeAsyncRoute.js +33 -0
  101. package/src/utils/__tests__/createUniqueId.test.js +18 -0
  102. package/src/utils/__tests__/formatToClfTime.test.js +14 -0
  103. package/src/utils/__tests__/generateHapiPath.test.js +46 -0
  104. package/src/utils/__tests__/lowerCaseKeys.test.js +30 -0
  105. package/src/utils/__tests__/parseHeaders.test.js +13 -0
  106. package/src/utils/__tests__/parseMultiValueHeaders.test.js +24 -0
  107. package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +159 -0
  108. package/src/utils/__tests__/parseQueryStringParameters.test.js +15 -0
  109. package/src/utils/__tests__/splitHandlerPathAndName.test.js +54 -0
  110. package/src/utils/__tests__/unflatten.test.js +32 -0
  111. package/src/utils/checkDockerDaemon.js +19 -0
  112. package/src/utils/checkGoVersion.js +16 -0
  113. package/src/utils/createApiKey.js +5 -0
  114. package/src/utils/createUniqueId.js +5 -0
  115. package/src/utils/detectExecutable.js +11 -0
  116. package/{dist → src}/utils/formatToClfTime.js +6 -14
  117. package/src/utils/generateHapiPath.js +26 -0
  118. package/src/utils/getHttpApiCorsConfig.js +28 -0
  119. package/src/utils/index.js +42 -0
  120. package/src/utils/jsonPath.js +13 -0
  121. package/src/utils/logRoutes.js +64 -0
  122. package/src/utils/lowerCaseKeys.js +6 -0
  123. package/src/utils/parseHeaders.js +14 -0
  124. package/src/utils/parseMultiValueHeaders.js +27 -0
  125. package/src/utils/parseMultiValueQueryStringParameters.js +31 -0
  126. package/src/utils/parseQueryStringParameters.js +15 -0
  127. package/src/utils/splitHandlerPathAndName.js +31 -0
  128. package/src/utils/unflatten.js +11 -0
  129. package/dist/ServerlessOffline.js +0 -514
  130. package/dist/config/commandOptions.js +0 -149
  131. package/dist/config/constants.js +0 -30
  132. package/dist/config/index.js +0 -55
  133. package/dist/config/supportedRuntimes.js +0 -40
  134. package/dist/debugLog.js +0 -12
  135. package/dist/events/authCanExecuteResource.js +0 -35
  136. package/dist/events/authFunctionNameExtractor.js +0 -87
  137. package/dist/events/authMatchPolicyResource.js +0 -62
  138. package/dist/events/authValidateContext.js +0 -53
  139. package/dist/events/http/Endpoint.js +0 -173
  140. package/dist/events/http/Http.js +0 -77
  141. package/dist/events/http/HttpEventDefinition.js +0 -36
  142. package/dist/events/http/HttpServer.js +0 -1370
  143. package/dist/events/http/OfflineEndpoint.js +0 -38
  144. package/dist/events/http/authJWTSettingsExtractor.js +0 -76
  145. package/dist/events/http/createAuthScheme.js +0 -184
  146. package/dist/events/http/createJWTAuthScheme.js +0 -159
  147. package/dist/events/http/index.js +0 -15
  148. package/dist/events/http/javaHelpers.js +0 -99
  149. package/dist/events/http/lambda-events/LambdaIntegrationEvent.js +0 -87
  150. package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -246
  151. package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -225
  152. package/dist/events/http/lambda-events/VelocityContext.js +0 -170
  153. package/dist/events/http/lambda-events/index.js +0 -39
  154. package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -111
  155. package/dist/events/http/payloadSchemaValidator.js +0 -13
  156. package/dist/events/schedule/Schedule.js +0 -183
  157. package/dist/events/schedule/ScheduleEvent.js +0 -27
  158. package/dist/events/schedule/ScheduleEventDefinition.js +0 -36
  159. package/dist/events/schedule/index.js +0 -15
  160. package/dist/events/websocket/HttpServer.js +0 -114
  161. package/dist/events/websocket/WebSocket.js +0 -78
  162. package/dist/events/websocket/WebSocketClients.js +0 -577
  163. package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
  164. package/dist/events/websocket/WebSocketServer.js +0 -139
  165. package/dist/events/websocket/http-routes/_catchAll/catchAllRoute.js +0 -33
  166. package/dist/events/websocket/http-routes/_catchAll/index.js +0 -15
  167. package/dist/events/websocket/http-routes/connections/ConnectionsController.js +0 -45
  168. package/dist/events/websocket/http-routes/connections/connectionsRoutes.js +0 -95
  169. package/dist/events/websocket/http-routes/connections/index.js +0 -15
  170. package/dist/events/websocket/http-routes/index.js +0 -23
  171. package/dist/events/websocket/index.js +0 -15
  172. package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +0 -99
  173. package/dist/events/websocket/lambda-events/WebSocketConnectEvent.js +0 -101
  174. package/dist/events/websocket/lambda-events/WebSocketDisconnectEvent.js +0 -47
  175. package/dist/events/websocket/lambda-events/WebSocketEvent.js +0 -54
  176. package/dist/events/websocket/lambda-events/WebSocketRequestContext.js +0 -98
  177. package/dist/events/websocket/lambda-events/index.js +0 -39
  178. package/dist/index.js +0 -15
  179. package/dist/lambda/HttpServer.js +0 -124
  180. package/dist/lambda/Lambda.js +0 -117
  181. package/dist/lambda/LambdaContext.js +0 -53
  182. package/dist/lambda/LambdaFunction.js +0 -390
  183. package/dist/lambda/LambdaFunctionPool.js +0 -127
  184. package/dist/lambda/handler-runner/HandlerRunner.js +0 -195
  185. package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -124
  186. package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -49
  187. package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
  188. package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -515
  189. package/dist/lambda/handler-runner/docker-runner/DockerImage.js +0 -67
  190. package/dist/lambda/handler-runner/docker-runner/DockerRunner.js +0 -74
  191. package/dist/lambda/handler-runner/docker-runner/index.js +0 -15
  192. package/dist/lambda/handler-runner/go-runner/GoRunner.js +0 -230
  193. package/dist/lambda/handler-runner/go-runner/index.js +0 -15
  194. package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -228
  195. package/dist/lambda/handler-runner/in-process-runner/index.js +0 -15
  196. package/dist/lambda/handler-runner/index.js +0 -15
  197. package/dist/lambda/handler-runner/java-runner/JavaRunner.js +0 -153
  198. package/dist/lambda/handler-runner/java-runner/index.js +0 -15
  199. package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -185
  200. package/dist/lambda/handler-runner/python-runner/index.js +0 -15
  201. package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -147
  202. package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
  203. package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -92
  204. package/dist/lambda/handler-runner/worker-thread-runner/index.js +0 -15
  205. package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +0 -31
  206. package/dist/lambda/index.js +0 -15
  207. package/dist/lambda/routes/index.js +0 -23
  208. package/dist/lambda/routes/invocations/InvocationsController.js +0 -142
  209. package/dist/lambda/routes/invocations/index.js +0 -15
  210. package/dist/lambda/routes/invocations/invocationsRoute.js +0 -90
  211. package/dist/lambda/routes/invoke-async/InvokeAsyncController.js +0 -38
  212. package/dist/lambda/routes/invoke-async/index.js +0 -15
  213. package/dist/lambda/routes/invoke-async/invokeAsyncRoute.js +0 -43
  214. package/dist/main.js +0 -11
  215. package/dist/serverlessLog.js +0 -91
  216. package/dist/utils/checkDockerDaemon.js +0 -27
  217. package/dist/utils/checkGoVersion.js +0 -27
  218. package/dist/utils/createApiKey.js +0 -12
  219. package/dist/utils/createUniqueId.js +0 -14
  220. package/dist/utils/detectExecutable.js +0 -21
  221. package/dist/utils/generateHapiPath.js +0 -28
  222. package/dist/utils/getHttpApiCorsConfig.js +0 -40
  223. package/dist/utils/index.js +0 -165
  224. package/dist/utils/jsonPath.js +0 -21
  225. package/dist/utils/lowerCaseKeys.js +0 -14
  226. package/dist/utils/parseHeaders.js +0 -23
  227. package/dist/utils/parseMultiValueHeaders.js +0 -36
  228. package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
  229. package/dist/utils/parseQueryStringParameters.js +0 -26
  230. package/dist/utils/resolveJoins.js +0 -36
  231. package/dist/utils/satisfiesVersionRange.js +0 -20
  232. package/dist/utils/splitHandlerPathAndName.js +0 -37
  233. package/dist/utils/unflatten.js +0 -18
@@ -0,0 +1,102 @@
1
+ import { log } from '@serverless/utils/log.js'
2
+
3
+ export default class InvocationsController {
4
+ #lambda = null
5
+
6
+ constructor(lambda) {
7
+ this.#lambda = lambda
8
+ }
9
+
10
+ async invoke(functionName, invocationType, event, clientContext) {
11
+ // Reject gracefully if functionName does not exist
12
+ const functionNames = this.#lambda.listFunctionNames()
13
+ if (functionNames.length === 0 || !functionNames.includes(functionName)) {
14
+ log.error(
15
+ `Attempt to invoke function '${functionName}' failed. Function does not exists.`,
16
+ )
17
+ // Conforms to the actual response from AWS Lambda when invoking a non-existent
18
+ // function. Details on the error are provided in the Payload.Message key
19
+ return {
20
+ FunctionError: 'ResourceNotFoundException',
21
+ Payload: {
22
+ Message: `Function not found: ${functionName}`,
23
+ Type: 'User',
24
+ },
25
+ StatusCode: 404,
26
+ }
27
+ }
28
+
29
+ const lambdaFunction = this.#lambda.getByFunctionName(functionName)
30
+
31
+ lambdaFunction.setClientContext(clientContext)
32
+ lambdaFunction.setEvent(event)
33
+
34
+ if (invocationType === 'Event') {
35
+ // don't await result!
36
+ lambdaFunction.runHandler()
37
+ return {
38
+ Payload: '',
39
+ StatusCode: 202,
40
+ }
41
+ }
42
+
43
+ if (!invocationType || invocationType === 'RequestResponse') {
44
+ let result
45
+
46
+ try {
47
+ result = await lambdaFunction.runHandler()
48
+ } catch (err) {
49
+ log.error(
50
+ `Unhandled Lambda Error during invoke of '${functionName}': ${err}`,
51
+ )
52
+ // In most circumstances this is the correct error type/structure.
53
+ // The API returns a StreamingBody with status code of 200
54
+ // that eventually spits out the error and stack trace.
55
+ // When the request is synchronous, aws-sdk should buffer
56
+ // the whole error stream, however this has not been validated.
57
+ return {
58
+ Payload: {
59
+ errorMessage: err.message,
60
+ errorType: 'Error',
61
+ trace: err.stack.split('\n'),
62
+ },
63
+ StatusCode: 200,
64
+ UnhandledError: true,
65
+ }
66
+ // TODO: Additional pre and post-handler validation can expose
67
+ // the following error types:
68
+ // RequestTooLargeException, InvalidParameterValueException,
69
+ // and whatever response is thrown when the response is too large.
70
+ }
71
+
72
+ // Checking if the result of the Lambda Invoke is a primitive string to wrap it. this is for future post-processing such as Step Functions Tasks
73
+ if (result) {
74
+ if (typeof result === 'string') {
75
+ result = `"${result}"`
76
+ }
77
+ }
78
+
79
+ // result is actually the Payload.
80
+ // So return in a standard structure so Hapi can
81
+ // respond with the correct status codes
82
+ return {
83
+ Payload: result,
84
+ StatusCode: 200,
85
+ }
86
+ }
87
+
88
+ // TODO FIXME
89
+ const errMsg = `invocationType: '${invocationType}' not supported by serverless-offline`
90
+
91
+ log.error(errMsg)
92
+
93
+ return {
94
+ FunctionError: 'InvalidParameterValueException',
95
+ Payload: {
96
+ Message: errMsg,
97
+ Type: 'User',
98
+ },
99
+ StatusCode: 400,
100
+ }
101
+ }
102
+ }
@@ -0,0 +1 @@
1
+ export { default } from './invocationsRoute.js'
@@ -0,0 +1,77 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import InvocationsController from './InvocationsController.js'
3
+
4
+ const { parse } = JSON
5
+
6
+ // https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html
7
+ export default function invocationsRoute(lambda, options) {
8
+ const invocationsController = new InvocationsController(lambda)
9
+
10
+ return {
11
+ async handler(request, h) {
12
+ const {
13
+ headers,
14
+ params: { functionName },
15
+ payload,
16
+ } = request
17
+
18
+ const parsedHeaders = new Headers(headers)
19
+ const clientContextHeader = parsedHeaders.get('x-amz-client-context')
20
+ const invocationType = parsedHeaders.get('x-amz-invocation-type')
21
+
22
+ // default is undefined
23
+ let clientContext
24
+
25
+ // check client context header was set
26
+ if (clientContextHeader) {
27
+ const clientContextBuffer = Buffer.from(clientContextHeader, 'base64')
28
+ clientContext = parse(clientContextBuffer.toString('utf-8'))
29
+ }
30
+
31
+ // check if payload was set, if not, default event is an empty object
32
+ const event = payload.length > 0 ? parse(payload.toString('utf-8')) : {}
33
+
34
+ const invokeResults = await invocationsController.invoke(
35
+ functionName,
36
+ invocationType,
37
+ event,
38
+ clientContext,
39
+ )
40
+
41
+ // Return with correct status codes
42
+ let resultPayload = ''
43
+ let statusCode = 200
44
+ let functionError = null
45
+ if (invokeResults) {
46
+ const isPayloadDefined = typeof invokeResults.Payload !== 'undefined'
47
+ resultPayload = isPayloadDefined ? invokeResults.Payload : ''
48
+ statusCode = invokeResults.StatusCode || 200
49
+ functionError = invokeResults.FunctionError || null
50
+ }
51
+ const response = h.response(resultPayload).code(statusCode)
52
+ if (functionError) {
53
+ // AWS Invoke documentation is wrong. The header for error type is
54
+ // 'x-amzn-ErrorType' in production, not 'X-Amz-Function-Error'
55
+ response.header('x-amzn-ErrorType', functionError)
56
+ }
57
+ if (invokeResults && invokeResults.UnhandledError) {
58
+ response.header('X-Amz-Function-Error', 'Unhandled')
59
+ }
60
+ return response
61
+ },
62
+ method: 'POST',
63
+ options: {
64
+ cors: options.corsConfig,
65
+ payload: {
66
+ // allow: ['binary/octet-stream'],
67
+ defaultContentType: 'binary/octet-stream',
68
+ // Set maximum size to 6 MB to match maximum invocation payload size in synchronous responses
69
+ maxBytes: 1024 * 1024 * 6,
70
+ // request.payload will be a raw buffer
71
+ parse: false,
72
+ },
73
+ tags: ['api'],
74
+ },
75
+ path: '/2015-03-31/functions/{functionName}/invocations',
76
+ }
77
+ }
@@ -0,0 +1,20 @@
1
+ export default class InvokeAsyncController {
2
+ #lambda = null
3
+
4
+ constructor(lambda) {
5
+ this.#lambda = lambda
6
+ }
7
+
8
+ async invokeAsync(functionName, event) {
9
+ const lambdaFunction = this.#lambda.getByFunctionName(functionName)
10
+
11
+ lambdaFunction.setEvent(event)
12
+
13
+ // don't await result!
14
+ lambdaFunction.runHandler()
15
+
16
+ return {
17
+ StatusCode: 202,
18
+ }
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ export { default } from './invokeAsyncRoute.js'
@@ -0,0 +1,33 @@
1
+ import InvokeAsyncController from './InvokeAsyncController.js'
2
+
3
+ const { parse } = JSON
4
+
5
+ // https://docs.aws.amazon.com/lambda/latest/dg/API_InvokeAsync.html
6
+ export default function invokeRoute(lambda, options) {
7
+ const invokeAsyncController = new InvokeAsyncController(lambda)
8
+
9
+ return {
10
+ handler(request) {
11
+ const {
12
+ params: { functionName },
13
+ payload,
14
+ } = request
15
+
16
+ const event = parse(payload.toString('utf-8'))
17
+
18
+ return invokeAsyncController.invokeAsync(functionName, event)
19
+ },
20
+ method: 'POST',
21
+ options: {
22
+ cors: options.corsConfig,
23
+ payload: {
24
+ // allow: ['binary/octet-stream'],
25
+ defaultContentType: 'binary/octet-stream',
26
+ // request.payload will be a raw buffer
27
+ parse: false,
28
+ },
29
+ tags: ['api'],
30
+ },
31
+ path: '/2014-11-13/functions/{functionName}/invoke-async/',
32
+ }
33
+ }
@@ -0,0 +1,18 @@
1
+ import assert from 'node:assert'
2
+ import createUniqueId from '../createUniqueId.js'
3
+
4
+ describe('createUniqueId', () => {
5
+ it('should be unique', () => {
6
+ const items = 100000
7
+ const set = new Set(Array.from(Array(items)).map(createUniqueId))
8
+
9
+ assert.equal(set.size, items)
10
+ })
11
+
12
+ it('should be a 36 character string', () => {
13
+ const id = createUniqueId()
14
+
15
+ assert.equal(typeof id, 'string')
16
+ assert.equal(id.length, 36)
17
+ })
18
+ })
@@ -0,0 +1,14 @@
1
+ import assert from 'node:assert'
2
+ import formatToClfTime from '../formatToClfTime.js'
3
+
4
+ const { now } = Date
5
+
6
+ describe('formatToClfTime', () => {
7
+ it('should return "common log format" formatted time', () => {
8
+ const millis = now()
9
+ const result = formatToClfTime(millis)
10
+
11
+ // expected: 17/Dec/1995:03:24:00 -0500 (with varying offset)
12
+ assert.match(result, /([\w:/]+\s[+-]\d{4})/)
13
+ })
14
+ })
@@ -0,0 +1,46 @@
1
+ import assert from 'node:assert'
2
+ import generateHapiPath from '../generateHapiPath.js'
3
+
4
+ const serverless = {
5
+ service: {
6
+ provider: {
7
+ stage: 'dev',
8
+ },
9
+ },
10
+ }
11
+
12
+ describe('generateHapiPath', () => {
13
+ it('should generate url starting with a slash', () => {
14
+ const options = {}
15
+ const result = generateHapiPath('users', options, serverless)
16
+ assert.equal(result[0], '/')
17
+ })
18
+
19
+ it('should generate url with the stage prepended', () => {
20
+ const options = {}
21
+ const result = generateHapiPath('users', options, serverless)
22
+ assert.equal(result, '/dev/users')
23
+ })
24
+
25
+ describe('when a prefix option is set', () => {
26
+ it('the url should add the prefix', () => {
27
+ const options = { prefix: 'some-prefix' }
28
+ const result = generateHapiPath('users', options, serverless)
29
+ assert.equal(result, '/some-prefix/dev/users')
30
+ })
31
+ })
32
+
33
+ describe('when the noPrependStageInUrl option is set', () => {
34
+ it('the url should omit the stage', () => {
35
+ const options = { noPrependStageInUrl: true }
36
+ const result = generateHapiPath('users', options, serverless)
37
+ assert.equal(result, '/users')
38
+ })
39
+ })
40
+
41
+ it('the stage from options should override stage from serverless config', () => {
42
+ const options = { stage: 'prod' }
43
+ const result = generateHapiPath('users', options, serverless)
44
+ assert.equal(result, '/prod/users')
45
+ })
46
+ })
@@ -0,0 +1,30 @@
1
+ import assert from 'node:assert'
2
+ import lowerCaseKeys from '../lowerCaseKeys.js'
3
+
4
+ describe('lowerCaseKeys', () => {
5
+ it('should handle empty object', () => {
6
+ const result = lowerCaseKeys({})
7
+ assert.deepEqual(result, {})
8
+ })
9
+
10
+ it('should handle object with one key', () => {
11
+ const result = lowerCaseKeys({ 'Some-Key': 'value' })
12
+ assert.deepEqual(result, { 'some-key': 'value' })
13
+ })
14
+
15
+ it('should handle object with multiple keys', () => {
16
+ const result = lowerCaseKeys({
17
+ 'already-lowercase': 'cool',
18
+ 'Another-Key': 'anotherValue',
19
+ 'lOts-OF-CAPitaLs': 'ButThisIsNotTouched',
20
+ 'Some-Key': 'value',
21
+ })
22
+
23
+ assert.deepEqual(result, {
24
+ 'already-lowercase': 'cool',
25
+ 'another-key': 'anotherValue',
26
+ 'lots-of-capitals': 'ButThisIsNotTouched',
27
+ 'some-key': 'value',
28
+ })
29
+ })
30
+ })
@@ -0,0 +1,13 @@
1
+ import assert from 'node:assert'
2
+ // uses the same tests as parseMultiValueHeaders
3
+ import tests from './parseMultiValueHeaders.test.js'
4
+ import parseHeaders from '../parseHeaders.js'
5
+
6
+ describe('parseQueryStringParameters', () => {
7
+ tests.forEach(({ description, expected, param }) => {
8
+ it(`should return ${description}`, () => {
9
+ const result = parseHeaders(param)
10
+ assert.deepEqual(result, expected)
11
+ })
12
+ })
13
+ })
@@ -0,0 +1,24 @@
1
+ import assert from 'node:assert'
2
+ import parseMultiValueHeaders from '../parseMultiValueHeaders.js'
3
+
4
+ // TODO need more tests
5
+ const tests = [
6
+ {
7
+ description: 'no parameter (empty array)',
8
+ expected: null,
9
+ expectedMulti: null,
10
+ param: [],
11
+ },
12
+ ]
13
+
14
+ describe('parseMultiValueHeaders', () => {
15
+ tests.forEach(({ description, expectedMulti, param }) => {
16
+ it(`should return ${description}`, () => {
17
+ const resultMulti = parseMultiValueHeaders(param)
18
+ assert.deepEqual(resultMulti, expectedMulti)
19
+ })
20
+ })
21
+ })
22
+
23
+ // export tests for parseHeaders
24
+ export default tests
@@ -0,0 +1,159 @@
1
+ import assert from 'node:assert'
2
+ import parseMultiValueQueryStringParameters from '../parseMultiValueQueryStringParameters.js'
3
+
4
+ const tests = [
5
+ {
6
+ description: 'no parameter (empty string)',
7
+ expected: null,
8
+ expectedMulti: null,
9
+ param: '',
10
+ },
11
+
12
+ {
13
+ description: 'string parameter',
14
+ expected: { foo: 'bar' },
15
+ expectedMulti: { foo: ['bar'] },
16
+ param: 'foo=bar',
17
+ },
18
+
19
+ {
20
+ description: 'number parameter (no type casting)',
21
+ expected: { foo: '1' },
22
+ expectedMulti: { foo: ['1'] },
23
+ param: 'foo=1',
24
+ },
25
+
26
+ {
27
+ description: 'boolean parameter (no type casting)',
28
+ expected: { foo: 'true' },
29
+ expectedMulti: { foo: ['true'] },
30
+ param: 'foo=true',
31
+ },
32
+
33
+ {
34
+ description: 'multiple parameters',
35
+ expected: { bar: 'test2', foo: 'test1' },
36
+ expectedMulti: { bar: ['test2'], foo: ['test1'] },
37
+ param: 'foo=test1&bar=test2',
38
+ },
39
+
40
+ {
41
+ description: 'multiple parameters, same keys',
42
+ expected: { foo: 'foobar' },
43
+ expectedMulti: { foo: ['test', 'foobar'] },
44
+ param: 'foo=test&foo=foobar',
45
+ },
46
+
47
+ {
48
+ description: 'multiple parameters, same keys, different casing',
49
+ expected: { foo: 'test', FOO: 'FOOBAR' },
50
+ expectedMulti: { foo: ['test'], FOO: ['FOOBAR'] },
51
+ param: 'foo=test&FOO=FOOBAR',
52
+ },
53
+
54
+ {
55
+ description: 'multiple parameters, same keys, same values',
56
+ expected: { foo: 'test' },
57
+ expectedMulti: { foo: ['test', 'test'] },
58
+ param: 'foo=test&foo=test',
59
+ },
60
+
61
+ {
62
+ description: 'no value',
63
+ expected: { foo: '' },
64
+ expectedMulti: { foo: [''] },
65
+ param: 'foo',
66
+ },
67
+
68
+ {
69
+ description: 'no value with =',
70
+ expected: { foo: '' },
71
+ expectedMulti: { foo: [''] },
72
+ param: 'foo=',
73
+ },
74
+
75
+ {
76
+ description: 'no value with &',
77
+ expected: { foo: '' },
78
+ expectedMulti: { foo: [''] },
79
+ param: 'foo&',
80
+ },
81
+
82
+ {
83
+ description: 'no value with = and &',
84
+ expected: { foo: '' },
85
+ expectedMulti: { foo: [''] },
86
+ param: 'foo=&',
87
+ },
88
+
89
+ {
90
+ description: 'value is whitespace',
91
+ expected: { foo: ' ' },
92
+ expectedMulti: { foo: [' '] },
93
+ param: 'foo=%20',
94
+ },
95
+
96
+ {
97
+ description: 'key and value have whitespace',
98
+ expected: { ' foo ': ' test ' },
99
+ expectedMulti: { ' foo ': [' test '] },
100
+ param: '%20foo%20=%20test%20',
101
+ },
102
+
103
+ {
104
+ description: 'unicode',
105
+ expected: { Σ: '😋' },
106
+ expectedMulti: { Σ: ['😋'] },
107
+ param: 'Σ=😋',
108
+ },
109
+
110
+ {
111
+ description: 'encoded', // encodeURIComponent
112
+ expected: { '?=/&:': '?=/&:' },
113
+ expectedMulti: { '?=/&:': ['?=/&:'] },
114
+ param: '%3F%3D%2F%26%3A=%3F%3D%2F%26%3A',
115
+ },
116
+
117
+ {
118
+ description: 'end of line',
119
+ expected: { '\n': '\n' },
120
+ expectedMulti: { '\n': ['\n'] },
121
+ param: '%0A=%0A',
122
+ },
123
+
124
+ // silly test section:
125
+ {
126
+ description: 'silly I.',
127
+ expected: { test: '?' },
128
+ expectedMulti: { test: ['?'] },
129
+ param: 'test=?',
130
+ },
131
+
132
+ {
133
+ description: 'silly II.',
134
+ expected: { test: '/' },
135
+ expectedMulti: { test: ['/'] },
136
+ param: 'test=/',
137
+ },
138
+
139
+ {
140
+ description: 'silly III.',
141
+ expected: { test: '=' },
142
+ expectedMulti: { test: ['='] },
143
+ param: 'test==',
144
+ },
145
+ ]
146
+
147
+ describe('parseMultiValueQueryStringParameters', () => {
148
+ tests.forEach(({ description, expectedMulti, param }) => {
149
+ const url = `foo?${param}`
150
+
151
+ it(`should return ${description}`, () => {
152
+ const resultMulti = parseMultiValueQueryStringParameters(url)
153
+ assert.deepEqual(resultMulti, expectedMulti)
154
+ })
155
+ })
156
+ })
157
+
158
+ // export tests for parseQueryStringParameters
159
+ export default tests
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert'
2
+ // uses the same tests as parseMultiValueQueryStringParameters
3
+ import tests from './parseMultiValueQueryStringParameters.test.js'
4
+ import parseQueryStringParameters from '../parseQueryStringParameters.js'
5
+
6
+ describe('parseQueryStringParameters', () => {
7
+ tests.forEach(({ description, expected, param }) => {
8
+ const url = `/foo?${param}`
9
+
10
+ it(`should return ${description}`, () => {
11
+ const result = parseQueryStringParameters(url)
12
+ assert.deepEqual(result, expected)
13
+ })
14
+ })
15
+ })
@@ -0,0 +1,54 @@
1
+ import assert from 'node:assert'
2
+ import splitHandlerPathAndName from '../splitHandlerPathAndName.js'
3
+
4
+ const tests = [
5
+ {
6
+ description: 'ruby handler with namespace resolution operator ::',
7
+ expected: ['./src/somefolder/source', 'LambdaFunctions::Handler.process'],
8
+ handler: './src/somefolder/source.LambdaFunctions::Handler.process',
9
+ },
10
+ {
11
+ description: 'ruby handler with multiple namespace resolution operators ::',
12
+ expected: [
13
+ './src/somefolder/source',
14
+ 'Functions::LambdaFunctions::Handler.process',
15
+ ],
16
+ handler:
17
+ './src/somefolder/source.Functions::LambdaFunctions::Handler.process',
18
+ },
19
+ {
20
+ description: 'ruby handler with namespace resolution operator ::, unnested',
21
+ expected: ['source', 'LambdaFunctions::Handler.process'],
22
+ handler: 'source.LambdaFunctions::Handler.process',
23
+ },
24
+ {
25
+ description:
26
+ 'ruby handler with multiple namespace resolution operators ::, unnested',
27
+ expected: ['./source', 'Functions::LambdaFunctions::Handler.process'],
28
+ handler: './source.Functions::LambdaFunctions::Handler.process',
29
+ },
30
+ {
31
+ description: 'ruby handler from kernel',
32
+ expected: ['./src/somefolder/function', 'handler'],
33
+ handler: './src/somefolder/function.handler',
34
+ },
35
+ {
36
+ description: 'generic handler',
37
+ expected: ['./src/somefolder/.handlers/handler', 'run'],
38
+ handler: './src/somefolder/.handlers/handler.run',
39
+ },
40
+ {
41
+ description: 'generic handler, unnested',
42
+ expected: ['handler', 'run'],
43
+ handler: 'handler.run',
44
+ },
45
+ ]
46
+
47
+ describe('splitHandlerPathAndName', () => {
48
+ tests.forEach(({ description, expected, handler }) => {
49
+ it(`should split ${description}`, () => {
50
+ const result = splitHandlerPathAndName(handler)
51
+ assert.deepEqual(result, expected)
52
+ })
53
+ })
54
+ })
@@ -0,0 +1,32 @@
1
+ import assert from 'node:assert'
2
+ import unflatten from '../unflatten.js'
3
+
4
+ describe('unflatten', () => {
5
+ it('should work with empty array parameter', () => {
6
+ const value = []
7
+ const out = unflatten(value, 2)
8
+ const expected = []
9
+
10
+ assert.deepEqual(out, expected)
11
+ })
12
+
13
+ it('should work with single pair parameter', () => {
14
+ const value = ['a', 1]
15
+ const out = unflatten(value, 2)
16
+ const expected = [['a', 1]]
17
+
18
+ assert.deepEqual(out, expected)
19
+ })
20
+
21
+ it('should work with multiple pair parameters', () => {
22
+ const value = ['a', 1, 'b', 2, 'c', 3]
23
+ const out = unflatten(value, 2)
24
+ const expected = [
25
+ ['a', 1],
26
+ ['b', 2],
27
+ ['c', 3],
28
+ ]
29
+
30
+ assert.deepEqual(out, expected)
31
+ })
32
+ })
@@ -0,0 +1,19 @@
1
+ import { execa } from 'execa'
2
+
3
+ export default async function checkDockerDaemon() {
4
+ let dockerServerOS
5
+
6
+ try {
7
+ ;({ stdout: dockerServerOS } = await execa('docker', [
8
+ 'version',
9
+ '--format',
10
+ '{{.Server.Os}}',
11
+ ]))
12
+ } catch {
13
+ throw new Error('The docker daemon is not running.')
14
+ }
15
+
16
+ if (dockerServerOS !== 'linux') {
17
+ throw new Error('Please switch docker daemon to linux mode.')
18
+ }
19
+ }