Skip to content

Commit

Permalink
feat: added support for Lambda Stream Response
Browse files Browse the repository at this point in the history
fix: various fixes and performance improvements
  • Loading branch information
Inqnuam committed May 3, 2023
1 parent b5393c6 commit 4432972
Show file tree
Hide file tree
Showing 44 changed files with 3,097 additions and 1,195 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_modules
yarn-error.log
yarn.lock
src
!src/lib/runtime/awslambda.ts
build.mjs
TODO.md
tsconfig.json
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ To have more control over the plugin you can passe a config file via `configPath
```yaml
custom:
serverless-aws-lambda:
port: 3000
configPath: ./config.default
```

Expand All @@ -164,6 +163,7 @@ See [defineConfig](resources/defineConfig.md) for advanced configuration.
- [AWS Local S3](resources/s3.md)
- [AWS Local SNS](resources/sns.md)
- [AWS Local SQS](resources/sqs.md)
- [DocumentDB Local Streams](https://github.com/Inqnuam/serverless-aws-lambda-documentdb-streams)
- [DynamoDB Local Streams](https://github.com/Inqnuam/serverless-aws-lambda-ddb-streams)
- [Jest](https://github.com/Inqnuam/serverless-aws-lambda-jest)
- [Vitest](https://github.com/Inqnuam/serverless-aws-lambda-vitest)
14 changes: 11 additions & 3 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const compileDeclarations = () => {
console.log(error.output?.[1]?.toString());
}
};
const external = ["esbuild", "archiver", "serve-static"];
const external = ["esbuild", "archiver", "serve-static", "@aws-sdk/eventstream-codec"];
const watchPlugin = {
name: "watch-plugin",
setup: (build) => {
Expand Down Expand Up @@ -40,7 +40,7 @@ const buildIndex = bundle.bind(null, {
"./src/index.ts",
"./src/server.ts",
"./src/defineConfig.ts",
"./src/lib/runtime/worker.ts",
"./src/lib/runtime/runners/node.ts",
"./src/lambda/router.ts",
"./src/plugins/sns/index.ts",
"./src/plugins/sqs/index.ts",
Expand All @@ -51,7 +51,15 @@ const buildIndex = bundle.bind(null, {

const buildRouterESM = bundle.bind(null, {
...esBuildConfig,
entryPoints: ["./src/lambda/router.ts", "./src/server.ts", "./src/lambda/body-parser.ts"],
entryPoints: [
"./src/lambda/router.ts",
"./src/server.ts",
"./src/lambda/body-parser.ts",
"./src/defineConfig.ts",
"./src/plugins/sns/index.ts",
"./src/plugins/sqs/index.ts",
"./src/plugins/s3/index.ts",
],
format: "esm",
outExtension: { ".js": ".mjs" },
});
Expand Down
22 changes: 14 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-aws-lambda",
"version": "4.4.4",
"version": "4.5.0",
"description": "AWS Application Load Balancer and API Gateway - Lambda dev tool for Serverless. Allows Express synthax in handlers. Supports packaging, local invoking and offline ALB, APG, S3, SNS, SQS, DynamoDB Stream server mocking.",
"author": "Inqnuam",
"license": "MIT",
Expand All @@ -17,7 +17,8 @@
},
"./defineConfig": {
"types": "./dist/defineConfig.d.ts",
"require": "./dist/defineConfig.js"
"require": "./dist/defineConfig.js",
"import": "./dist/defineConfig.mjs"
},
"./router": {
"types": "./dist/lambda/router.d.ts",
Expand All @@ -36,28 +37,32 @@
},
"./sns": {
"types": "./dist/plugins/sns/index.d.ts",
"require": "./dist/plugins/sns/index.js"
"require": "./dist/plugins/sns/index.js",
"import": "./dist/plugins/sns/index.mjs"
},
"./sqs": {
"types": "./dist/plugins/sqs/index.d.ts",
"require": "./dist/plugins/sqs/index.js"
"require": "./dist/plugins/sqs/index.js",
"import": "./dist/plugins/sqs/index.mjs"
},
"./s3": {
"types": "./dist/plugins/s3/index.d.ts",
"require": "./dist/plugins/s3/index.js"
"require": "./dist/plugins/s3/index.js",
"import": "./dist/plugins/s3/index.mjs"
}
},
"dependencies": {
"@aws-sdk/eventstream-codec": "^3.310.0",
"@types/serverless": "^3.12.11",
"archiver": "^5.3.1",
"esbuild": "0.17.15",
"esbuild": "0.17.18",
"serve-static": "^1.15.0"
},
"devDependencies": {
"@types/archiver": "^5.3.1",
"@types/archiver": "^5.3.2",
"@types/node": "^14.14.31",
"@types/serve-static": "^1.15.1",
"typescript": "^4.9.5"
"typescript": "^5.0.4"
},
"keywords": [
"aws",
Expand All @@ -73,6 +78,7 @@
"s3",
"stream",
"dynamodb",
"documentdb",
"invoke",
"bundle",
"esbuild",
Expand Down
15 changes: 15 additions & 0 deletions resources/defineConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,18 @@ module.exports = defineConfig({
plugins: [myCustomPlugin],
});
```

`defineConfig` can be imported as ESM as well.

```js
import { defineConfig } from "serverless-aws-lambda/defineConfig";
import { sqsPlugin } from "serverless-aws-lambda/sqs";

export default defineConfig({
offline: {
staticPath: "./.aws_lambda",
port: 9999,
},
plugins: [sqsPlugin()],
});
```
1 change: 1 addition & 0 deletions resources/esbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ Most of esbuild options are supported. It isn't the case for example for `entryP
| mainFields | array | | |
| nodePaths | array | | |
| jsx | string | | |
| format | string | cjs | |
41 changes: 24 additions & 17 deletions resources/express.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { playersController } from "../controllers/playersController";

const route = Router();

route.handle(auth, playersController);
route.use(auth, playersController);

route.use((error, req, res, next) => {
console.log(error);
Expand All @@ -32,9 +32,7 @@ route.use((error, req, res, next) => {
export default route;
```

`route.handle` is similar to Express [app.METHOD("/somePath, ...")](https://expressjs.com/en/4x/api.html#app), a function (async or not) which accepts 3 arguments. request, response and next.

It is also possible to use `route.use` instead of `route.handle`.
`route.use` is similar to Express [app.use(...)](https://expressjs.com/en/4x/api.html#app), a function (async or not) which accepts 3-4 arguments. request, response and next.

```js
const route = Router();
Expand Down Expand Up @@ -63,28 +61,32 @@ route.use(auth).use(playersController).use(errorHandler);
or with multi argument:

```js
const route = Router();
import { Router } from "serverless-aws-lambda/router";

const handler = Router();

const errorHandler = (error, req, res, next) => {
console.log(error);
res.status(500).send("Internal Server Error");
};

route.use(auth, playersController, errorHandler);
handler.use(auth, playersController, errorHandler);

export { handler };
```

### Request

| property | type | doc | info |
| -------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| body | any | [doc](https://expressjs.com/en/4x/api.html#req.body) | Request with json content-type are automatically parsed. Use body-parser middleware from the package to parse Form Data and files |
| cookies | key-value | [doc](https://expressjs.com/en/4x/api.html#req.cookies) | compatible with Express's cookie-parser |
| method | string | [doc](https://expressjs.com/en/4x/api.html#req.method) | |
| params | string[] | As we don't handle custom routes we can't support named params, instead `params` will return an array of string containing `path` components separated by `/` (without `query` string) | Not compatible with Express |
| path | string | [doc](https://expressjs.com/en/4x/api.html#req.path) | |
| protocol | string | [doc](https://expressjs.com/en/4x/api.html#req.protocol) | |
| query | key-value | [doc](https://expressjs.com/en/4x/api.html#req.query) | |
| get | function | [doc](https://expressjs.com/en/4x/api.html#req.get) | |
| property | type | doc | info |
| -------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| body | any | [doc](https://expressjs.com/en/4x/api.html#req.body) | Request with json content-type are automatically parsed. Use body-parser middleware from `serverless-aws-lambda/body-parser` to parse Form Data and files |
| cookies | key-value | [doc](https://expressjs.com/en/4x/api.html#req.cookies) | compatible with Express's cookie-parser |
| method | string | [doc](https://expressjs.com/en/4x/api.html#req.method) | |
| params | string[] | As we don't handle custom routes we can't support named params, instead `params` will return an array of string containing `path` components separated by `/` (without `query` string) | Not compatible with Express |
| path | string | [doc](https://expressjs.com/en/4x/api.html#req.path) | |
| protocol | string | [doc](https://expressjs.com/en/4x/api.html#req.protocol) | |
| query | key-value | [doc](https://expressjs.com/en/4x/api.html#req.query) | |
| get | function | [doc](https://expressjs.com/en/4x/api.html#req.get) | |

++ includes also `event` raw object from AWS Lambda (except "`cookies`" which can be easly parsed with `cookie-parser` middleware)

Expand All @@ -110,4 +112,9 @@ route.use(auth, playersController, errorHandler);

### Next

similar to ExpressJs next function
Similar to ExpressJs next function.

`next()` can take one argument.
If an argument is provided Router triggers next middleware which has 4 arguments.
This is usally used to handle errors (see examples above).
Check Express documentation for more info.
7 changes: 6 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { BuildOptions, BuildResult } from "esbuild";
import { IncomingMessage, ServerResponse } from "http";
import type { HttpMethod } from "./lib/server/handlers";
import type { awslambda } from "./lib/runtime/awslambda";
export interface OfflineConfig {
staticPath?: string;
port?: number;
Expand All @@ -13,7 +14,7 @@ export interface OfflineConfig {
}

export interface Config {
esbuild?: Omit<BuildOptions, "entryPoints" | "outExtension" | "outfile" | "bundle" | "splitting" | "stdin" | "format" | "platforme" | "metafile">;
esbuild?: Omit<BuildOptions, "entryPoints" | "outExtension" | "outfile" | "bundle" | "splitting" | "stdin" | "platforme" | "metafile" | "format"> & { format?: "cjs" | "esm" };
offline?: OfflineConfig;
buildCallback?: (result: BuildResult, isRebuild: boolean) => Promise<void> | void;
afterDeployCallbacks?: (() => Promise<void> | void)[];
Expand All @@ -26,3 +27,7 @@ export interface ServerConfig {
port?: number;
onRebuild?: () => Promise<void> | void;
}

declare global {
const awslambda: awslambda;
}
32 changes: 21 additions & 11 deletions src/defineConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PluginBuild, BuildResult } from "esbuild";
import type { Config, OfflineConfig } from "./config";
import type { ILambdaMock } from "./lib/runtime/lambdaMock";
import type { ILambdaMock } from "./lib/runtime/rapidApi";
import type { HttpMethod } from "./lib/server/handlers";
import type { IncomingMessage, ServerResponse } from "http";
import type Serverless from "serverless";
Expand All @@ -19,13 +19,18 @@ export type ILambda = {
* Be notified when this lambda is invoked.
*/
onInvoke: (callback: (event: any, info?: any) => void) => void;
} & Omit<ILambdaMock, "invokeSub">;
onInvokeError: (callback: (input: any, error: any, info?: any) => void) => void;
onInvokeSuccess: (callback: (input: any, output: any, info?: any) => void) => void;
} & Omit<ILambdaMock, "invokeSub" | "invokeSuccessSub" | "invokeErrorSub">;

export interface ClientConfigParams {
stop: (err?: any) => Promise<void>;
lambdas: ILambda[];
isDeploying: boolean;
isPackaging: boolean;
/**
* @deprecated use `someLambda.setEnv(key, value)` instead.
*/
setEnv: (lambdaName: string, key: string, value: string) => void;
stage: string;
esbuild: PluginBuild["esbuild"];
Expand All @@ -40,6 +45,15 @@ export interface ClientConfigParams {
};
}

export interface OfflineRequest {
/**
* @default "ANY"
*/
method?: HttpMethod | HttpMethod[];
filter: string | RegExp;
callback: (this: ClientConfigParams, req: IncomingMessage, res: ServerResponse) => Promise<any | void> | any | void;
}

export interface SlsAwsLambdaPlugin {
name: string;
buildCallback?: (this: ClientConfigParams, result: BuildResult, isRebuild: boolean) => Promise<void> | void;
Expand All @@ -49,22 +63,18 @@ export interface SlsAwsLambdaPlugin {
offline?: {
onReady?: (this: ClientConfigParams, port: number, ip: string) => Promise<void> | void;
/**
* Add new requests to the offline server.
* Add new requests to the local server.
*/
request?: {
/**
* @default "ANY"
*/
method?: HttpMethod | HttpMethod[];
filter: string | RegExp;
callback: (this: ClientConfigParams, req: IncomingMessage, res: ServerResponse) => Promise<any | void> | any | void;
}[];
request?: OfflineRequest[];
};
}

export interface Options {
esbuild?: Config["esbuild"];
offline?: {
/**
* Serve files locally from provided directory
*/
staticPath?: string;
port?: number;
};
Expand Down
Loading

0 comments on commit 4432972

Please sign in to comment.