serverless-simple-middleware 0.0.73 → 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.
Files changed (45) hide show
  1. package/.prettierignore +2 -2
  2. package/README.md +3 -3
  3. package/dist/middleware/base.d.ts +4 -0
  4. package/dist/middleware/base.js +6 -0
  5. package/dist/middleware/build.d.ts +30 -2
  6. package/dist/middleware/build.js +60 -1
  7. package/dist/middleware/buildWebSocket.d.ts +5 -0
  8. package/dist/middleware/buildWebSocket.js +127 -0
  9. package/dist/middleware/database/connectionProxy.d.ts +2 -0
  10. package/dist/middleware/database/connectionProxy.js +43 -19
  11. package/dist/middleware/database/sqlClient.js +34 -9
  12. package/dist/middleware/index.d.ts +30 -1
  13. package/dist/middleware/index.js +3 -0
  14. package/dist/middleware/mysql.d.ts +2 -0
  15. package/dist/middleware/websocketBase.d.ts +32 -0
  16. package/dist/middleware/websocketBase.js +65 -0
  17. package/dist/utils/secretsManager.d.ts +3 -0
  18. package/dist/utils/secretsManager.js +10 -1
  19. package/jest.config.js +7 -7
  20. package/package.json +70 -69
  21. package/src/aws/config.ts +46 -46
  22. package/src/aws/define.ts +10 -10
  23. package/src/aws/index.ts +3 -3
  24. package/src/aws/simple.ts +705 -705
  25. package/src/index.ts +3 -3
  26. package/src/internal/AwsError.ts +13 -13
  27. package/src/internal/oncePromise.ts +29 -29
  28. package/src/internal/s3.ts +75 -75
  29. package/src/middleware/aws.ts +78 -78
  30. package/src/middleware/base.ts +206 -198
  31. package/src/middleware/build.ts +273 -173
  32. package/src/middleware/buildWebSocket.ts +199 -0
  33. package/src/middleware/database/connectionProxy.ts +294 -247
  34. package/src/middleware/database/sqlClient.ts +208 -176
  35. package/src/middleware/index.ts +24 -21
  36. package/src/middleware/logger.ts +28 -28
  37. package/src/middleware/mysql.ts +62 -60
  38. package/src/middleware/trace.ts +265 -265
  39. package/src/middleware/websocketBase.ts +92 -0
  40. package/src/utils/index.ts +2 -2
  41. package/src/utils/logger.ts +94 -94
  42. package/src/utils/misc.ts +20 -20
  43. package/src/utils/secretsManager.ts +86 -73
  44. package/tsconfig.json +16 -15
  45. package/tslint.json +12 -12
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`.
@@ -9,12 +9,16 @@ export declare class HandlerRequest {
9
9
  private lazyBody?;
10
10
  constructor(event: any, context: any);
11
11
  get body(): any;
12
+ set body(value: any);
12
13
  get path(): {
13
14
  [key: string]: string | undefined;
14
15
  };
15
16
  get query(): {
16
17
  [key: string]: string | undefined;
17
18
  };
19
+ set query(value: {
20
+ [key: string]: any;
21
+ });
18
22
  header(key: string): string | undefined;
19
23
  records<T, U>(selector?: (each: T) => U): T[] | U[];
20
24
  }
@@ -29,12 +29,18 @@ class HandlerRequest {
29
29
  }
30
30
  return this.lazyBody || {};
31
31
  }
32
+ set body(value) {
33
+ this.lazyBody = value;
34
+ }
32
35
  get path() {
33
36
  return this.event.pathParameters || {};
34
37
  }
35
38
  get query() {
36
39
  return this.event.queryStringParameters || {};
37
40
  }
41
+ set query(value) {
42
+ this.event.queryStringParameters = value;
43
+ }
38
44
  header(key) {
39
45
  return this.event.headers[key.toLowerCase()];
40
46
  }
@@ -1,3 +1,31 @@
1
- import { Handler, HandlerAuxBase, HandlerPluginBase } from './base';
2
- declare const build: <Aux extends HandlerAuxBase>(plugins: Array<HandlerPluginBase<any>>) => (handler: Handler<Aux>) => (event: any, context: any, callback: any) => void;
1
+ import { type ZodError, type ZodSchema } from 'zod';
2
+ import { Handler, HandlerAuxBase, HandlerPluginBase, HandlerRequest, HandlerResponse } from './base';
3
+ declare const build: <Aux extends HandlerAuxBase>(plugins: Array<HandlerPluginBase<any>>) => ((handler: Handler<Aux>) => (event: any, context: any, callback: any) => void) & {
4
+ withBody: <S>(schema: ZodSchema<S>, handler: (context: {
5
+ request: Omit<HandlerRequest, "body"> & {
6
+ body: S;
7
+ };
8
+ response: HandlerResponse;
9
+ aux: Aux;
10
+ }) => any, onInvalid?: (error: ZodError) => {
11
+ statusCode: number;
12
+ body: any;
13
+ } | Promise<{
14
+ statusCode: number;
15
+ body: any;
16
+ } | void> | void) => (event: any, context: any, callback: any) => void;
17
+ withQuery: <Q>(schema: ZodSchema<Q>, handler: (context: {
18
+ request: Omit<HandlerRequest, "query"> & {
19
+ query: Q;
20
+ };
21
+ response: HandlerResponse;
22
+ aux: Aux;
23
+ }) => any, onInvalid?: (error: ZodError<Q>) => {
24
+ statusCode: number;
25
+ body: any;
26
+ } | Promise<{
27
+ statusCode: number;
28
+ body: any;
29
+ } | void> | void) => (event: any, context: any, callback: any) => void;
30
+ };
3
31
  export default build;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const logger_1 = require("../utils/logger");
4
+ const zod_1 = require("zod");
4
5
  const utils_1 = require("../utils");
5
6
  const base_1 = require("./base");
6
7
  const logger = (0, logger_1.getLogger)(__filename);
@@ -107,8 +108,66 @@ class HandlerProxy {
107
108
  // It will break type safety because there is no relation between Aux and Plugin.
108
109
  const build = (plugins) => {
109
110
  const middleware = new HandlerMiddleware(plugins);
110
- return (handler) => (event, context, callback) => {
111
+ const invoke = (handler) => (event, context, callback) => {
111
112
  new HandlerProxy(event, context, callback).call(middleware, handler);
112
113
  };
114
+ /**
115
+ * @param schema - Zod schema to validate the request body.
116
+ * @param handler - Handler that receives the validated body.
117
+ * @param onInvalid - Optional callback to customize invalid responses. If it
118
+ * returns `{ statusCode, body }`, that is sent instead of the default zod
119
+ * error payload.
120
+ */
121
+ const withBody = (schema, handler, onInvalid) => invoke(async ({ request, response, aux }) => {
122
+ const parsed = schema.safeParse(request.body);
123
+ if (!parsed.success) {
124
+ logger.error(`Validation failed: ${(0, utils_1.stringifyError)((0, zod_1.treeifyError)(parsed.error))}`);
125
+ if (onInvalid) {
126
+ const result = await onInvalid(parsed.error);
127
+ if (result) {
128
+ return response.fail(result.body, result.statusCode);
129
+ }
130
+ }
131
+ return response.fail((0, zod_1.treeifyError)(parsed.error), 400);
132
+ }
133
+ const typedRequest = request;
134
+ typedRequest.body = parsed.data;
135
+ return handler({
136
+ request: typedRequest,
137
+ response,
138
+ aux,
139
+ });
140
+ });
141
+ /**
142
+ * @param schema - Zod schema to validate the request query.
143
+ * @param handler - Handler that receives the validated query.
144
+ * @param onInvalid - Optional callback to customize invalid responses. If it
145
+ * returns `{ statusCode, body }`, that is sent instead of the default zod
146
+ * error payload.
147
+ */
148
+ const withQuery = (schema, handler, onInvalid) => invoke(async ({ request, response, aux }) => {
149
+ const parsed = schema.safeParse(request.query);
150
+ if (!parsed.success) {
151
+ logger.error(`Validation failed: ${(0, utils_1.stringifyError)((0, zod_1.treeifyError)(parsed.error))}`);
152
+ if (onInvalid) {
153
+ const result = await onInvalid(parsed.error);
154
+ if (result) {
155
+ return response.fail(result.body, result.statusCode);
156
+ }
157
+ }
158
+ return response.fail((0, zod_1.treeifyError)(parsed.error), 400);
159
+ }
160
+ const typedRequest = request;
161
+ typedRequest.query = parsed.data;
162
+ return handler({
163
+ request: typedRequest,
164
+ response,
165
+ aux,
166
+ });
167
+ });
168
+ return Object.assign(invoke, {
169
+ withBody,
170
+ withQuery,
171
+ });
113
172
  };
114
173
  exports.default = build;
@@ -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;
@@ -8,6 +8,7 @@ export declare class ConnectionProxy {
8
8
  private connectionInitOnce;
9
9
  private initialized;
10
10
  private dbName?;
11
+ private readonly MAX_RETRIES;
11
12
  constructor(options: MySQLPluginOptions);
12
13
  query: <T>(sql: string, params?: any[]) => Promise<T | undefined>;
13
14
  fetch: <T>(sql: string, params?: any[]) => Promise<T[]>;
@@ -23,6 +24,7 @@ export declare class ConnectionProxy {
23
24
  destroyConnection: () => void;
24
25
  onPluginCreated: () => Promise<void>;
25
26
  private prepareConnection;
27
+ private createConnection;
26
28
  private ensureConnectionConfig;
27
29
  private changeDatabase;
28
30
  private tryToInitializeSchema;
@@ -15,6 +15,7 @@ class ConnectionProxy {
15
15
  connectionInitOnce = new oncePromise_1.OncePromise();
16
16
  initialized;
17
17
  dbName;
18
+ MAX_RETRIES = 1;
18
19
  constructor(options) {
19
20
  this.options = options;
20
21
  if (options.schema && options.schema.database) {
@@ -22,10 +23,11 @@ class ConnectionProxy {
22
23
  options.config.database = undefined;
23
24
  }
24
25
  this.secretsCache = secretsManager_1.SecretsManagerCache.getInstance();
26
+ if (options.secretsManagerConfig) {
27
+ this.secretsCache.configure(options.secretsManagerConfig);
28
+ }
25
29
  }
26
- query = (sql, params) => new Promise(async (resolve, reject) => {
27
- const connection = await this.prepareConnection();
28
- await this.tryToInitializeSchema(false);
30
+ query = (sql, params) => this.prepareConnection().then((connection) => this.tryToInitializeSchema(false).then(() => new Promise((resolve, reject) => {
29
31
  if (process.env.NODE_ENV !== 'test') {
30
32
  logger.silly(`Execute query[${sql}] with params[${params}]`);
31
33
  }
@@ -41,7 +43,7 @@ class ConnectionProxy {
41
43
  }
42
44
  }
43
45
  });
44
- });
46
+ })));
45
47
  fetch = (sql, params) => this.query(sql, params).then((res) => res || []);
46
48
  fetchOne = (sql, params, defaultValue) => this.fetch(sql, params).then((res) => {
47
49
  if (res === undefined || res[0] === undefined) {
@@ -50,9 +52,7 @@ class ConnectionProxy {
50
52
  }
51
53
  return res[0];
52
54
  });
53
- beginTransaction = () => new Promise(async (resolve, reject) => {
54
- const connection = await this.prepareConnection();
55
- await this.tryToInitializeSchema(false);
55
+ beginTransaction = () => this.prepareConnection().then((connection) => this.tryToInitializeSchema(false).then(() => new Promise((resolve, reject) => {
56
56
  connection.beginTransaction((err) => {
57
57
  if (err) {
58
58
  reject(err);
@@ -60,10 +60,8 @@ class ConnectionProxy {
60
60
  }
61
61
  resolve();
62
62
  });
63
- });
64
- commit = () => new Promise(async (resolve, reject) => {
65
- const connection = await this.prepareConnection();
66
- await this.tryToInitializeSchema(false);
63
+ })));
64
+ commit = () => this.prepareConnection().then((connection) => this.tryToInitializeSchema(false).then(() => new Promise((resolve, reject) => {
67
65
  connection.commit((err) => {
68
66
  if (err) {
69
67
  reject(err);
@@ -71,10 +69,8 @@ class ConnectionProxy {
71
69
  }
72
70
  resolve();
73
71
  });
74
- });
75
- rollback = () => new Promise(async (resolve, reject) => {
76
- const connection = await this.prepareConnection();
77
- await this.tryToInitializeSchema(false);
72
+ })));
73
+ rollback = () => this.prepareConnection().then((connection) => this.tryToInitializeSchema(false).then(() => new Promise((resolve, reject) => {
78
74
  connection.rollback((err) => {
79
75
  if (err) {
80
76
  reject(err);
@@ -82,7 +78,7 @@ class ConnectionProxy {
82
78
  }
83
79
  resolve();
84
80
  });
85
- });
81
+ })));
86
82
  clearConnection = () => {
87
83
  const conn = this.connection;
88
84
  this.connection = undefined;
@@ -120,12 +116,40 @@ class ConnectionProxy {
120
116
  }
121
117
  return await this.connectionInitOnce.run(async () => {
122
118
  await this.ensureConnectionConfig();
123
- const conn = (0, mysql2_1.createConnection)(this.connectionConfig);
124
- conn.connect();
125
- this.connection = conn;
119
+ this.connection = await this.createConnection(this.MAX_RETRIES);
126
120
  return this.connection;
127
121
  });
128
122
  };
123
+ createConnection = async (remainingRetries) => {
124
+ const conn = (0, mysql2_1.createConnection)(this.connectionConfig);
125
+ return new Promise((resolve, reject) => {
126
+ conn.on('error', (err) => {
127
+ logger.error(`Connection error event: ${err.message}`);
128
+ });
129
+ conn.connect((err) => {
130
+ if (err) {
131
+ logger.error(`Failed to connect to database: ${err.message}`);
132
+ conn.destroy();
133
+ if (remainingRetries > 0) {
134
+ logger.warn(`Retrying database connection... (${remainingRetries} attempt(s) remaining)`);
135
+ setTimeout(() => {
136
+ this.createConnection(remainingRetries - 1)
137
+ .then(resolve)
138
+ .catch(reject);
139
+ }, 100);
140
+ }
141
+ else {
142
+ logger.error('Database connection failed after all retries. Giving up.');
143
+ reject(err);
144
+ }
145
+ }
146
+ else {
147
+ logger.verbose('Database connection established successfully.');
148
+ resolve(conn);
149
+ }
150
+ });
151
+ });
152
+ };
129
153
  ensureConnectionConfig = async () => {
130
154
  if (this.connectionConfig) {
131
155
  return;
@@ -14,9 +14,13 @@ class LazyConnectionPool {
14
14
  secretsCache;
15
15
  configInitOnce = new oncePromise_1.OncePromise();
16
16
  connectionInitOnce = new oncePromise_1.OncePromise();
17
+ MAX_RETRIES = 1;
17
18
  constructor(options) {
18
19
  this.options = options;
19
20
  this.secretsCache = secretsManager_1.SecretsManagerCache.getInstance();
21
+ if (options.secretsManagerConfig) {
22
+ this.secretsCache.configure(options.secretsManagerConfig);
23
+ }
20
24
  }
21
25
  ensureConnectionConfig = async () => {
22
26
  if (this.connectionConfig) {
@@ -44,21 +48,42 @@ class LazyConnectionPool {
44
48
  this.connectionInitOnce
45
49
  .run(async () => {
46
50
  await this.ensureConnectionConfig();
47
- const conn = (0, mysql2_1.createConnection)(this.connectionConfig);
48
- return await new Promise((resolve, reject) => {
49
- conn.connect((err) => {
50
- if (err) {
51
+ return await this.createConnection(this.MAX_RETRIES);
52
+ })
53
+ .then((conn) => callback(null, conn))
54
+ .catch((err) => callback(err, {}));
55
+ };
56
+ createConnection = async (remainingRetries) => {
57
+ const conn = (0, mysql2_1.createConnection)(this.connectionConfig);
58
+ return new Promise((resolve, reject) => {
59
+ conn.on('error', (err) => {
60
+ logger.error(`Database connection error occurred: ${err.message}`);
61
+ });
62
+ conn.connect((err) => {
63
+ if (err) {
64
+ logger.error(`Failed to connect to database: ${err.message}`);
65
+ conn.destroy();
66
+ if (remainingRetries > 0) {
67
+ logger.warn(`Retrying database connection... (${remainingRetries} attempt(s) remaining)`);
68
+ setTimeout(() => {
69
+ this.createConnection(remainingRetries - 1)
70
+ .then(resolve)
71
+ .catch(reject);
72
+ }, 100);
73
+ }
74
+ else {
75
+ logger.error('Database connection failed after all retries. Giving up.');
51
76
  reject(err);
52
- return;
53
77
  }
78
+ }
79
+ else {
80
+ logger.verbose('Database connection established successfully.');
54
81
  const wrapped = this._addRelease(conn);
55
82
  this.connection = wrapped;
56
83
  resolve(wrapped);
57
- });
84
+ }
58
85
  });
59
- })
60
- .then((conn) => callback(null, conn))
61
- .catch((err) => callback(err, {}));
86
+ });
62
87
  };
63
88
  end = (callback) => {
64
89
  const conn = this.connection;
@@ -1,5 +1,33 @@
1
1
  export declare const middleware: {
2
- build: <Aux extends import("./base").HandlerAuxBase>(plugins: Array<import("./base").HandlerPluginBase<any>>) => (handler: import("./base").Handler<Aux>) => (event: any, context: any, callback: any) => void;
2
+ build: <Aux extends import("./base").HandlerAuxBase>(plugins: Array<import("./base").HandlerPluginBase<any>>) => ((handler: import("./base").Handler<Aux>) => (event: any, context: any, callback: any) => void) & {
3
+ withBody: <S>(schema: import("zod").ZodType<S>, handler: (context: {
4
+ request: Omit<import("./base").HandlerRequest, "body"> & {
5
+ body: S;
6
+ };
7
+ response: import("./base").HandlerResponse;
8
+ aux: Aux;
9
+ }) => any, onInvalid?: (error: import("zod").ZodError) => {
10
+ statusCode: number;
11
+ body: any;
12
+ } | Promise<{
13
+ statusCode: number;
14
+ body: any;
15
+ } | void> | void) => (event: any, context: any, callback: any) => void;
16
+ withQuery: <Q>(schema: import("zod").ZodType<Q>, handler: (context: {
17
+ request: Omit<import("./base").HandlerRequest, "query"> & {
18
+ query: Q;
19
+ };
20
+ response: import("./base").HandlerResponse;
21
+ aux: Aux;
22
+ }) => any, onInvalid?: (error: import("zod").ZodError<Q>) => {
23
+ statusCode: number;
24
+ body: any;
25
+ } | Promise<{
26
+ statusCode: number;
27
+ body: any;
28
+ } | void> | void) => (event: any, context: any, callback: any) => void;
29
+ };
30
+ buildWebSocket: <Aux extends import("./websocketBase").WebSocketHandlerAuxBase>(plugins: Array<import("./base").HandlerPluginBase<any>>) => (handler: import("./websocketBase").WebSocketHandler<Aux>) => import("aws-lambda").APIGatewayProxyWebsocketHandlerV2;
3
31
  aws: (options?: import("./aws").AWSPluginOptions) => import("./aws").AWSPlugin;
4
32
  trace: (options: import("./trace").TracerPluginOptions) => import("./trace").TracerPlugin;
5
33
  logger: (options: import("./logger").LoggerPluginOptions) => import("./logger").LoggerPlugin;
@@ -11,3 +39,4 @@ export * from './database/index';
11
39
  export * from './logger';
12
40
  export * from './mysql';
13
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);
@@ -1,3 +1,4 @@
1
+ import type { SecretsManagerClientConfig } from '@aws-sdk/client-secrets-manager';
1
2
  import type { ConnectionOptions, PoolOptions } from 'mysql2';
2
3
  import { HandlerAuxBase, HandlerPluginBase } from './base';
3
4
  import { ConnectionProxy } from './database/connectionProxy';
@@ -12,6 +13,7 @@ export interface MySQLPluginOptions {
12
13
  * AWS Secrets Manager secret ID containing {@link DatabaseCredentials}
13
14
  */
14
15
  secretId?: string;
16
+ secretsManagerConfig?: SecretsManagerClientConfig;
15
17
  schema?: {
16
18
  eager?: boolean;
17
19
  ignoreError?: boolean;
@@ -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;