serverless-offline 8.8.1 → 9.0.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 (235) hide show
  1. package/README.md +3 -3
  2. package/package.json +33 -57
  3. package/src/ServerlessOffline.js +412 -0
  4. package/src/config/commandOptions.js +155 -0
  5. package/src/config/constants.js +22 -0
  6. package/{dist → src}/config/defaultOptions.js +8 -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 +1277 -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 +308 -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 +166 -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/resolveJoins.js +29 -0
  128. package/src/utils/splitHandlerPathAndName.js +31 -0
  129. package/src/utils/unflatten.js +11 -0
  130. package/CHANGELOG.md +0 -78
  131. package/dist/ServerlessOffline.js +0 -508
  132. package/dist/config/commandOptions.js +0 -149
  133. package/dist/config/constants.js +0 -30
  134. package/dist/config/index.js +0 -55
  135. package/dist/config/supportedRuntimes.js +0 -40
  136. package/dist/debugLog.js +0 -12
  137. package/dist/events/authCanExecuteResource.js +0 -35
  138. package/dist/events/authFunctionNameExtractor.js +0 -87
  139. package/dist/events/authMatchPolicyResource.js +0 -62
  140. package/dist/events/authValidateContext.js +0 -53
  141. package/dist/events/http/Endpoint.js +0 -173
  142. package/dist/events/http/Http.js +0 -77
  143. package/dist/events/http/HttpEventDefinition.js +0 -36
  144. package/dist/events/http/HttpServer.js +0 -1370
  145. package/dist/events/http/OfflineEndpoint.js +0 -38
  146. package/dist/events/http/authJWTSettingsExtractor.js +0 -76
  147. package/dist/events/http/createAuthScheme.js +0 -184
  148. package/dist/events/http/createJWTAuthScheme.js +0 -159
  149. package/dist/events/http/index.js +0 -15
  150. package/dist/events/http/javaHelpers.js +0 -99
  151. package/dist/events/http/lambda-events/LambdaIntegrationEvent.js +0 -87
  152. package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -246
  153. package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -225
  154. package/dist/events/http/lambda-events/VelocityContext.js +0 -170
  155. package/dist/events/http/lambda-events/index.js +0 -39
  156. package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -111
  157. package/dist/events/http/payloadSchemaValidator.js +0 -13
  158. package/dist/events/schedule/Schedule.js +0 -183
  159. package/dist/events/schedule/ScheduleEvent.js +0 -27
  160. package/dist/events/schedule/ScheduleEventDefinition.js +0 -36
  161. package/dist/events/schedule/index.js +0 -15
  162. package/dist/events/websocket/HttpServer.js +0 -114
  163. package/dist/events/websocket/WebSocket.js +0 -78
  164. package/dist/events/websocket/WebSocketClients.js +0 -577
  165. package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
  166. package/dist/events/websocket/WebSocketServer.js +0 -139
  167. package/dist/events/websocket/http-routes/_catchAll/catchAllRoute.js +0 -33
  168. package/dist/events/websocket/http-routes/_catchAll/index.js +0 -15
  169. package/dist/events/websocket/http-routes/connections/ConnectionsController.js +0 -45
  170. package/dist/events/websocket/http-routes/connections/connectionsRoutes.js +0 -95
  171. package/dist/events/websocket/http-routes/connections/index.js +0 -15
  172. package/dist/events/websocket/http-routes/index.js +0 -23
  173. package/dist/events/websocket/index.js +0 -15
  174. package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +0 -99
  175. package/dist/events/websocket/lambda-events/WebSocketConnectEvent.js +0 -101
  176. package/dist/events/websocket/lambda-events/WebSocketDisconnectEvent.js +0 -47
  177. package/dist/events/websocket/lambda-events/WebSocketEvent.js +0 -54
  178. package/dist/events/websocket/lambda-events/WebSocketRequestContext.js +0 -98
  179. package/dist/events/websocket/lambda-events/index.js +0 -39
  180. package/dist/index.js +0 -15
  181. package/dist/lambda/HttpServer.js +0 -124
  182. package/dist/lambda/Lambda.js +0 -117
  183. package/dist/lambda/LambdaContext.js +0 -53
  184. package/dist/lambda/LambdaFunction.js +0 -390
  185. package/dist/lambda/LambdaFunctionPool.js +0 -127
  186. package/dist/lambda/handler-runner/HandlerRunner.js +0 -195
  187. package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -124
  188. package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -49
  189. package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
  190. package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -515
  191. package/dist/lambda/handler-runner/docker-runner/DockerImage.js +0 -67
  192. package/dist/lambda/handler-runner/docker-runner/DockerRunner.js +0 -74
  193. package/dist/lambda/handler-runner/docker-runner/index.js +0 -15
  194. package/dist/lambda/handler-runner/go-runner/GoRunner.js +0 -230
  195. package/dist/lambda/handler-runner/go-runner/index.js +0 -15
  196. package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -228
  197. package/dist/lambda/handler-runner/in-process-runner/index.js +0 -15
  198. package/dist/lambda/handler-runner/index.js +0 -15
  199. package/dist/lambda/handler-runner/java-runner/JavaRunner.js +0 -153
  200. package/dist/lambda/handler-runner/java-runner/index.js +0 -15
  201. package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -185
  202. package/dist/lambda/handler-runner/python-runner/index.js +0 -15
  203. package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -147
  204. package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
  205. package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -92
  206. package/dist/lambda/handler-runner/worker-thread-runner/index.js +0 -15
  207. package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +0 -31
  208. package/dist/lambda/index.js +0 -15
  209. package/dist/lambda/routes/index.js +0 -23
  210. package/dist/lambda/routes/invocations/InvocationsController.js +0 -142
  211. package/dist/lambda/routes/invocations/index.js +0 -15
  212. package/dist/lambda/routes/invocations/invocationsRoute.js +0 -90
  213. package/dist/lambda/routes/invoke-async/InvokeAsyncController.js +0 -38
  214. package/dist/lambda/routes/invoke-async/index.js +0 -15
  215. package/dist/lambda/routes/invoke-async/invokeAsyncRoute.js +0 -43
  216. package/dist/main.js +0 -11
  217. package/dist/serverlessLog.js +0 -91
  218. package/dist/utils/checkDockerDaemon.js +0 -27
  219. package/dist/utils/checkGoVersion.js +0 -27
  220. package/dist/utils/createApiKey.js +0 -12
  221. package/dist/utils/createUniqueId.js +0 -14
  222. package/dist/utils/detectExecutable.js +0 -21
  223. package/dist/utils/generateHapiPath.js +0 -28
  224. package/dist/utils/getHttpApiCorsConfig.js +0 -40
  225. package/dist/utils/index.js +0 -165
  226. package/dist/utils/jsonPath.js +0 -21
  227. package/dist/utils/lowerCaseKeys.js +0 -14
  228. package/dist/utils/parseHeaders.js +0 -23
  229. package/dist/utils/parseMultiValueHeaders.js +0 -36
  230. package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
  231. package/dist/utils/parseQueryStringParameters.js +0 -26
  232. package/dist/utils/resolveJoins.js +0 -36
  233. package/dist/utils/satisfiesVersionRange.js +0 -20
  234. package/dist/utils/splitHandlerPathAndName.js +0 -37
  235. package/dist/utils/unflatten.js +0 -18
package/README.md CHANGED
@@ -89,7 +89,7 @@ plugins:
89
89
  - serverless-offline
90
90
  ```
91
91
 
92
- You can check wether you have successfully installed the plugin by running the serverless command line:
92
+ You can check whether you have successfully installed the plugin by running the serverless command line:
93
93
 
94
94
  `serverless --verbose`
95
95
 
@@ -108,7 +108,6 @@ to list all the options for the plugin run:
108
108
  All CLI options are optional:
109
109
 
110
110
  ```
111
- --allowCache Allows the code of lambda functions to cache if supported.
112
111
  --apiKey Defines the API key value to be used for endpoints marked as private Defaults to a random hash.
113
112
  --corsAllowHeaders Used as default Access-Control-Allow-Headers header value for responses. Delimit multiple values with commas. Default: 'accept,content-type,x-api-key'
114
113
  --corsAllowOrigin Used as default Access-Control-Allow-Origin header value for responses. Delimit multiple values with commas. Default: '*'
@@ -134,10 +133,11 @@ All CLI options are optional:
134
133
  --noTimeout -t Disables the timeout feature.
135
134
  --prefix -p Adds a prefix to every path, to send your requests to http://localhost:3000/[prefix]/[your_path] instead. Default: ''
136
135
  --printOutput Turns on logging of your lambda outputs in the terminal.
136
+ --reloadHandler Reloads handler with each request.
137
137
  --resourceRoutes Turns on loading of your HTTP proxy settings from serverless.yml
138
138
  --useChildProcesses Run handlers in a child process
139
139
  --useDocker Run handlers in a docker container.
140
- --useWorkerThreads Uses worker threads to run handlers.
140
+ --useInProcess Run handlers in the same process as 'serverless-offline'.
141
141
  --webSocketHardTimeout Set WebSocket hard timeout in seconds to reproduce AWS limits (https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html#apigateway-execution-service-websocket-limits-table). Default: 7200 (2 hours)
142
142
  --webSocketIdleTimeout Set WebSocket idle timeout in seconds to reproduce AWS limits (https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html#apigateway-execution-service-websocket-limits-table). Default: 600 (10 minutes)
143
143
  --websocketPort WebSocket port to listen on. Default: 3001
package/package.json CHANGED
@@ -1,25 +1,26 @@
1
1
  {
2
2
  "dedicatedTo": "Blue, a great migrating bird.",
3
3
  "name": "serverless-offline",
4
- "version": "8.8.1",
4
+ "version": "9.0.0",
5
5
  "description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
6
6
  "license": "MIT",
7
- "main": "dist/main.js",
8
- "type": "commonjs",
7
+ "main": "./src/index.js",
8
+ "exports": "./src/index.js",
9
+ "type": "module",
9
10
  "scripts": {
10
- "build": "rimraf dist && babel src --ignore \"**/__tests__/**/*\" --out-dir dist && copyfiles -u 1 \"src/**/*.{vm,py,rb}\" dist",
11
11
  "format": "eslint . --fix",
12
12
  "lint": "eslint .",
13
13
  "lint:updated": "pipe-git-updated --ext=js -- eslint",
14
14
  "list-contributors": "echo 'clone https://github.com/mgechev/github-contributors-list.git first, then run npm install' && cd ../github-contributors-list && node bin/githubcontrib --owner dherault --repo serverless-offline --sortBy contributions --showlogin true --sortOrder desc > contributors.md",
15
- "prepare": "npm run build",
15
+ "prepare": "husky install",
16
16
  "prepare-release": "standard-version && prettier --write CHANGELOG.md",
17
- "prepublishOnly": "npm run lint && npm run build",
17
+ "prepublishOnly": "npm run lint",
18
18
  "prettier-check": "prettier -c --ignore-path .gitignore \"**/*.{css,html,js,json,md,yaml,yml}\"",
19
19
  "prettier-check:updated": "pipe-git-updated --ext=css --ext=html --ext=js --ext=json --ext=md --ext=yaml --ext=yml -- prettier -c",
20
20
  "prettify": "prettier --write --ignore-path .gitignore \"**/*.{css,html,js,json,md,yaml,yml}\"",
21
21
  "prettify:updated": "pipe-git-updated --ext=css --ext=html --ext=js --ext=json --ext=md --ext=yaml --ext=yml -- prettier --write",
22
- "test": "npm run build && jest --verbose --silent --runInBand",
22
+ "test": "mocha --require ./tests/mochaHooks.cjs",
23
+ "test:jest": "npm run build && jest --verbose --silent --runInBand",
23
24
  "test:cov": "npm run build && jest --coverage --silent --runInBand --collectCoverageFrom=src/**/*.js",
24
25
  "test:log": "npm run build && jest --verbose",
25
26
  "test:noBuild": "jest --verbose --runInBand --bail",
@@ -49,7 +50,7 @@
49
50
  "websocket"
50
51
  ],
51
52
  "files": [
52
- "dist/**",
53
+ "src/**",
53
54
  "package.json",
54
55
  "LICENSE",
55
56
  "README.md"
@@ -162,21 +163,8 @@
162
163
  "Fernando Alvarez (https://github.com/jefer590)",
163
164
  "Eric Carter (https://github.com/ericctsf)"
164
165
  ],
165
- "husky": {
166
- "hooks": {
167
- "pre-commit": "lint-staged"
168
- }
169
- },
170
- "lint-staged": {
171
- "*.js": [
172
- "eslint"
173
- ],
174
- "*.{css,html,js,json,md,yaml,yml}": [
175
- "prettier -c"
176
- ]
177
- },
178
166
  "engines": {
179
- "node": ">=12.0.0"
167
+ "node": ">=14.18.0"
180
168
  },
181
169
  "standard-version": {
182
170
  "skip": {
@@ -203,58 +191,46 @@
203
191
  ]
204
192
  },
205
193
  "dependencies": {
206
- "@hapi/boom": "^9.1.4",
194
+ "@hapi/boom": "^10.0.0",
207
195
  "@hapi/h2o2": "^9.1.0",
208
196
  "@hapi/hapi": "^20.2.2",
209
- "aws-sdk": "^2.1136.0",
210
- "boxen": "^5.1.2",
211
- "chalk": "^4.1.2",
212
- "cuid": "^2.1.8",
213
- "execa": "^5.1.1",
197
+ "aws-sdk": "^2.1176.0",
198
+ "boxen": "^7.0.0",
199
+ "chalk": "^5.0.1",
200
+ "execa": "^6.1.0",
214
201
  "fs-extra": "^10.1.0",
215
202
  "java-invoke-local": "0.0.6",
216
203
  "js-string-escape": "^1.0.1",
217
- "jsonpath-plus": "^5.1.0",
218
- "jsonschema": "^1.4.0",
204
+ "jsonpath-plus": "^7.0.0",
205
+ "jsonschema": "^1.4.1",
219
206
  "jsonwebtoken": "^8.5.1",
220
- "jszip": "^3.9.1",
221
- "luxon": "^2.4.0",
222
- "node-fetch": "^2.6.7",
207
+ "jszip": "^3.10.0",
208
+ "luxon": "^3.0.1",
209
+ "node-fetch": "^3.2.8",
223
210
  "node-schedule": "^2.1.0",
224
- "p-memoize": "^4.0.4",
225
- "p-queue": "^6.6.2",
226
- "p-retry": "^4.6.2",
227
- "semver": "^7.3.7",
211
+ "object.hasown": "^1.1.1",
212
+ "p-memoize": "^7.1.0",
213
+ "p-retry": "^5.1.1",
228
214
  "velocityjs": "^2.0.6",
229
- "ws": "^7.5.7"
215
+ "ws": "^8.8.1"
230
216
  },
231
217
  "devDependencies": {
232
- "@babel/cli": "^7.17.10",
233
- "@babel/core": "^7.17.12",
234
- "@babel/plugin-proposal-class-properties": "^7.17.12",
235
- "@babel/plugin-proposal-dynamic-import": "^7.16.7",
236
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.17.12",
237
- "@babel/plugin-proposal-optional-chaining": "^7.17.12",
238
- "@babel/plugin-transform-modules-commonjs": "^7.17.12",
239
- "@babel/register": "^7.17.7",
240
218
  "archiver": "^5.3.1",
241
- "copyfiles": "^2.4.1",
242
- "eslint": "^8.15.0",
219
+ "eslint": "^8.20.0",
243
220
  "eslint-config-airbnb-base": "^15.0.0",
244
221
  "eslint-config-prettier": "^8.5.0",
245
222
  "eslint-plugin-import": "^2.25.4",
246
- "eslint-plugin-prettier": "^4.0.0",
223
+ "eslint-plugin-prettier": "^4.2.1",
247
224
  "git-list-updated": "^1.2.1",
248
- "husky": "^4.3.8",
249
- "jest": "^26.6.3",
250
- "lint-staged": "^11.2.6",
251
- "p-map": "^4.0.0",
252
- "prettier": "^2.6.2",
253
- "rimraf": "^3.0.2",
254
- "serverless": "^2.72.3",
225
+ "husky": "^8.0.1",
226
+ "lint-staged": "^13.0.3",
227
+ "mocha": "^10.0.0",
228
+ "prettier": "^2.7.1",
229
+ "serverless": "^3.21.0",
255
230
  "standard-version": "^9.5.0"
256
231
  },
257
232
  "peerDependencies": {
258
- "serverless": "^1.60.0 || 2 || 3"
233
+ "@serverless/utils": "^6.7.0",
234
+ "serverless": "^3.2.0"
259
235
  }
260
236
  }
@@ -0,0 +1,412 @@
1
+ import process, { env, exit } from 'node:process'
2
+ import { log } from '@serverless/utils/log.js'
3
+ import chalk from 'chalk'
4
+ import {
5
+ commandOptions,
6
+ CUSTOM_OPTION,
7
+ defaultOptions,
8
+ SERVER_SHUTDOWN_TIMEOUT,
9
+ } from './config/index.js'
10
+
11
+ export default class ServerlessOffline {
12
+ #cliOptions = null
13
+
14
+ #http = null
15
+
16
+ #lambda = null
17
+
18
+ #options = null
19
+
20
+ #schedule = null
21
+
22
+ #serverless = null
23
+
24
+ #webSocket = null
25
+
26
+ constructor(serverless, cliOptions) {
27
+ this.#cliOptions = cliOptions
28
+ this.#serverless = serverless
29
+
30
+ this.commands = {
31
+ offline: {
32
+ // add start nested options
33
+ commands: {
34
+ functionsUpdated: {
35
+ lifecycleEvents: ['cleanup'],
36
+ type: 'entrypoint',
37
+ },
38
+ start: {
39
+ lifecycleEvents: ['init', 'ready', 'end'],
40
+ options: commandOptions,
41
+ usage:
42
+ 'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.',
43
+ },
44
+ },
45
+ lifecycleEvents: ['start'],
46
+ options: commandOptions,
47
+ usage: 'Simulates API Gateway to call your lambda functions offline.',
48
+ },
49
+ }
50
+
51
+ this.hooks = {
52
+ 'offline:functionsUpdated:cleanup': this.#cleanupFunctions.bind(this),
53
+ 'offline:start': this.#startWithExplicitEnd.bind(this),
54
+ 'offline:start:end': this.end.bind(this),
55
+ 'offline:start:init': this.start.bind(this),
56
+ 'offline:start:ready': this.#ready.bind(this),
57
+ }
58
+ }
59
+
60
+ // Entry point for the plugin (sls offline) when running 'sls offline start'
61
+ async start() {
62
+ // Put here so available everywhere, not just in handlers
63
+ env.IS_OFFLINE = true
64
+
65
+ this.#mergeOptions()
66
+
67
+ const { httpEvents, lambdas, scheduleEvents, webSocketEvents } =
68
+ this.#getEvents()
69
+
70
+ // if (lambdas.length > 0) {
71
+ await this.#createLambda(lambdas)
72
+ // }
73
+
74
+ const eventModules = []
75
+
76
+ if (httpEvents.length > 0) {
77
+ eventModules.push(this.#createHttp(httpEvents))
78
+ }
79
+
80
+ if (!this.#options.disableScheduledEvents && scheduleEvents.length > 0) {
81
+ eventModules.push(this.#createSchedule(scheduleEvents))
82
+ }
83
+
84
+ if (webSocketEvents.length > 0) {
85
+ eventModules.push(this.#createWebSocket(webSocketEvents))
86
+ }
87
+
88
+ await Promise.all(eventModules)
89
+ }
90
+
91
+ async #ready() {
92
+ await this.#listenForTermination()
93
+ }
94
+
95
+ async end(skipExit) {
96
+ log.info('Halting offline server')
97
+
98
+ const eventModules = []
99
+
100
+ if (this.#lambda) {
101
+ eventModules.push(this.#lambda.cleanup())
102
+ eventModules.push(this.#lambda.stop(SERVER_SHUTDOWN_TIMEOUT))
103
+ }
104
+
105
+ if (this.#http) {
106
+ eventModules.push(this.#http.stop(SERVER_SHUTDOWN_TIMEOUT))
107
+ }
108
+
109
+ // if (this.#schedule) {
110
+ // eventModules.push(this.#schedule.stop())
111
+ // }
112
+
113
+ if (this.#webSocket) {
114
+ eventModules.push(this.#webSocket.stop(SERVER_SHUTDOWN_TIMEOUT))
115
+ }
116
+
117
+ await Promise.all(eventModules)
118
+
119
+ if (!skipExit) {
120
+ exit(0)
121
+ }
122
+ }
123
+
124
+ async #cleanupFunctions() {
125
+ if (this.#lambda) {
126
+ log.debug('Forcing cleanup of Lambda functions')
127
+ await this.#lambda.cleanup()
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Entry point for the plugin (serverless offline) when running 'serverless offline'
133
+ * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
134
+ * by downstream plugins. When running serverless offline that can be expected, but docs say that
135
+ * 'serverless offline start' will provide the init and end hooks for other plugins to consume
136
+ * */
137
+ async #startWithExplicitEnd() {
138
+ await this.start()
139
+ await this.#ready()
140
+ this.end()
141
+ }
142
+
143
+ async #listenForTermination() {
144
+ const command = await new Promise((resolve) => {
145
+ process
146
+ // SIGINT will be usually sent when user presses ctrl+c
147
+ .on('SIGINT', () => resolve('SIGINT'))
148
+ // SIGTERM is a default termination signal in many cases,
149
+ // for example when "killing" a subprocess spawned in node
150
+ // with child_process methods
151
+ .on('SIGTERM', () => resolve('SIGTERM'))
152
+ })
153
+
154
+ log.info(`Got ${command} signal. Offline Halting...`)
155
+ }
156
+
157
+ async #createLambda(lambdas, skipStart) {
158
+ const { default: Lambda } = await import('./lambda/index.js')
159
+
160
+ this.#lambda = new Lambda(this.#serverless, this.#options)
161
+
162
+ this.#lambda.create(lambdas)
163
+
164
+ if (!skipStart) {
165
+ await this.#lambda.start()
166
+ }
167
+ }
168
+
169
+ async #createHttp(events, skipStart) {
170
+ const { default: Http } = await import('./events/http/index.js')
171
+
172
+ this.#http = new Http(this.#serverless, this.#options, this.#lambda)
173
+
174
+ await this.#http.registerPlugins()
175
+
176
+ this.#http.create(events)
177
+
178
+ // HTTP Proxy defined in Resource
179
+ this.#http.createResourceRoutes()
180
+
181
+ // Not found handling
182
+ // we have to create the 404 routes last, otherwise we could have
183
+ // collisions with catch all routes, e.g. any (proxy+}
184
+ this.#http.create404Route()
185
+
186
+ if (!skipStart) {
187
+ await this.#http.start()
188
+ }
189
+ }
190
+
191
+ async #createSchedule(events) {
192
+ const { default: Schedule } = await import('./events/schedule/index.js')
193
+
194
+ this.#schedule = new Schedule(
195
+ this.#lambda,
196
+ this.#serverless.service.provider.region,
197
+ )
198
+
199
+ this.#schedule.create(events)
200
+ }
201
+
202
+ async #createWebSocket(events) {
203
+ const { default: WebSocket } = await import('./events/websocket/index.js')
204
+
205
+ this.#webSocket = new WebSocket(
206
+ this.#serverless,
207
+ this.#options,
208
+ this.#lambda,
209
+ )
210
+
211
+ this.#webSocket.create(events)
212
+
213
+ return this.#webSocket.start()
214
+ }
215
+
216
+ #mergeOptions() {
217
+ const {
218
+ service: { custom = {}, provider },
219
+ } = this.#serverless
220
+
221
+ const customOptions = custom[CUSTOM_OPTION]
222
+
223
+ // merge options
224
+ // order of Precedence: command line options, custom options, defaults.
225
+ this.#options = {
226
+ ...defaultOptions,
227
+ ...customOptions,
228
+ ...this.#cliOptions,
229
+ }
230
+
231
+ // Parse CORS options
232
+ this.#options.corsAllowHeaders = this.#options.corsAllowHeaders
233
+ .replace(/\s/g, '')
234
+ .split(',')
235
+ this.#options.corsAllowOrigin = this.#options.corsAllowOrigin
236
+ .replace(/\s/g, '')
237
+ .split(',')
238
+ this.#options.corsExposedHeaders = this.#options.corsExposedHeaders
239
+ .replace(/\s/g, '')
240
+ .split(',')
241
+
242
+ if (this.#options.corsDisallowCredentials) {
243
+ this.#options.corsAllowCredentials = false
244
+ }
245
+
246
+ this.#options.corsConfig = {
247
+ credentials: this.#options.corsAllowCredentials,
248
+ exposedHeaders: this.#options.corsExposedHeaders,
249
+ headers: this.#options.corsAllowHeaders,
250
+ origin: this.#options.corsAllowOrigin,
251
+ }
252
+
253
+ log.notice()
254
+ log.notice(
255
+ `Starting Offline at stage ${provider.stage} ${chalk.gray(
256
+ `(${provider.region})`,
257
+ )}`,
258
+ )
259
+ log.notice()
260
+ log.debug('options:', this.#options)
261
+ }
262
+
263
+ #getEvents() {
264
+ const { service } = this.#serverless
265
+
266
+ const httpEvents = []
267
+ const lambdas = []
268
+ const scheduleEvents = []
269
+ const webSocketEvents = []
270
+
271
+ const functionKeys = service.getAllFunctions()
272
+
273
+ let hasPrivateHttpEvent = false
274
+
275
+ functionKeys.forEach((functionKey) => {
276
+ const functionDefinition = service.getFunction(functionKey)
277
+
278
+ lambdas.push({ functionDefinition, functionKey })
279
+
280
+ const events = service.getAllEventsInFunction(functionKey) || []
281
+
282
+ events.forEach((event) => {
283
+ const { http, httpApi, schedule, websocket } = event
284
+
285
+ if ((http || httpApi) && functionDefinition.handler) {
286
+ const httpEvent = {
287
+ functionKey,
288
+ handler: functionDefinition.handler,
289
+ http: http || httpApi,
290
+ }
291
+
292
+ if (httpApi) {
293
+ // Ensure definitions for 'httpApi' events are objects so that they can be marked
294
+ // with an 'isHttpApi' property (they are handled differently to 'http' events)
295
+ if (typeof httpEvent.http === 'string') {
296
+ httpEvent.http = {
297
+ routeKey: httpEvent.http === '*' ? '$default' : httpEvent.http,
298
+ }
299
+ } else if (typeof httpEvent.http === 'object') {
300
+ if (!httpEvent.http.method) {
301
+ log.warning(
302
+ `Event definition is missing a method for function "${functionKey}"`,
303
+ )
304
+ httpEvent.http.method = ''
305
+ }
306
+ if (
307
+ httpEvent.http.method === '*' &&
308
+ httpEvent.http.path === '*'
309
+ ) {
310
+ httpEvent.http.routeKey = '$default'
311
+ } else {
312
+ const resolvedMethod =
313
+ httpEvent.http.method === '*'
314
+ ? 'ANY'
315
+ : httpEvent.http.method.toUpperCase()
316
+ httpEvent.http.routeKey = `${resolvedMethod} ${httpEvent.http.path}`
317
+ }
318
+ // Clear these properties to avoid confusion (they will be derived from the routeKey
319
+ // when needed later)
320
+ delete httpEvent.http.method
321
+ delete httpEvent.http.path
322
+ } else {
323
+ log.warning(
324
+ `Event definition must be a string or object but received ${typeof httpEvent.http} for function "${functionKey}"`,
325
+ )
326
+ httpEvent.http.routeKey = ''
327
+ }
328
+
329
+ httpEvent.http.isHttpApi = true
330
+ if (
331
+ functionDefinition.httpApi &&
332
+ functionDefinition.httpApi.payload
333
+ ) {
334
+ httpEvent.http.payload = functionDefinition.httpApi.payload
335
+ } else {
336
+ httpEvent.http.payload =
337
+ service.provider.httpApi && service.provider.httpApi.payload
338
+ ? service.provider.httpApi.payload
339
+ : '2.0'
340
+ }
341
+ }
342
+
343
+ if (http && http.private) {
344
+ hasPrivateHttpEvent = true
345
+ }
346
+
347
+ httpEvents.push(httpEvent)
348
+ }
349
+
350
+ if (schedule) {
351
+ scheduleEvents.push({
352
+ functionKey,
353
+ schedule,
354
+ })
355
+ }
356
+
357
+ if (websocket) {
358
+ webSocketEvents.push({
359
+ functionKey,
360
+ websocket,
361
+ })
362
+ }
363
+ })
364
+ })
365
+
366
+ // for simple API Key authentication model
367
+ if (hasPrivateHttpEvent) {
368
+ log.notice(`Key with token: ${this.#options.apiKey}`)
369
+
370
+ if (this.#options.noAuth) {
371
+ log.notice(
372
+ 'Authorizers are turned off. You do not need to use x-api-key header.',
373
+ )
374
+ } else {
375
+ log.notice('Remember to use x-api-key on the request headers')
376
+ }
377
+ }
378
+
379
+ return {
380
+ httpEvents,
381
+ lambdas,
382
+ scheduleEvents,
383
+ webSocketEvents,
384
+ }
385
+ }
386
+
387
+ // TODO FIXME
388
+ // TEMP quick fix to expose for testing, look for better solution
389
+ internals() {
390
+ return {
391
+ createHttp: (events, skipStart) => {
392
+ return this.#createHttp(events, skipStart)
393
+ },
394
+
395
+ createLambda: (lambdas, skipStart) => {
396
+ return this.#createLambda(lambdas, skipStart)
397
+ },
398
+
399
+ getApiGatewayServer: () => {
400
+ return this.#http.getServer()
401
+ },
402
+
403
+ getEvents: () => {
404
+ return this.#getEvents()
405
+ },
406
+
407
+ mergeOptions: () => {
408
+ this.#mergeOptions()
409
+ },
410
+ }
411
+ }
412
+ }