1 | # Serverless Offline
|
2 |
|
3 | <p>
|
4 | <a href="https://www.serverless.com">
|
5 | <img src="http://public.serverless.com/badges/v3.svg">
|
6 | </a>
|
7 | <a href="https://www.npmjs.com/package/serverless-offline">
|
8 | <img src="https://img.shields.io/npm/v/serverless-offline.svg?style=flat-square">
|
9 | </a>
|
10 | <a href="https://travis-ci.com/dherault/serverless-offline">
|
11 | <img src="https://img.shields.io/travis/dherault/serverless-offline/master.svg?style=flat-square">
|
12 | </a>
|
13 | <img src="https://img.shields.io/node/v/serverless-offline.svg?style=flat-square">
|
14 | <a href="https://github.com/serverless/serverless">
|
15 | <img src="https://img.shields.io/npm/dependency-version/serverless-offline/peer/serverless.svg?style=flat-square">
|
16 | </a>
|
17 | <a href="https://github.com/prettier/prettier">
|
18 | <img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square">
|
19 | </a>
|
20 | <img src="https://img.shields.io/npm/l/serverless-offline.svg?style=flat-square">
|
21 | <a href="#contributing">
|
22 | <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square">
|
23 | </a>
|
24 | <a href="https://gitter.im/serverless-offline/community">
|
25 | <img src="https://badges.gitter.im/serverless-offline.png">
|
26 | </a>
|
27 | </p>
|
28 |
|
29 | This [Serverless](https://github.com/serverless/serverless) plugin emulates [AWS λ](https://aws.amazon.com/lambda) and [API Gateway](https://aws.amazon.com/api-gateway) on your local machine to speed up your development cycles.
|
30 | To do so, it starts an HTTP server that handles the request's lifecycle like APIG does and invokes your handlers.
|
31 |
|
32 | **Features:**
|
33 |
|
34 | - [Node.js](https://nodejs.org), [Python](https://www.python.org), [Ruby](https://www.ruby-lang.org) λ runtimes.
|
35 | - Velocity templates support.
|
36 | - Lazy loading of your handler files.
|
37 | - And more: integrations, authorizers, proxies, timeouts, responseParameters, HTTPS, CORS, etc...
|
38 |
|
39 | This plugin is updated by its users, I just do maintenance and ensure that PRs are relevant to the community. In other words, if you [find a bug or want a new feature](https://github.com/dherault/serverless-offline/issues), please help us by becoming one of the [contributors](https://github.com/dherault/serverless-offline/graphs/contributors) :v: ! See the [contributing section](#contributing).
|
40 |
|
41 | ## Documentation
|
42 |
|
43 | - [Installation](#installation)
|
44 | - [Usage and command line options](#usage-and-command-line-options)
|
45 | - [Usage with `invoke`](#usage-with-invoke)
|
46 | - [The `process.env.IS_OFFLINE` variable](#the-processenvis_offline-variable)
|
47 | - [Token authorizers](#token-authorizers)
|
48 | - [Custom authorizers](#custom-authorizers)
|
49 | - [Remote authorizers](#remote-authorizers)
|
50 | - [JWT authorizers](#jwt-authorizers)
|
51 | - [Custom headers](#custom-headers)
|
52 | - [Environment variables](#environment-variables)
|
53 | - [AWS API Gateway Features](#aws-api-gateway-features)
|
54 | - [Velocity Templates](#velocity-templates)
|
55 | - [CORS](#cors)
|
56 | - [Catch-all Path Variables](#catch-all-path-variables)
|
57 | - [ANY method](#any-method)
|
58 | - [Lambda and Lambda Proxy Integrations](#lambda-and-lambda-proxy-integrations)
|
59 | - [HTTP Proxy](#http-proxy)
|
60 | - [Response parameters](#response-parameters)
|
61 | - [WebSocket](#websocket)
|
62 | - [Usage with Webpack](#usage-with-webpack)
|
63 | - [Velocity nuances](#velocity-nuances)
|
64 | - [Debug process](#debug-process)
|
65 | - [Resource permissions and AWS profile](#resource-permissions-and-aws-profile)
|
66 | - [Scoped execution](#scoped-execution)
|
67 | - [Simulation quality](#simulation-quality)
|
68 | - [Usage with serverless-dynamodb-local and serverless-webpack plugin](#usage-with-serverless-dynamodb-local-and-serverless-webpack-plugin)
|
69 | - [Credits and inspiration](#credits-and-inspiration)
|
70 | - [License](#license)
|
71 | - [Contributing](#contributing)
|
72 | - [Contributors](#contributors)
|
73 |
|
74 | ## Installation
|
75 |
|
76 | First, add Serverless Offline to your project:
|
77 |
|
78 | `npm install serverless-offline --save-dev`
|
79 |
|
80 | Then inside your project's `serverless.yml` file add following entry to the plugins section: `serverless-offline`. If there is no plugin section you will need to add it to the file.
|
81 |
|
82 | It should look something like this:
|
83 |
|
84 | ```YAML
|
85 | plugins:
|
86 | - serverless-offline
|
87 | ```
|
88 |
|
89 | You can check wether you have successfully installed the plugin by running the serverless command line:
|
90 |
|
91 | `serverless --verbose`
|
92 |
|
93 | the console should display _Offline_ as one of the plugins now available in your Serverless project.
|
94 |
|
95 | ## Usage and command line options
|
96 |
|
97 | In your project root run:
|
98 |
|
99 | `serverless offline` or `sls offline`.
|
100 |
|
101 | to list all the options for the plugin run:
|
102 |
|
103 | `sls offline --help`
|
104 |
|
105 | All CLI options are optional:
|
106 |
|
107 | ```
|
108 | --apiKey Defines the API key value to be used for endpoints marked as private Defaults to a random hash.
|
109 | --corsAllowHeaders Used as default Access-Control-Allow-Headers header value for responses. Delimit multiple values with commas. Default: 'accept,content-type,x-api-key'
|
110 | --corsAllowOrigin Used as default Access-Control-Allow-Origin header value for responses. Delimit multiple values with commas. Default: '*'
|
111 | --corsDisallowCredentials When provided, the default Access-Control-Allow-Credentials header value will be passed as 'false'. Default: true
|
112 | --corsExposedHeaders Used as additional Access-Control-Exposed-Headers header value for responses. Delimit multiple values with commas. Default: 'WWW-Authenticate,Server-Authorization'
|
113 | --disableCookieValidation Used to disable cookie-validation on hapi.js-server
|
114 | --enforceSecureCookies Enforce secure cookies
|
115 | --hideStackTraces Hide the stack trace on lambda failure. Default: false
|
116 | --host -o Host name to listen on. Default: localhost
|
117 | --httpPort Http port to listen on. Default: 3000
|
118 | --httpsProtocol -H To enable HTTPS, specify directory (relative to your cwd, typically your project dir) for both cert.pem and key.pem files
|
119 | --ignoreJWTSignature When using HttpApi with a JWT authorizer, don't check the signature of the JWT token. This should only be used for local development.
|
120 | --lambdaPort Lambda http port to listen on. Default: 3002
|
121 | --noPrependStageInUrl Don't prepend http routes with the stage.
|
122 | --noAuth Turns off all authorizers
|
123 | --noTimeout -t Disables the timeout feature.
|
124 | --prefix -p Adds a prefix to every path, to send your requests to http://localhost:3000/[prefix]/[your_path] instead. Default: ''
|
125 | --printOutput Turns on logging of your lambda outputs in the terminal.
|
126 | --resourceRoutes Turns on loading of your HTTP proxy settings from serverless.yml
|
127 | --useChildProcesses Run handlers in a child process
|
128 | --useWorkerThreads Uses worker threads for handlers. Requires node.js v11.7.0 or higher
|
129 | --websocketPort WebSocket port to listen on. Default: 3001
|
130 | --useDocker Run handlers in a docker container.
|
131 | ```
|
132 |
|
133 | Any of the CLI options can be added to your `serverless.yml`. For example:
|
134 |
|
135 | ```
|
136 | custom:
|
137 | serverless-offline:
|
138 | httpsProtocol: "dev-certs"
|
139 | httpPort: 4000
|
140 | stageVariables:
|
141 | foo: "bar"
|
142 | ```
|
143 |
|
144 | Options passed on the command line override YAML options.
|
145 |
|
146 | By default you can send your requests to `http://localhost:3000/`. Please note that:
|
147 |
|
148 | - You'll need to restart the plugin if you modify your `serverless.yml` or any of the default velocity template files.
|
149 | - When no Content-Type header is set on a request, API Gateway defaults to `application/json`, and so does the plugin.
|
150 | But if you send an `application/x-www-form-urlencoded` or a `multipart/form-data` body with an `application/json` (or no) Content-Type, API Gateway won't parse your data (you'll get the ugly raw as input), whereas the plugin will answer 400 (malformed JSON).
|
151 | Please consider explicitly setting your requests' Content-Type and using separate templates.
|
152 |
|
153 | ## Usage with `invoke`
|
154 |
|
155 | To use `Lambda.invoke` you need to set the lambda endpoint to the serverless-offline endpoint:
|
156 |
|
157 | ```js
|
158 | const { Lambda } = require('aws-sdk')
|
159 |
|
160 | const lambda = new Lambda({
|
161 | apiVersion: '2015-03-31',
|
162 | // endpoint needs to be set only if it deviates from the default, e.g. in a dev environment
|
163 | // process.env.SOME_VARIABLE could be set in e.g. serverless.yml for provider.environment or function.environment
|
164 | endpoint: process.env.SOME_VARIABLE
|
165 | ? 'http://localhost:3002'
|
166 | : 'https://lambda.us-east-1.amazonaws.com',
|
167 | })
|
168 | ```
|
169 |
|
170 | All your lambdas can then be invoked in a handler using
|
171 |
|
172 | ```js
|
173 | exports.handler = async function() {
|
174 | const params = {
|
175 | // FunctionName is composed of: service name - stage - function name, e.g.
|
176 | FunctionName: 'myServiceName-dev-invokedHandler',
|
177 | InvocationType: 'RequestResponse',
|
178 | Payload: JSON.stringify({ data: 'foo' }),
|
179 | }
|
180 |
|
181 | const response = await lambda.invoke(params).promise()
|
182 | }
|
183 | ```
|
184 |
|
185 | You can also invoke using the aws cli by specifying `--endpoint-url`
|
186 |
|
187 | ```
|
188 | aws lambda invoke /dev/null \
|
189 | --endpoint-url http://localhost:3002 \
|
190 | --function-name myServiceName-dev-invokedHandler
|
191 | ```
|
192 |
|
193 | ## The `process.env.IS_OFFLINE` variable
|
194 |
|
195 | Will be `"true"` in your handlers and thorough the plugin.
|
196 |
|
197 | ## Token authorizers
|
198 |
|
199 | As defined in the [Serverless Documentation](https://serverless.com/framework/docs/providers/aws/events/apigateway/#setting-api-keys-for-your-rest-api) you can use API Keys as a simple authentication method.
|
200 |
|
201 | Serverless-offline will emulate the behaviour of APIG and create a random token that's printed on the screen. With this token you can access your private methods adding `x-api-key: generatedToken` to your request header. All api keys will share the same token. To specify a custom token use the `--apiKey` cli option.
|
202 |
|
203 | ## Custom authorizers
|
204 |
|
205 | Only [custom authorizers](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) are supported. Custom authorizers are executed before a Lambda function is executed and return an Error or a Policy document.
|
206 |
|
207 | The Custom authorizer is passed an `event` object as below:
|
208 |
|
209 | ```javascript
|
210 | {
|
211 | "type": "TOKEN",
|
212 | "authorizationToken": "<Incoming bearer token>",
|
213 | "methodArn": "arn:aws:execute-api:<Region id>:<Account id>:<API id>/<Stage>/<Method>/<Resource path>"
|
214 | }
|
215 | ```
|
216 |
|
217 | The `methodArn` does not include the Account id or API id.
|
218 |
|
219 | The plugin only supports retrieving Tokens from headers. You can configure the header as below:
|
220 |
|
221 | ```javascript
|
222 | "authorizer": {
|
223 | "type": "TOKEN",
|
224 | "identitySource": "method.request.header.Authorization", // or method.request.header.SomeOtherHeader
|
225 | "authorizerResultTtlInSeconds": "0"
|
226 | }
|
227 | ```
|
228 |
|
229 | ## Remote authorizers
|
230 |
|
231 | You are able to mock the response from remote authorizers by setting the environmental variable `AUTHORIZER` before running `sls offline start`
|
232 |
|
233 | Example:
|
234 |
|
235 | > Unix: `export AUTHORIZER='{"principalId": "123"}'`
|
236 |
|
237 | > Windows: `SET AUTHORIZER='{"principalId": "123"}'`
|
238 |
|
239 | ## JWT authorizers
|
240 |
|
241 | For HTTP APIs, [JWT authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html)
|
242 | defined in the `serverless.yml` can be used to validate the token and scopes in the token. However at this time,
|
243 | the signature of the JWT is not validated with the defined issuer. Since this is a security risk, this feature is
|
244 | only enabled with the `--ignoreJWTSignature` flag. Make sure to only set this flag for local development work.
|
245 |
|
246 | ## Custom headers
|
247 |
|
248 | You are able to use some custom headers in your request to gain more control over the requestContext object.
|
249 |
|
250 | | Header | Event key |
|
251 | | ------------------------------- | ----------------------------------------------------------- |
|
252 | | cognito-identity-id | event.requestContext.identity.cognitoIdentityId |
|
253 | | cognito-authentication-provider | event.requestContext.identity.cognitoAuthenticationProvider |
|
254 |
|
255 | By doing this you are now able to change those values using a custom header. This can help you with easier authentication or retrieving the userId from a `cognitoAuthenticationProvider` value.
|
256 |
|
257 | ## Environment variables
|
258 |
|
259 | You are able to use environment variables to customize identity params in event context.
|
260 |
|
261 | | Environment Variable | Event key |
|
262 | | ----------------------------------- | ----------------------------------------------------------- |
|
263 | | SLS_COGNITO_IDENTITY_POOL_ID | event.requestContext.identity.cognitoIdentityPoolId |
|
264 | | SLS_ACCOUNT_ID | event.requestContext.identity.accountId |
|
265 | | SLS_COGNITO_IDENTITY_ID | event.requestContext.identity.cognitoIdentityId |
|
266 | | SLS_CALLER | event.requestContext.identity.caller |
|
267 | | SLS_API_KEY | event.requestContext.identity.apiKey |
|
268 | | SLS_COGNITO_AUTHENTICATION_TYPE | event.requestContext.identity.cognitoAuthenticationType |
|
269 | | SLS_COGNITO_AUTHENTICATION_PROVIDER | event.requestContext.identity.cognitoAuthenticationProvider |
|
270 |
|
271 | You can use [serverless-dotenv-plugin](https://github.com/colynb/serverless-dotenv-plugin) to load environment variables from your `.env` file.
|
272 |
|
273 | ## AWS API Gateway Features
|
274 |
|
275 | ### Velocity Templates
|
276 |
|
277 | [Serverless doc](https://serverless.com/framework/docs/providers/aws/events/apigateway#request-templates)
|
278 | ~ [AWS doc](http://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html#models-mappings-mappings)
|
279 |
|
280 | You can supply response and request templates for each function. This is optional. To do so you will have to place function specific template files in the same directory as your function file and add the .req.vm extension to the template filename.
|
281 | For example,
|
282 | if your function is in code-file: `helloworld.js`,
|
283 | your response template should be in file: `helloworld.res.vm` and your request template in file `helloworld.req.vm`.
|
284 |
|
285 | ### CORS
|
286 |
|
287 | [Serverless doc](https://serverless.com/framework/docs/providers/aws/events/apigateway#enabling-cors)
|
288 |
|
289 | If the endpoint config has CORS set to true, the plugin will use the CLI CORS options for the associated route.
|
290 | Otherwise, no CORS headers will be added.
|
291 |
|
292 | ### Catch-all Path Variables
|
293 |
|
294 | [AWS doc](https://aws.amazon.com/blogs/aws/api-gateway-update-new-features-simplify-api-development/)
|
295 |
|
296 | Set greedy paths like `/store/{proxy+}` that will intercept requests made to `/store/list-products`, `/store/add-product`, etc...
|
297 |
|
298 | ### ANY method
|
299 |
|
300 | [AWS doc](https://aws.amazon.com/blogs/aws/api-gateway-update-new-features-simplify-api-development/)
|
301 |
|
302 | Works out of the box.
|
303 |
|
304 | ### Lambda and Lambda Proxy Integrations
|
305 |
|
306 | [Serverless doc](https://serverless.com/framework/docs/providers/aws/events/apigateway#lambda-proxy-integration)
|
307 | ~ [AWS doc](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html)
|
308 |
|
309 | Works out of the box. See examples in the manual_test directory.
|
310 |
|
311 | ### HTTP Proxy
|
312 |
|
313 | [Serverless doc](https://serverless.com/framework/docs/providers/aws/events/apigateway#setting-an-http-proxy-on-api-gateway)
|
314 | ~
|
315 | [AWS doc - AWS::ApiGateway::Method](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html)
|
316 | ~
|
317 | [AWS doc - AWS::ApiGateway::Resource](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-resource.html)
|
318 |
|
319 | Example of enabling proxy:
|
320 |
|
321 | ```
|
322 | custom:
|
323 | serverless-offline:
|
324 | resourceRoutes: true
|
325 | ```
|
326 |
|
327 | or
|
328 |
|
329 | ```
|
330 | YourCloudFormationMethodId:
|
331 | Type: AWS::ApiGateway::Method
|
332 | Properties:
|
333 | ......
|
334 | Integration:
|
335 | Type: HTTP_PROXY
|
336 | Uri: 'https://s3-${self:custom.region}.amazonaws.com/${self:custom.yourBucketName}/{proxy}'
|
337 | ......
|
338 | ```
|
339 |
|
340 | ```
|
341 | custom:
|
342 | serverless-offline:
|
343 | resourceRoutes:
|
344 | YourCloudFormationMethodId:
|
345 | Uri: 'http://localhost:3001/assets/{proxy}'
|
346 | ```
|
347 |
|
348 | ### Response parameters
|
349 |
|
350 | [AWS doc](http://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html#mapping-response-parameters)
|
351 |
|
352 | You can set your response's headers using ResponseParameters.
|
353 |
|
354 | May not work properly. Please PR. (Difficulty: hard?)
|
355 |
|
356 | Example response velocity template:
|
357 |
|
358 | ```javascript
|
359 | "responseParameters": {
|
360 | "method.response.header.X-Powered-By": "Serverless", // a string
|
361 | "method.response.header.Warning": "integration.response.body", // the whole response
|
362 | "method.response.header.Location": "integration.response.body.some.key" // a pseudo JSON-path
|
363 | },
|
364 | ```
|
365 |
|
366 | ## WebSocket
|
367 |
|
368 | Usage in order to send messages back to clients:
|
369 |
|
370 | `POST http://localhost:3001/@connections/{connectionId}`
|
371 |
|
372 | Or,
|
373 |
|
374 | ```js
|
375 | const apiGatewayManagementApi = new AWS.ApiGatewayManagementApi({
|
376 | apiVersion: '2018-11-29',
|
377 | endpoint: 'http://localhost:3001',
|
378 | });
|
379 |
|
380 | apiGatewayManagementApi.postToConnection({
|
381 | ConnectionId: ...,
|
382 | Data: ...,
|
383 | });
|
384 | ```
|
385 |
|
386 | Where the `event` is received in the lambda handler function.
|
387 |
|
388 | There's support for [websocketsApiRouteSelectionExpression](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html) in it's basic form: `$request.body.x.y.z`, where the default value is `$request.body.action`.
|
389 |
|
390 | Authorizers and wss:// are currently not supported.
|
391 |
|
392 | ## Usage with Webpack
|
393 |
|
394 | Use [serverless-webpack](https://github.com/serverless-heaven/serverless-webpack) to compile and bundle your ES-next code
|
395 |
|
396 | ## Velocity nuances
|
397 |
|
398 | Consider this requestTemplate for a POST endpoint:
|
399 |
|
400 | ```json
|
401 | "application/json": {
|
402 | "payload": "$input.json('$')",
|
403 | "id_json": "$input.json('$.id')",
|
404 | "id_path": "$input.path('$').id"
|
405 | }
|
406 | ```
|
407 |
|
408 | Now let's make a request with this body: `{ "id": 1 }`
|
409 |
|
410 | AWS parses the event as such:
|
411 |
|
412 | ```javascript
|
413 | {
|
414 | "payload": {
|
415 | "id": 1
|
416 | },
|
417 | "id_json": 1,
|
418 | "id_path": "1" // Notice the string
|
419 | }
|
420 | ```
|
421 |
|
422 | Whereas Offline parses:
|
423 |
|
424 | ```javascript
|
425 | {
|
426 | "payload": {
|
427 | "id": 1
|
428 | },
|
429 | "id_json": 1,
|
430 | "id_path": 1 // Notice the number
|
431 | }
|
432 | ```
|
433 |
|
434 | Accessing an attribute after using `$input.path` will return a string on AWS (expect strings like `"1"` or `"true"`) but not with Offline (`1` or `true`).
|
435 | You may find other differences.
|
436 |
|
437 | ## Debug process
|
438 |
|
439 | Serverless offline plugin will respond to the overall framework settings and output additional information to the console in debug mode. In order to do this you will have to set the `SLS_DEBUG` environmental variable. You can run the following in the command line to switch to debug mode execution.
|
440 |
|
441 | > Unix: `export SLS_DEBUG=*`
|
442 |
|
443 | > Windows: `SET SLS_DEBUG=*`
|
444 |
|
445 | Interactive debugging is also possible for your project if you have installed the node-inspector module and chrome browser. You can then run the following command line inside your project's root.
|
446 |
|
447 | Initial installation:
|
448 | `npm install -g node-inspector`
|
449 |
|
450 | For each debug run:
|
451 | `node-debug sls offline`
|
452 |
|
453 | The system will start in wait status. This will also automatically start the chrome browser and wait for you to set breakpoints for inspection. Set the breakpoints as needed and, then, click the play button for the debugging to continue.
|
454 |
|
455 | Depending on the breakpoint, you may need to call the URL path for your function in seperate browser window for your serverless function to be run and made available for debugging.
|
456 |
|
457 | ## Resource permissions and AWS profile
|
458 |
|
459 | Lambda functions assume an IAM role during execution: the framework creates this role and set all the permission provided in the `iamRoleStatements` section of `serverless.yml`.
|
460 |
|
461 | However, serverless offline makes use of your local AWS profile credentials to run the lambda functions and that might result in a different set of permissions. By default, the aws-sdk would load credentials for you default AWS profile specified in your configuration file.
|
462 |
|
463 | You can change this profile directly in the code or by setting proper environment variables. Setting the `AWS_PROFILE` environment variable before calling `serverless` offline to a different profile would effectively change the credentials, e.g.
|
464 |
|
465 | `AWS_PROFILE=<profile> serverless offline`
|
466 |
|
467 | ## Scoped execution
|
468 |
|
469 | Downstream plugins may tie into the `before:offline:start:end` hook to release resources when the server is shutting down.
|
470 |
|
471 | ## Simulation quality
|
472 |
|
473 | This plugin simulates API Gateway for many practical purposes, good enough for development - but is not a perfect simulator.
|
474 | Specifically, Lambda currently runs on Node.js v10.x and v12.x ([AWS Docs](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)), whereas _Offline_ runs on your own runtime where no memory limits are enforced.
|
475 |
|
476 | ## Usage with serverless-dynamodb-local and serverless-webpack plugin
|
477 |
|
478 | Run `serverless offline start`. In comparison with `serverless offline`, the `start` command will fire an `init` and a `end` lifecycle hook which is needed for serverless-offline and serverless-dynamodb-local to switch off resources.
|
479 |
|
480 | Add plugins to your `serverless.yml` file:
|
481 |
|
482 | ```yaml
|
483 | plugins:
|
484 | - serverless-webpack
|
485 | - serverless-dynamodb-local
|
486 | - serverless-offline # serverless-offline needs to be last in the list
|
487 | ```
|
488 |
|
489 | ## Credits and inspiration
|
490 |
|
491 | This plugin was initially a fork of [Nopik](https://github.com/Nopik/)'s [Serverless-serve](https://github.com/Nopik/serverless-serve).
|
492 |
|
493 | ## License
|
494 |
|
495 | MIT
|
496 |
|
497 | ## Contributing
|
498 |
|
499 | Yes, thank you!
|
500 | This plugin is community-driven, most of its features are from different authors.
|
501 | Please update the docs and tests and add your name to the package.json file.
|
502 | We try to follow [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript).
|
503 |
|
504 | ## Contributors
|
505 |
|
506 | [<img alt="dnalborczyk" src="https://avatars1.githubusercontent.com/u/2903325?v=4&s=117" width="117">](https://github.com/dnalborczyk) |[<img alt="dherault" src="https://avatars2.githubusercontent.com/u/4154003?v=4&s=117" width="117">](https://github.com/dherault) |[<img alt="computerpunc" src="https://avatars3.githubusercontent.com/u/721008?v=4&s=117" width="117">](https://github.com/computerpunc) |[<img alt="leonardoalifraco" src="https://avatars0.githubusercontent.com/u/2942943?v=4&s=117" width="117">](https://github.com/leonardoalifraco) |[<img alt="daniel-cottone" src="https://avatars3.githubusercontent.com/u/26556340?v=4&s=117" width="117">](https://github.com/daniel-cottone) |
|
507 | :---: |:---: |:---: |:---: |:---: |
|
508 | [dnalborczyk](https://github.com/dnalborczyk) |[dherault](https://github.com/dherault) |[computerpunc](https://github.com/computerpunc) |[leonardoalifraco](https://github.com/leonardoalifraco) |[daniel-cottone](https://github.com/daniel-cottone) |
|
509 |
|
510 | [<img alt="mikestaub" src="https://avatars2.githubusercontent.com/u/1254558?v=4&s=117" width="117">](https://github.com/mikestaub) |[<img alt="Bilal-S" src="https://avatars0.githubusercontent.com/u/668901?v=4&s=117" width="117">](https://github.com/Bilal-S) |[<img alt="dl748" src="https://avatars1.githubusercontent.com/u/4815868?v=4&s=117" width="117">](https://github.com/dl748) |[<img alt="frsechet" src="https://avatars3.githubusercontent.com/u/7351940?v=4&s=117" width="117">](https://github.com/frsechet) |[<img alt="zoellner" src="https://avatars2.githubusercontent.com/u/2665319?v=4&s=117" width="117">](https://github.com/zoellner) |
|
511 | :---: |:---: |:---: |:---: |:---: |
|
512 | [mikestaub](https://github.com/mikestaub) |[Bilal-S](https://github.com/Bilal-S) |[dl748](https://github.com/dl748) |[frsechet](https://github.com/frsechet) |[zoellner](https://github.com/zoellner) |
|
513 |
|
514 | [<img alt="johncmckim" src="https://avatars2.githubusercontent.com/u/1297227?v=4&s=117" width="117">](https://github.com/johncmckim) |[<img alt="ThisIsNoZaku" src="https://avatars1.githubusercontent.com/u/4680766?v=4&s=117" width="117">](https://github.com/ThisIsNoZaku) |[<img alt="darthtrevino" src="https://avatars0.githubusercontent.com/u/113544?v=4&s=117" width="117">](https://github.com/darthtrevino) |[<img alt="miltador" src="https://avatars3.githubusercontent.com/u/17062283?v=4&s=117" width="117">](https://github.com/miltador) |[<img alt="gertjvr" src="https://avatars0.githubusercontent.com/u/1691062?v=4&s=117" width="117">](https://github.com/gertjvr) |
|
515 | :---: |:---: |:---: |:---: |:---: |
|
516 | [johncmckim](https://github.com/johncmckim) |[ThisIsNoZaku](https://github.com/ThisIsNoZaku) |[darthtrevino](https://github.com/darthtrevino) |[miltador](https://github.com/miltador) |[gertjvr](https://github.com/gertjvr) |
|
517 |
|
518 | [<img alt="juanjoDiaz" src="https://avatars0.githubusercontent.com/u/3322485?v=4&s=117" width="117">](https://github.com/juanjoDiaz) |[<img alt="perkyguy" src="https://avatars3.githubusercontent.com/u/4624648?v=4&s=117" width="117">](https://github.com/perkyguy) |[<img alt="jack-seek" src="https://avatars1.githubusercontent.com/u/19676584?v=4&s=117" width="117">](https://github.com/jack-seek) |[<img alt="hueniverse" src="https://avatars2.githubusercontent.com/u/56631?v=4&s=117" width="117">](https://github.com/hueniverse) |[<img alt="robbtraister" src="https://avatars3.githubusercontent.com/u/5815296?v=4&s=117" width="117">](https://github.com/robbtraister) |
|
519 | :---: |:---: |:---: |:---: |:---: |
|
520 | [juanjoDiaz](https://github.com/juanjoDiaz) |[perkyguy](https://github.com/perkyguy) |[jack-seek](https://github.com/jack-seek) |[hueniverse](https://github.com/hueniverse) |[robbtraister](https://github.com/robbtraister) |
|
521 |
|
522 | [<img alt="dortega3000" src="https://avatars1.githubusercontent.com/u/6676525?v=4&s=117" width="117">](https://github.com/dortega3000) |[<img alt="ansraliant" src="https://avatars1.githubusercontent.com/u/7121475?v=4&s=117" width="117">](https://github.com/ansraliant) |[<img alt="joubertredrat" src="https://avatars2.githubusercontent.com/u/1520407?v=4&s=117" width="117">](https://github.com/joubertredrat) |[<img alt="andreipopovici" src="https://avatars0.githubusercontent.com/u/1143417?v=4&s=117" width="117">](https://github.com/andreipopovici) |[<img alt="Andorbal" src="https://avatars1.githubusercontent.com/u/579839?v=4&s=117" width="117">](https://github.com/Andorbal) |
|
523 | :---: |:---: |:---: |:---: |:---: |
|
524 | [dortega3000](https://github.com/dortega3000) |[ansraliant](https://github.com/ansraliant) |[joubertredrat](https://github.com/joubertredrat) |[andreipopovici](https://github.com/andreipopovici) |[Andorbal](https://github.com/Andorbal) |
|
525 |
|
526 | [<img alt="AyushG3112" src="https://avatars0.githubusercontent.com/u/21307931?v=4&s=117" width="117">](https://github.com/AyushG3112) |[<img alt="franciscocpg" src="https://avatars1.githubusercontent.com/u/3680556?v=4&s=117" width="117">](https://github.com/franciscocpg) |[<img alt="kajwiklund" src="https://avatars2.githubusercontent.com/u/6842806?v=4&s=117" width="117">](https://github.com/kajwiklund) |[<img alt="ondrowan" src="https://avatars2.githubusercontent.com/u/423776?v=4&s=117" width="117">](https://github.com/ondrowan) |[<img alt="sulaysumaria" src="https://avatars3.githubusercontent.com/u/20580362?v=4&s=117" width="117">](https://github.com/sulaysumaria) |
|
527 | :---: |:---: |:---: |:---: |:---: |
|
528 | [AyushG3112](https://github.com/AyushG3112) |[franciscocpg](https://github.com/franciscocpg) |[kajwiklund](https://github.com/kajwiklund) |[ondrowan](https://github.com/ondrowan) |[sulaysumaria](https://github.com/sulaysumaria) |
|
529 |
|
530 | [<img alt="jormaechea" src="https://avatars3.githubusercontent.com/u/5612500?v=4&s=117" width="117">](https://github.com/jormaechea) |[<img alt="awwong1" src="https://avatars0.githubusercontent.com/u/2760111?v=4&s=117" width="117">](https://github.com/awwong1) |[<img alt="c24w" src="https://avatars2.githubusercontent.com/u/710406?v=4&s=117" width="117">](https://github.com/c24w) |[<img alt="vmadman" src="https://avatars1.githubusercontent.com/u/1026490?v=4&s=117" width="117">](https://github.com/vmadman) |[<img alt="encounter" src="https://avatars3.githubusercontent.com/u/549122?v=4&s=117" width="117">](https://github.com/encounter) |
|
531 | :---: |:---: |:---: |:---: |:---: |
|
532 | [jormaechea](https://github.com/jormaechea) |[awwong1](https://github.com/awwong1) |[c24w](https://github.com/c24w) |[vmadman](https://github.com/vmadman) |[encounter](https://github.com/encounter) |
|
533 |
|
534 | [<img alt="Bob-Thomas" src="https://avatars3.githubusercontent.com/u/2785213?v=4&s=117" width="117">](https://github.com/Bob-Thomas) |[<img alt="njriordan" src="https://avatars2.githubusercontent.com/u/11200170?v=4&s=117" width="117">](https://github.com/njriordan) |[<img alt="bebbi" src="https://avatars0.githubusercontent.com/u/2752391?v=4&s=117" width="117">](https://github.com/bebbi) |[<img alt="trevor-leach" src="https://avatars0.githubusercontent.com/u/39206334?v=4&s=117" width="117">](https://github.com/trevor-leach) |[<img alt="emmoistner" src="https://avatars2.githubusercontent.com/u/5419727?v=4&s=117" width="117">](https://github.com/emmoistner) |
|
535 | :---: |:---: |:---: |:---: |:---: |
|
536 | [Bob-Thomas](https://github.com/Bob-Thomas) |[njriordan](https://github.com/njriordan) |[bebbi](https://github.com/bebbi) |[trevor-leach](https://github.com/trevor-leach) |[emmoistner](https://github.com/emmoistner) |
|
537 |
|
538 | [<img alt="OrKoN" src="https://avatars3.githubusercontent.com/u/399150?v=4&s=117" width="117">](https://github.com/OrKoN) |[<img alt="adieuadieu" src="https://avatars1.githubusercontent.com/u/438848?v=4&s=117" width="117">](https://github.com/adieuadieu) |[<img alt="apalumbo" src="https://avatars0.githubusercontent.com/u/1729784?v=4&s=117" width="117">](https://github.com/apalumbo) |[<img alt="anishkny" src="https://avatars0.githubusercontent.com/u/357499?v=4&s=117" width="117">](https://github.com/anishkny) |[<img alt="cameroncooper" src="https://avatars3.githubusercontent.com/u/898689?v=4&s=117" width="117">](https://github.com/cameroncooper) |
|
539 | :---: |:---: |:---: |:---: |:---: |
|
540 | [OrKoN](https://github.com/OrKoN) |[adieuadieu](https://github.com/adieuadieu) |[apalumbo](https://github.com/apalumbo) |[anishkny](https://github.com/anishkny) |[cameroncooper](https://github.com/cameroncooper) |
|
541 |
|
542 | [<img alt="cmuto09" src="https://avatars3.githubusercontent.com/u/4679612?v=4&s=117" width="117">](https://github.com/cmuto09) |[<img alt="dschep" src="https://avatars0.githubusercontent.com/u/667763?v=4&s=117" width="117">](https://github.com/dschep) |[<img alt="DimaDK24" src="https://avatars1.githubusercontent.com/u/25607790?v=4&s=117" width="117">](https://github.com/DimaDK24) |[<img alt="dwbelliston" src="https://avatars2.githubusercontent.com/u/11450118?v=4&s=117" width="117">](https://github.com/dwbelliston) |[<img alt="eabadjiev" src="https://avatars0.githubusercontent.com/u/934059?v=4&s=117" width="117">](https://github.com/eabadjiev) |
|
543 | :---: |:---: |:---: |:---: |:---: |
|
544 | [cmuto09](https://github.com/cmuto09) |[dschep](https://github.com/dschep) |[DimaDK24](https://github.com/DimaDK24) |[dwbelliston](https://github.com/dwbelliston) |[eabadjiev](https://github.com/eabadjiev) |
|
545 |
|
546 | [<img alt="Arkfille" src="https://avatars2.githubusercontent.com/u/19840740?v=4&s=117" width="117">](https://github.com/Arkfille) |[<img alt="garunski" src="https://avatars0.githubusercontent.com/u/1002770?v=4&s=117" width="117">](https://github.com/garunski) |[<img alt="james-relyea" src="https://avatars0.githubusercontent.com/u/1944491?v=4&s=117" width="117">](https://github.com/james-relyea) |[<img alt="joewestcott" src="https://avatars0.githubusercontent.com/u/11187741?v=4&s=117" width="117">](https://github.com/joewestcott) |[<img alt="LoganArnett" src="https://avatars2.githubusercontent.com/u/8780547?v=4&s=117" width="117">](https://github.com/LoganArnett) |
|
547 | :---: |:---: |:---: |:---: |:---: |
|
548 | [Arkfille](https://github.com/Arkfille) |[garunski](https://github.com/garunski) |[james-relyea](https://github.com/james-relyea) |[joewestcott](https://github.com/joewestcott) |[LoganArnett](https://github.com/LoganArnett) |
|
549 |
|
550 | [<img alt="ablythe" src="https://avatars2.githubusercontent.com/u/6164745?v=4&s=117" width="117">](https://github.com/ablythe) |[<img alt="marccampbell" src="https://avatars3.githubusercontent.com/u/173451?v=4&s=117" width="117">](https://github.com/marccampbell) |[<img alt="purefan" src="https://avatars1.githubusercontent.com/u/315880?v=4&s=117" width="117">](https://github.com/purefan) |[<img alt="mzmiric5" src="https://avatars1.githubusercontent.com/u/1480072?v=4&s=117" width="117">](https://github.com/mzmiric5) |[<img alt="paulhbarker" src="https://avatars0.githubusercontent.com/u/7366567?v=4&s=117" width="117">](https://github.com/paulhbarker) |
|
551 | :---: |:---: |:---: |:---: |:---: |
|
552 | [ablythe](https://github.com/ablythe) |[marccampbell](https://github.com/marccampbell) |[purefan](https://github.com/purefan) |[mzmiric5](https://github.com/mzmiric5) |[paulhbarker](https://github.com/paulhbarker) |
|
553 |
|
554 | [<img alt="pmuens" src="https://avatars3.githubusercontent.com/u/1606004?v=4&s=117" width="117">](https://github.com/pmuens) |[<img alt="pierreis" src="https://avatars2.githubusercontent.com/u/203973?v=4&s=117" width="117">](https://github.com/pierreis) |[<img alt="ramonEmilio" src="https://avatars2.githubusercontent.com/u/10362370?v=4&s=117" width="117">](https://github.com/ramonEmilio) |[<img alt="rschick" src="https://avatars3.githubusercontent.com/u/423474?v=4&s=117" width="117">](https://github.com/rschick) |[<img alt="selcukcihan" src="https://avatars0.githubusercontent.com/u/7043904?v=4&s=117" width="117">](https://github.com/selcukcihan) |
|
555 | :---: |:---: |:---: |:---: |:---: |
|
556 | [pmuens](https://github.com/pmuens) |[pierreis](https://github.com/pierreis) |[ramonEmilio](https://github.com/ramonEmilio) |[rschick](https://github.com/rschick) |[selcukcihan](https://github.com/selcukcihan) |
|
557 |
|
558 | [<img alt="patrickheeney" src="https://avatars3.githubusercontent.com/u/1407228?v=4&s=117" width="117">](https://github.com/patrickheeney) |[<img alt="rma4ok" src="https://avatars1.githubusercontent.com/u/470292?v=4&s=117" width="117">](https://github.com/rma4ok) |[<img alt="clschnei" src="https://avatars3.githubusercontent.com/u/1232625?v=4&s=117" width="117">](https://github.com/clschnei) |[<img alt="djcrabhat" src="https://avatars2.githubusercontent.com/u/803042?v=4&s=117" width="117">](https://github.com/djcrabhat) |[<img alt="adam-nielsen" src="https://avatars0.githubusercontent.com/u/278772?v=4&s=117" width="117">](https://github.com/adam-nielsen) |
|
559 | :---: |:---: |:---: |:---: |:---: |
|
560 | [patrickheeney](https://github.com/patrickheeney) |[rma4ok](https://github.com/rma4ok) |[clschnei](https://github.com/clschnei) |[djcrabhat](https://github.com/djcrabhat) |[adam-nielsen](https://github.com/adam-nielsen) |
|
561 |
|
562 | [<img alt="adamelliottsweeting" src="https://avatars2.githubusercontent.com/u/8907331?v=4&s=117" width="117">](https://github.com/adamelliottsweeting) |[<img alt="againer" src="https://avatars3.githubusercontent.com/u/509709?v=4&s=117" width="117">](https://github.com/againer) |[<img alt="alebianco-doxee" src="https://avatars0.githubusercontent.com/u/56639478?v=4&s=117" width="117">](https://github.com/alebianco-doxee) |[<img alt="koterpillar" src="https://avatars0.githubusercontent.com/u/140276?v=4&s=117" width="117">](https://github.com/koterpillar) |[<img alt="triptec" src="https://avatars0.githubusercontent.com/u/240159?v=4&s=117" width="117">](https://github.com/triptec) |
|
563 | :---: |:---: |:---: |:---: |:---: |
|
564 | [adamelliottsweeting](https://github.com/adamelliottsweeting) |[againer](https://github.com/againer) |[alebianco-doxee](https://github.com/alebianco-doxee) |[koterpillar](https://github.com/koterpillar) |[triptec](https://github.com/triptec) |
|
565 |
|
566 | [<img alt="constb" src="https://avatars3.githubusercontent.com/u/1006766?v=4&s=117" width="117">](https://github.com/constb) |[<img alt="cspotcode" src="https://avatars1.githubusercontent.com/u/376504?v=4&s=117" width="117">](https://github.com/cspotcode) |[<img alt="aliatsis" src="https://avatars3.githubusercontent.com/u/4140524?v=4&s=117" width="117">](https://github.com/aliatsis) |[<img alt="arnas" src="https://avatars3.githubusercontent.com/u/13507001?v=4&s=117" width="117">](https://github.com/arnas) |[<img alt="akaila" src="https://avatars2.githubusercontent.com/u/484181?v=4&s=117" width="117">](https://github.com/akaila) |
|
567 | :---: |:---: |:---: |:---: |:---: |
|
568 | [constb](https://github.com/constb) |[cspotcode](https://github.com/cspotcode) |[aliatsis](https://github.com/aliatsis) |[arnas](https://github.com/arnas) |[akaila](https://github.com/akaila) |
|
569 |
|
570 | [<img alt="ac360" src="https://avatars1.githubusercontent.com/u/2752551?v=4&s=117" width="117">](https://github.com/ac360) |[<img alt="austin-payne" src="https://avatars3.githubusercontent.com/u/29075091?v=4&s=117" width="117">](https://github.com/austin-payne) |[<img alt="bencooling" src="https://avatars3.githubusercontent.com/u/718994?v=4&s=117" width="117">](https://github.com/bencooling) |[<img alt="BorjaMacedo" src="https://avatars1.githubusercontent.com/u/16381759?v=4&s=117" width="117">](https://github.com/BorjaMacedo) |[<img alt="BrandonE" src="https://avatars1.githubusercontent.com/u/542245?v=4&s=117" width="117">](https://github.com/BrandonE) |
|
571 | :---: |:---: |:---: |:---: |:---: |
|
572 | [ac360](https://github.com/ac360) |[austin-payne](https://github.com/austin-payne) |[bencooling](https://github.com/bencooling) |[BorjaMacedo](https://github.com/BorjaMacedo) |[BrandonE](https://github.com/BrandonE) |
|
573 |
|
574 | [<img alt="guerrerocarlos" src="https://avatars2.githubusercontent.com/u/82532?v=4&s=117" width="117">](https://github.com/guerrerocarlos) |[<img alt="chrismcleod" src="https://avatars1.githubusercontent.com/u/1134683?v=4&s=117" width="117">](https://github.com/chrismcleod) |[<img alt="christophgysin" src="https://avatars0.githubusercontent.com/u/527924?v=4&s=117" width="117">](https://github.com/christophgysin) |[<img alt="Clement134" src="https://avatars2.githubusercontent.com/u/6473775?v=4&s=117" width="117">](https://github.com/Clement134) |[<img alt="rlgod" src="https://avatars2.githubusercontent.com/u/1705096?v=4&s=117" width="117">](https://github.com/rlgod) |
|
575 | :---: |:---: |:---: |:---: |:---: |
|
576 | [guerrerocarlos](https://github.com/guerrerocarlos) |[chrismcleod](https://github.com/chrismcleod) |[christophgysin](https://github.com/christophgysin) |[Clement134](https://github.com/Clement134) |[rlgod](https://github.com/rlgod) |
|
577 |
|
578 | [<img alt="dbunker" src="https://avatars1.githubusercontent.com/u/751580?v=4&s=117" width="117">](https://github.com/dbunker) |[<img alt="dobrynin" src="https://avatars3.githubusercontent.com/u/12061016?v=4&s=117" width="117">](https://github.com/dobrynin) |[<img alt="domaslasauskas" src="https://avatars2.githubusercontent.com/u/2464675?v=4&s=117" width="117">](https://github.com/domaslasauskas) |[<img alt="enolan" src="https://avatars0.githubusercontent.com/u/61517?v=4&s=117" width="117">](https://github.com/enolan) |[<img alt="minibikini" src="https://avatars3.githubusercontent.com/u/439309?v=4&s=117" width="117">](https://github.com/minibikini) |
|
579 | :---: |:---: |:---: |:---: |:---: |
|
580 | [dbunker](https://github.com/dbunker) |[dobrynin](https://github.com/dobrynin) |[domaslasauskas](https://github.com/domaslasauskas) |[enolan](https://github.com/enolan) |[minibikini](https://github.com/minibikini) |
|
581 |
|
582 | [<img alt="em0ney" src="https://avatars0.githubusercontent.com/u/5679658?v=4&s=117" width="117">](https://github.com/em0ney) |[<img alt="erauer" src="https://avatars0.githubusercontent.com/u/792171?v=4&s=117" width="117">](https://github.com/erauer) |[<img alt="gbroques" src="https://avatars0.githubusercontent.com/u/12969835?v=4&s=117" width="117">](https://github.com/gbroques) |[<img alt="guillaume" src="https://avatars1.githubusercontent.com/u/368?v=4&s=117" width="117">](https://github.com/guillaume) |[<img alt="balassy" src="https://avatars1.githubusercontent.com/u/1872777?v=4&s=117" width="117">](https://github.com/balassy) |
|
583 | :---: |:---: |:---: |:---: |:---: |
|
584 | [em0ney](https://github.com/em0ney) |[erauer](https://github.com/erauer) |[gbroques](https://github.com/gbroques) |[guillaume](https://github.com/guillaume) |[balassy](https://github.com/balassy) |
|
585 |
|
586 | [<img alt="idmontie" src="https://avatars3.githubusercontent.com/u/412382?v=4&s=117" width="117">](https://github.com/idmontie) |[<img alt="jacintoArias" src="https://avatars0.githubusercontent.com/u/7511199?v=4&s=117" width="117">](https://github.com/jacintoArias) |[<img alt="jgrigg" src="https://avatars1.githubusercontent.com/u/12800024?v=4&s=117" width="117">](https://github.com/jgrigg) |[<img alt="jsnajdr" src="https://avatars3.githubusercontent.com/u/664258?v=4&s=117" width="117">](https://github.com/jsnajdr) |[<img alt="horyd" src="https://avatars3.githubusercontent.com/u/916414?v=4&s=117" width="117">](https://github.com/horyd) |
|
587 | :---: |:---: |:---: |:---: |:---: |
|
588 | [idmontie](https://github.com/idmontie) |[jacintoArias](https://github.com/jacintoArias) |[jgrigg](https://github.com/jgrigg) |[jsnajdr](https://github.com/jsnajdr) |[horyd](https://github.com/horyd) |
|
589 |
|
590 | [<img alt="jaydp17" src="https://avatars1.githubusercontent.com/u/1743425?v=4&s=117" width="117">](https://github.com/jaydp17) |[<img alt="jeremygiberson" src="https://avatars2.githubusercontent.com/u/487411?v=4&s=117" width="117">](https://github.com/jeremygiberson) |[<img alt="josephwarrick" src="https://avatars2.githubusercontent.com/u/5392984?v=4&s=117" width="117">](https://github.com/josephwarrick) |[<img alt="jlsjonas" src="https://avatars1.githubusercontent.com/u/932193?v=4&s=117" width="117">](https://github.com/jlsjonas) |[<img alt="joostfarla" src="https://avatars0.githubusercontent.com/u/851863?v=4&s=117" width="117">](https://github.com/joostfarla) |
|
591 | :---: |:---: |:---: |:---: |:---: |
|
592 | [jaydp17](https://github.com/jaydp17) |[jeremygiberson](https://github.com/jeremygiberson) |[josephwarrick](https://github.com/josephwarrick) |[jlsjonas](https://github.com/jlsjonas) |[joostfarla](https://github.com/joostfarla) |
|
593 |
|
594 | [<img alt="kenleytomlin" src="https://avatars3.githubusercontent.com/u/3004590?v=4&s=117" width="117">](https://github.com/kenleytomlin) |[<img alt="lalifraco-devspark" src="https://avatars1.githubusercontent.com/u/13339324?v=4&s=117" width="117">](https://github.com/lalifraco-devspark) |[<img alt="DynamicSTOP" src="https://avatars0.githubusercontent.com/u/9434504?v=4&s=117" width="117">](https://github.com/DynamicSTOP) |[<img alt="medikoo" src="https://avatars3.githubusercontent.com/u/122434?v=4&s=117" width="117">](https://github.com/medikoo) |[<img alt="neverendingqs" src="https://avatars1.githubusercontent.com/u/8854618?v=4&s=117" width="117">](https://github.com/neverendingqs) |
|
595 | :---: |:---: |:---: |:---: |:---: |
|
596 | [kenleytomlin](https://github.com/kenleytomlin) |[lalifraco-devspark](https://github.com/lalifraco-devspark) |[DynamicSTOP](https://github.com/DynamicSTOP) |[medikoo](https://github.com/medikoo) |[neverendingqs](https://github.com/neverendingqs) |
|
597 |
|
598 | [<img alt="msjonker" src="https://avatars3.githubusercontent.com/u/781683?v=4&s=117" width="117">](https://github.com/msjonker) |[<img alt="Takeno" src="https://avatars0.githubusercontent.com/u/1499063?v=4&s=117" width="117">](https://github.com/Takeno) |[<img alt="mjmac" src="https://avatars1.githubusercontent.com/u/83737?v=4&s=117" width="117">](https://github.com/mjmac) |[<img alt="ojongerius" src="https://avatars0.githubusercontent.com/u/1726055?v=4&s=117" width="117">](https://github.com/ojongerius) |[<img alt="thepont" src="https://avatars1.githubusercontent.com/u/2901992?v=4&s=117" width="117">](https://github.com/thepont) |
|
599 | :---: |:---: |:---: |:---: |:---: |
|
600 | [msjonker](https://github.com/msjonker) |[Takeno](https://github.com/Takeno) |[mjmac](https://github.com/mjmac) |[ojongerius](https://github.com/ojongerius) |[thepont](https://github.com/thepont) |
|
601 |
|
602 | [<img alt="WooDzu" src="https://avatars3.githubusercontent.com/u/2228236?v=4&s=117" width="117">](https://github.com/WooDzu) |[<img alt="PsychicCat" src="https://avatars3.githubusercontent.com/u/4073856?v=4&s=117" width="117">](https://github.com/PsychicCat) |[<img alt="Raph22" src="https://avatars0.githubusercontent.com/u/18127594?v=4&s=117" width="117">](https://github.com/Raph22) |[<img alt="wwsno" src="https://avatars0.githubusercontent.com/u/6328924?v=4&s=117" width="117">](https://github.com/wwsno) |[<img alt="gribnoysup" src="https://avatars2.githubusercontent.com/u/5036933?v=4&s=117" width="117">](https://github.com/gribnoysup) |
|
603 | :---: |:---: |:---: |:---: |:---: |
|
604 | [WooDzu](https://github.com/WooDzu) |[PsychicCat](https://github.com/PsychicCat) |[Raph22](https://github.com/Raph22) |[wwsno](https://github.com/wwsno) |[gribnoysup](https://github.com/gribnoysup) |
|
605 |
|
606 | [<img alt="starsprung" src="https://avatars3.githubusercontent.com/u/48957?v=4&s=117" width="117">](https://github.com/starsprung) |[<img alt="shineli-not-used-anymore" src="https://avatars3.githubusercontent.com/u/1043331?v=4&s=117" width="117">](https://github.com/shineli-not-used-anymore) |[<img alt="stesie" src="https://avatars1.githubusercontent.com/u/113068?v=4&s=117" width="117">](https://github.com/stesie) |[<img alt="stevemao" src="https://avatars0.githubusercontent.com/u/6316590?v=4&s=117" width="117">](https://github.com/stevemao) |[<img alt="ittus" src="https://avatars3.githubusercontent.com/u/5120965?v=4&s=117" width="117">](https://github.com/ittus) |
|
607 | :---: |:---: |:---: |:---: |:---: |
|
608 | [starsprung](https://github.com/starsprung) |[shineli-not-used-anymore](https://github.com/shineli-not-used-anymore) |[stesie](https://github.com/stesie) |[stevemao](https://github.com/stevemao) |[ittus](https://github.com/ittus) |
|
609 |
|
610 | [<img alt="tiagogoncalves89" src="https://avatars2.githubusercontent.com/u/55122?v=4&s=117" width="117">](https://github.com/tiagogoncalves89) |[<img alt="tuanmh" src="https://avatars2.githubusercontent.com/u/3193353?v=4&s=117" width="117">](https://github.com/tuanmh) |[<img alt="Gregoirevda" src="https://avatars3.githubusercontent.com/u/12223738?v=4&s=117" width="117">](https://github.com/Gregoirevda) |[<img alt="gcphost" src="https://avatars3.githubusercontent.com/u/1173636?v=4&s=117" width="117">](https://github.com/gcphost) |[<img alt="YaroslavApatiev" src="https://avatars0.githubusercontent.com/u/24372409?v=4&s=117" width="117">](https://github.com/YaroslavApatiev) |
|
611 | :---: |:---: |:---: |:---: |:---: |
|
612 | [tiagogoncalves89](https://github.com/tiagogoncalves89) |[tuanmh](https://github.com/tuanmh) |[Gregoirevda](https://github.com/Gregoirevda) |[gcphost](https://github.com/gcphost) |[YaroslavApatiev](https://github.com/YaroslavApatiev) |
|
613 |
|
614 | [<img alt="zacacollier" src="https://avatars2.githubusercontent.com/u/18710669?v=4&s=117" width="117">](https://github.com/zacacollier) |[<img alt="allenhartwig" src="https://avatars2.githubusercontent.com/u/1261521?v=4&s=117" width="117">](https://github.com/allenhartwig) |[<img alt="demetriusnunes" src="https://avatars0.githubusercontent.com/u/4699?v=4&s=117" width="117">](https://github.com/demetriusnunes) |[<img alt="hsz" src="https://avatars3.githubusercontent.com/u/108333?v=4&s=117" width="117">](https://github.com/hsz) |[<img alt="electrikdevelopment" src="https://avatars3.githubusercontent.com/u/14976795?v=4&s=117" width="117">](https://github.com/electrikdevelopment) |
|
615 | :---: |:---: |:---: |:---: |:---: |
|
616 | [zacacollier](https://github.com/zacacollier) |[allenhartwig](https://github.com/allenhartwig) |[demetriusnunes](https://github.com/demetriusnunes) |[hsz](https://github.com/hsz) |[electrikdevelopment](https://github.com/electrikdevelopment) |
|
617 |
|
618 | [<img alt="jgilbert01" src="https://avatars1.githubusercontent.com/u/1082126?v=4&s=117" width="117">](https://github.com/jgilbert01) |[<img alt="polaris340" src="https://avatars2.githubusercontent.com/u/2861192?v=4&s=117" width="117">](https://github.com/polaris340) |[<img alt="kobanyan" src="https://avatars0.githubusercontent.com/u/14950314?v=4&s=117" width="117">](https://github.com/kobanyan) |[<img alt="leruitga-ss" src="https://avatars1.githubusercontent.com/u/39830392?v=4&s=117" width="117">](https://github.com/leruitga-ss) |[<img alt="livingmine" src="https://avatars1.githubusercontent.com/u/7286614?v=4&s=117" width="117">](https://github.com/livingmine) |
|
619 | :---: |:---: |:---: |:---: |:---: |
|
620 | [jgilbert01](https://github.com/jgilbert01) |[polaris340](https://github.com/polaris340) |[kobanyan](https://github.com/kobanyan) |[leruitga-ss](https://github.com/leruitga-ss) |[livingmine](https://github.com/livingmine) |
|
621 |
|
622 | [<img alt="lteacher" src="https://avatars3.githubusercontent.com/u/6103860?v=4&s=117" width="117">](https://github.com/lteacher) |[<img alt="martinmicunda" src="https://avatars1.githubusercontent.com/u/1643606?v=4&s=117" width="117">](https://github.com/martinmicunda) |[<img alt="nori3tsu" src="https://avatars0.githubusercontent.com/u/379587?v=4&s=117" width="117">](https://github.com/nori3tsu) |[<img alt="ppasmanik" src="https://avatars0.githubusercontent.com/u/3534835?v=4&s=117" width="117">](https://github.com/ppasmanik) |[<img alt="ryanzyy" src="https://avatars1.githubusercontent.com/u/2299226?v=4&s=117" width="117">](https://github.com/ryanzyy) |
|
623 | :---: |:---: |:---: |:---: |:---: |
|
624 | [lteacher](https://github.com/lteacher) |[martinmicunda](https://github.com/martinmicunda) |[nori3tsu](https://github.com/nori3tsu) |[ppasmanik](https://github.com/ppasmanik) |[ryanzyy](https://github.com/ryanzyy) |
|
625 |
|
626 | [<img alt="m0ppers" src="https://avatars3.githubusercontent.com/u/819421?v=4&s=117" width="117">](https://github.com/m0ppers) |
|
627 | :---: |
|
628 | [m0ppers](https://github.com/m0ppers) |
|