serverless-simple-middleware 0.0.74 → 0.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierignore CHANGED
@@ -1,3 +1,3 @@
1
- tslint.json
2
- tsconfig.json
1
+ tslint.json
2
+ tsconfig.json
3
3
  package.json
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
- # Serverless simple middleware
2
-
3
- This is just simple middleware for me to translate the interface of lambda's handler to use `request => response`.
1
+ # Serverless simple middleware
2
+
3
+ This is just simple middleware for me to translate the interface of lambda's handler to use `request => response`.
@@ -0,0 +1,5 @@
1
+ import type { APIGatewayProxyWebsocketHandlerV2 } from 'aws-lambda';
2
+ import { HandlerPluginBase } from './base';
3
+ import { WebSocketHandler, WebSocketHandlerAuxBase } from './websocketBase';
4
+ declare const buildWebSocket: <Aux extends WebSocketHandlerAuxBase>(plugins: Array<HandlerPluginBase<any>>) => (handler: WebSocketHandler<Aux>) => APIGatewayProxyWebsocketHandlerV2;
5
+ export default buildWebSocket;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ const logger_1 = require("../utils/logger");
5
+ const websocketBase_1 = require("./websocketBase");
6
+ const logger = (0, logger_1.getLogger)(__filename);
7
+ class WebSocketHandlerMiddleware {
8
+ auxPromise;
9
+ plugins;
10
+ constructor(plugins) {
11
+ this.plugins = plugins;
12
+ this.auxPromise = this.createAuxPromise();
13
+ }
14
+ createAuxPromise = () => {
15
+ return !this.plugins || this.plugins.length === 0
16
+ ? Promise.resolve({}) // tslint:disable-line
17
+ : Promise.all(this.plugins.map((plugin) => {
18
+ const maybePromise = plugin.create();
19
+ return maybePromise instanceof Promise
20
+ ? maybePromise
21
+ : Promise.resolve(maybePromise);
22
+ })).then((auxes) => auxes.reduce((all, each) => ({ ...all, ...each }), {}));
23
+ };
24
+ }
25
+ class WebSocketHandlerProxy {
26
+ request;
27
+ aux;
28
+ result;
29
+ constructor(event, context) {
30
+ logger.stupid(`WebSocket event`, event);
31
+ this.request = new websocketBase_1.WebSocketHandlerRequest(event, context);
32
+ this.aux = {}; // tslint:disable-line
33
+ this.result = { statusCode: 200 };
34
+ }
35
+ call = async (middleware, handler) => {
36
+ try {
37
+ this.aux = await middleware.auxPromise;
38
+ }
39
+ catch (error) {
40
+ logger.error(`Error while initializing plugins' aux: ${(0, utils_1.stringifyError)(error)}`);
41
+ return {
42
+ statusCode: 500,
43
+ body: JSON.stringify(error instanceof Error ? { error: error.message } : error),
44
+ };
45
+ }
46
+ const actualHandler = [this.generateHandlerDelegator(handler)];
47
+ const beginHandlers = middleware.plugins.map((plugin) => this.generatePluginDelegator(plugin.begin));
48
+ const endHandlers = middleware.plugins.map((plugin) => this.generatePluginDelegator(plugin.end));
49
+ const errorHandlers = middleware.plugins.map((plugin) => this.generatePluginDelegator(plugin.error));
50
+ const iterate = async (handlers) => Promise.all(handlers.map((each) => this.safeCall(each, errorHandlers)));
51
+ const results = [
52
+ ...(await iterate(beginHandlers)),
53
+ ...(await iterate(actualHandler)),
54
+ ...(await iterate(endHandlers)),
55
+ ].filter((x) => x);
56
+ // In test phase, throws any exception if there was.
57
+ if (process.env.NODE_ENV === 'test') {
58
+ for (const each of results) {
59
+ if (each instanceof Error) {
60
+ logger.error(`Error occurred: ${(0, utils_1.stringifyError)(each)}`);
61
+ throw each;
62
+ }
63
+ }
64
+ }
65
+ results.forEach((result) => logger.silly(`WebSocket middleware result: ${JSON.stringify(result)}`));
66
+ return this.result;
67
+ };
68
+ safeCall = async (delegator, errorHandlers) => {
69
+ try {
70
+ const result = await delegator();
71
+ return result;
72
+ }
73
+ catch (error) {
74
+ const handled = await this.handleError(error, errorHandlers);
75
+ return handled;
76
+ }
77
+ };
78
+ generateHandlerDelegator = (handler) => async () => {
79
+ const maybePromise = handler({
80
+ request: this.request,
81
+ response: undefined, // WebSocket doesn't use response
82
+ aux: this.aux,
83
+ });
84
+ const result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
85
+ logger.stupid(`WebSocket handler result`, result);
86
+ if (result) {
87
+ this.result = result;
88
+ }
89
+ return result;
90
+ };
91
+ generatePluginDelegator = (pluginCallback) => async () => {
92
+ const maybePromise = pluginCallback({
93
+ request: this.request,
94
+ response: undefined, // WebSocket doesn't use response (for HTTP plugin compatibility)
95
+ aux: this.aux,
96
+ });
97
+ const result = maybePromise instanceof Promise ? await maybePromise : maybePromise;
98
+ logger.stupid(`WebSocket plugin callback result`, result);
99
+ return result;
100
+ };
101
+ handleError = async (error, errorHandlers) => {
102
+ logger.error(error);
103
+ this.request.lastError = error;
104
+ if (errorHandlers) {
105
+ for (const handler of errorHandlers) {
106
+ try {
107
+ await handler();
108
+ }
109
+ catch (ignorable) {
110
+ logger.error(ignorable);
111
+ }
112
+ }
113
+ }
114
+ this.result = {
115
+ statusCode: 500,
116
+ body: JSON.stringify(error instanceof Error ? { error: error.message } : error),
117
+ };
118
+ return error;
119
+ };
120
+ }
121
+ const buildWebSocket = (plugins) => {
122
+ const middleware = new WebSocketHandlerMiddleware(plugins);
123
+ return (handler) => async (event, context) => {
124
+ return new WebSocketHandlerProxy(event, context).call(middleware, handler);
125
+ };
126
+ };
127
+ exports.default = buildWebSocket;
@@ -27,6 +27,7 @@ export declare const middleware: {
27
27
  body: any;
28
28
  } | void> | void) => (event: any, context: any, callback: any) => void;
29
29
  };
30
+ buildWebSocket: <Aux extends import("./websocketBase").WebSocketHandlerAuxBase>(plugins: Array<import("./base").HandlerPluginBase<any>>) => (handler: import("./websocketBase").WebSocketHandler<Aux>) => import("aws-lambda").APIGatewayProxyWebsocketHandlerV2;
30
31
  aws: (options?: import("./aws").AWSPluginOptions) => import("./aws").AWSPlugin;
31
32
  trace: (options: import("./trace").TracerPluginOptions) => import("./trace").TracerPlugin;
32
33
  logger: (options: import("./logger").LoggerPluginOptions) => import("./logger").LoggerPlugin;
@@ -38,3 +39,4 @@ export * from './database/index';
38
39
  export * from './logger';
39
40
  export * from './mysql';
40
41
  export * from './trace';
42
+ export * from './websocketBase';
@@ -16,12 +16,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.middleware = void 0;
18
18
  const build_1 = require("./build");
19
+ const buildWebSocket_1 = require("./buildWebSocket");
19
20
  const aws_1 = require("./aws");
20
21
  const logger_1 = require("./logger");
21
22
  const mysql_1 = require("./mysql");
22
23
  const trace_1 = require("./trace");
23
24
  exports.middleware = {
24
25
  build: build_1.default,
26
+ buildWebSocket: buildWebSocket_1.default,
25
27
  aws: aws_1.default,
26
28
  trace: trace_1.default,
27
29
  logger: logger_1.default,
@@ -33,3 +35,4 @@ __exportStar(require("./database/index"), exports);
33
35
  __exportStar(require("./logger"), exports);
34
36
  __exportStar(require("./mysql"), exports);
35
37
  __exportStar(require("./trace"), exports);
38
+ __exportStar(require("./websocketBase"), exports);
@@ -0,0 +1,32 @@
1
+ import type { APIGatewayProxyWebsocketEventV2, Context } from 'aws-lambda';
2
+ export interface WebSocketHandlerAuxBase {
3
+ [key: string]: any;
4
+ }
5
+ export declare class WebSocketHandlerRequest {
6
+ event: APIGatewayProxyWebsocketEventV2;
7
+ context: Context;
8
+ lastError: Error | string | undefined;
9
+ private lazyBody?;
10
+ constructor(event: APIGatewayProxyWebsocketEventV2, context: Context);
11
+ get body(): any;
12
+ get connectionId(): string;
13
+ get routeKey(): string;
14
+ get domainName(): string;
15
+ get stage(): string;
16
+ /**
17
+ * For HTTP plugin compatibility (TracerPlugin uses this).
18
+ * WebSocket events may have headers in $connect route (HTTP handshake),
19
+ * but typically don't have headers in other routes.
20
+ */
21
+ header(key: string): string | undefined;
22
+ }
23
+ export interface WebSocketHandlerResponse {
24
+ statusCode: number;
25
+ body?: string;
26
+ }
27
+ export interface WebSocketHandlerContext<A extends WebSocketHandlerAuxBase> {
28
+ request: WebSocketHandlerRequest;
29
+ response: undefined;
30
+ aux: A;
31
+ }
32
+ export type WebSocketHandler<A extends WebSocketHandlerAuxBase> = (context: WebSocketHandlerContext<A>) => WebSocketHandlerResponse | Promise<WebSocketHandlerResponse> | undefined;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocketHandlerRequest = void 0;
4
+ const logger_1 = require("../utils/logger");
5
+ const logger = (0, logger_1.getLogger)(__filename);
6
+ class WebSocketHandlerRequest {
7
+ event;
8
+ context;
9
+ lastError;
10
+ lazyBody;
11
+ constructor(event, context) {
12
+ this.event = event;
13
+ this.context = context;
14
+ this.lastError = undefined;
15
+ const ev = this.event;
16
+ if (ev.headers) {
17
+ const normalized = {};
18
+ for (const key of Object.keys(ev.headers)) {
19
+ normalized[key.toLowerCase()] = ev.headers[key];
20
+ }
21
+ ev.headers = normalized;
22
+ }
23
+ }
24
+ get body() {
25
+ if (!this.event.body) {
26
+ return {};
27
+ }
28
+ if (this.lazyBody === undefined) {
29
+ try {
30
+ this.lazyBody = JSON.parse(this.event.body);
31
+ }
32
+ catch (error) {
33
+ logger.error(`Failed to parse WebSocket body: ${error}`);
34
+ this.lazyBody = {};
35
+ }
36
+ }
37
+ return this.lazyBody || {};
38
+ }
39
+ get connectionId() {
40
+ return this.event.requestContext.connectionId;
41
+ }
42
+ get routeKey() {
43
+ return this.event.requestContext.routeKey;
44
+ }
45
+ get domainName() {
46
+ return this.event.requestContext.domainName;
47
+ }
48
+ get stage() {
49
+ return this.event.requestContext.stage;
50
+ }
51
+ // HTTP plugin compatibility methods
52
+ /**
53
+ * For HTTP plugin compatibility (TracerPlugin uses this).
54
+ * WebSocket events may have headers in $connect route (HTTP handshake),
55
+ * but typically don't have headers in other routes.
56
+ */
57
+ header(key) {
58
+ const event = this.event;
59
+ if (event.headers) {
60
+ return event.headers[key.toLowerCase()];
61
+ }
62
+ return undefined;
63
+ }
64
+ }
65
+ exports.WebSocketHandlerRequest = WebSocketHandlerRequest;
package/jest.config.js CHANGED
@@ -1,7 +1,7 @@
1
- module.exports = {
2
- transform: {
3
- '^.+\\.tsx?$': 'ts-jest',
4
- },
5
- testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
6
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
7
- };
1
+ module.exports = {
2
+ transform: {
3
+ '^.+\\.tsx?$': 'ts-jest',
4
+ },
5
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
6
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
7
+ };
package/package.json CHANGED
@@ -1,70 +1,70 @@
1
- {
2
- "name": "serverless-simple-middleware",
3
- "description": "Simple middleware to translate the interface of lambda's handler to request => response",
4
- "version": "0.0.74",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "author": "VoyagerX",
8
- "license": "MIT",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+ssh://git@github.com/v6x/serverless-simple-middleware.git"
12
- },
13
- "engines": {
14
- "node": ">=18"
15
- },
16
- "keywords": [
17
- "serverless"
18
- ],
19
- "bugs": {
20
- "url": "https://github.com/v6x/serverless-simple-middleware/issues"
21
- },
22
- "homepage": "https://github.com/v6x/serverless-simple-middleware#readme",
23
- "scripts": {
24
- "lint": "tslint --project tsconfig.json",
25
- "clean": "rm -rf dist && rm -rf node_modules && yarn install",
26
- "build": "tsc --project tsconfig.json",
27
- "build:watch": "tsc --project tsconfig.json --watch",
28
- "test": "jest --config jest.config.js",
29
- "test:watch": "jest --config jest.config.js --watch",
30
- "deploy": "yarn build && yarn publish"
31
- },
32
- "dependencies": {
33
- "@aws-sdk/client-dynamodb": "^3.828.0",
34
- "@aws-sdk/client-s3": "^3.828.0",
35
- "@aws-sdk/client-secrets-manager": "3.828.0",
36
- "@aws-sdk/client-sqs": "^3.828.0",
37
- "@aws-sdk/cloudfront-signer": "^3.821.0",
38
- "@aws-sdk/lib-dynamodb": "^3.828.0",
39
- "@aws-sdk/lib-storage": "^3.828.0",
40
- "@aws-sdk/s3-request-presigner": "^3.828.0",
41
- "@types/aws-lambda": "8",
42
- "cross-fetch": "^2.2.2",
43
- "kysely": "^0.28.2",
44
- "mysql2": "^3.14.1",
45
- "nanoid": "4.0.2",
46
- "simple-staging": "^0.0.12",
47
- "ts-enum-util": "^3.1.0",
48
- "uuid": "^3.3.2",
49
- "zod": "^4.3.5"
50
- },
51
- "devDependencies": {
52
- "@types/jest": "^23.3.1",
53
- "@types/node": "18",
54
- "@types/uuid": "^3.4.4",
55
- "babel-core": "6.26.0",
56
- "babel-jest": "^23.4.2",
57
- "cross-env": "^5.2.0",
58
- "jest": "^23.4.2",
59
- "prettier": "3.3.3",
60
- "ts-jest": "^23.1.3",
61
- "tslint": "^5.11.0",
62
- "tslint-config-prettier": "^1.14.0",
63
- "typescript": "5.5.3"
64
- },
65
- "prettier": {
66
- "printWidth": 80,
67
- "singleQuote": true,
68
- "trailingComma": "all"
69
- }
70
- }
1
+ {
2
+ "name": "serverless-simple-middleware",
3
+ "description": "Simple middleware to translate the interface of lambda's handler to request => response",
4
+ "version": "0.0.75",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "author": "VoyagerX",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+ssh://git@github.com/v6x/serverless-simple-middleware.git"
12
+ },
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "keywords": [
17
+ "serverless"
18
+ ],
19
+ "bugs": {
20
+ "url": "https://github.com/v6x/serverless-simple-middleware/issues"
21
+ },
22
+ "homepage": "https://github.com/v6x/serverless-simple-middleware#readme",
23
+ "scripts": {
24
+ "lint": "tslint --project tsconfig.json",
25
+ "clean": "rm -rf dist && rm -rf node_modules && yarn install",
26
+ "build": "tsc --project tsconfig.json",
27
+ "build:watch": "tsc --project tsconfig.json --watch",
28
+ "test": "jest --config jest.config.js",
29
+ "test:watch": "jest --config jest.config.js --watch",
30
+ "deploy": "yarn build && yarn publish"
31
+ },
32
+ "dependencies": {
33
+ "@aws-sdk/client-dynamodb": "^3.828.0",
34
+ "@aws-sdk/client-s3": "^3.828.0",
35
+ "@aws-sdk/client-secrets-manager": "3.828.0",
36
+ "@aws-sdk/client-sqs": "^3.828.0",
37
+ "@aws-sdk/cloudfront-signer": "^3.821.0",
38
+ "@aws-sdk/lib-dynamodb": "^3.828.0",
39
+ "@aws-sdk/lib-storage": "^3.828.0",
40
+ "@aws-sdk/s3-request-presigner": "^3.828.0",
41
+ "@types/aws-lambda": "8",
42
+ "cross-fetch": "^2.2.2",
43
+ "kysely": "^0.28.2",
44
+ "mysql2": "^3.14.1",
45
+ "nanoid": "4.0.2",
46
+ "simple-staging": "^0.0.12",
47
+ "ts-enum-util": "^3.1.0",
48
+ "uuid": "^3.3.2",
49
+ "zod": "^4.3.5"
50
+ },
51
+ "devDependencies": {
52
+ "@types/jest": "^23.3.1",
53
+ "@types/node": "18",
54
+ "@types/uuid": "^3.4.4",
55
+ "babel-core": "6.26.0",
56
+ "babel-jest": "^23.4.2",
57
+ "cross-env": "^5.2.0",
58
+ "jest": "^23.4.2",
59
+ "prettier": "3.3.3",
60
+ "ts-jest": "^23.1.3",
61
+ "tslint": "^5.11.0",
62
+ "tslint-config-prettier": "^1.14.0",
63
+ "typescript": "5.5.3"
64
+ },
65
+ "prettier": {
66
+ "printWidth": 80,
67
+ "singleQuote": true,
68
+ "trailingComma": "all"
69
+ }
70
+ }
package/src/aws/config.ts CHANGED
@@ -1,46 +1,46 @@
1
- import fetch from 'cross-fetch';
2
- import * as fs from 'fs';
3
-
4
- import { AWSComponent } from './define';
5
-
6
- export interface AWSConfig {
7
- [key: string]: string | boolean | number | undefined;
8
- }
9
-
10
- export interface AWSConfigs {
11
- [service: string]: AWSConfig;
12
- }
13
-
14
- export type AWSConfigResolver = (service: string) => AWSConfig;
15
-
16
- export type SimpleAWSConfigLoadParam = AWSConfigs | string;
17
-
18
- export class SimpleAWSConfig {
19
- private configs: AWSConfigs | undefined;
20
-
21
- public constructor(configs?: AWSConfigs) {
22
- this.configs = configs;
23
- }
24
-
25
- public get = (service: AWSComponent): AWSConfig | undefined => {
26
- return this.configs ? this.configs[service] : undefined;
27
- };
28
- }
29
-
30
- export const loadAWSConfig = (
31
- newConfigsOrUrl: SimpleAWSConfigLoadParam,
32
- ): Promise<SimpleAWSConfig> => {
33
- if (typeof newConfigsOrUrl === 'string') {
34
- if (/^http.*json$/.test(newConfigsOrUrl)) {
35
- return fetch(newConfigsOrUrl)
36
- .then((r) => r.json())
37
- .then(loadAWSConfig);
38
- } else if (/json$/.test(newConfigsOrUrl)) {
39
- return loadAWSConfig(
40
- JSON.parse(fs.readFileSync(newConfigsOrUrl, 'utf-8')),
41
- );
42
- }
43
- return loadAWSConfig(JSON.parse(newConfigsOrUrl));
44
- }
45
- return Promise.resolve(new SimpleAWSConfig(newConfigsOrUrl));
46
- };
1
+ import fetch from 'cross-fetch';
2
+ import * as fs from 'fs';
3
+
4
+ import { AWSComponent } from './define';
5
+
6
+ export interface AWSConfig {
7
+ [key: string]: string | boolean | number | undefined;
8
+ }
9
+
10
+ export interface AWSConfigs {
11
+ [service: string]: AWSConfig;
12
+ }
13
+
14
+ export type AWSConfigResolver = (service: string) => AWSConfig;
15
+
16
+ export type SimpleAWSConfigLoadParam = AWSConfigs | string;
17
+
18
+ export class SimpleAWSConfig {
19
+ private configs: AWSConfigs | undefined;
20
+
21
+ public constructor(configs?: AWSConfigs) {
22
+ this.configs = configs;
23
+ }
24
+
25
+ public get = (service: AWSComponent): AWSConfig | undefined => {
26
+ return this.configs ? this.configs[service] : undefined;
27
+ };
28
+ }
29
+
30
+ export const loadAWSConfig = (
31
+ newConfigsOrUrl: SimpleAWSConfigLoadParam,
32
+ ): Promise<SimpleAWSConfig> => {
33
+ if (typeof newConfigsOrUrl === 'string') {
34
+ if (/^http.*json$/.test(newConfigsOrUrl)) {
35
+ return fetch(newConfigsOrUrl)
36
+ .then((r) => r.json())
37
+ .then(loadAWSConfig);
38
+ } else if (/json$/.test(newConfigsOrUrl)) {
39
+ return loadAWSConfig(
40
+ JSON.parse(fs.readFileSync(newConfigsOrUrl, 'utf-8')),
41
+ );
42
+ }
43
+ return loadAWSConfig(JSON.parse(newConfigsOrUrl));
44
+ }
45
+ return Promise.resolve(new SimpleAWSConfig(newConfigsOrUrl));
46
+ };
package/src/aws/define.ts CHANGED
@@ -1,10 +1,10 @@
1
- export enum AWSComponent {
2
- s3 = 's3',
3
- sqs = 'sqs',
4
- dynamodb = 'dynamodb',
5
- }
6
-
7
- export interface SQSMessageBody<T> {
8
- handle: string;
9
- body?: T;
10
- }
1
+ export enum AWSComponent {
2
+ s3 = 's3',
3
+ sqs = 'sqs',
4
+ dynamodb = 'dynamodb',
5
+ }
6
+
7
+ export interface SQSMessageBody<T> {
8
+ handle: string;
9
+ body?: T;
10
+ }
package/src/aws/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './config';
2
- export * from './define';
3
- export * from './simple';
1
+ export * from './config';
2
+ export * from './define';
3
+ export * from './simple';