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.
@@ -1,265 +1,265 @@
1
- import { v4 as uuid4 } from 'uuid';
2
- import {
3
- AWSComponent,
4
- loadAWSConfig,
5
- SimpleAWS,
6
- SimpleAWSConfigLoadParam,
7
- } from '../aws';
8
- import { getLogger, stringifyError } from '../utils';
9
-
10
- import { SQS } from '@aws-sdk/client-sqs';
11
- import { $enum } from 'ts-enum-util';
12
- import { HandlerAuxBase, HandlerContext, HandlerPluginBase } from './base';
13
-
14
- const logger = getLogger(__filename);
15
-
16
- interface ITracerLog {
17
- uuid: string;
18
- timestamp: number;
19
- route: string;
20
- key: string;
21
- system: string;
22
- action: string;
23
- attribute: string;
24
- body: string;
25
- error: boolean;
26
- client: string;
27
- version: string;
28
- }
29
-
30
- interface ITracerLogInput {
31
- route?: string;
32
- key?: string;
33
- system?: string;
34
- action?: string;
35
- attribute: string;
36
- body: string;
37
- error?: boolean;
38
- client?: string;
39
- version?: string;
40
- }
41
-
42
- export class TracerLog implements ITracerLog {
43
- public readonly uuid: string;
44
- public readonly timestamp: number;
45
-
46
- constructor(
47
- public readonly route: string,
48
- public readonly key: string,
49
- public readonly system: string,
50
- public readonly action: string,
51
- public readonly attribute: string,
52
- public readonly body: string,
53
- public readonly error: boolean,
54
- public readonly client: string,
55
- public readonly version: string,
56
- ) {
57
- this.uuid = uuid4();
58
- this.timestamp = Date.now();
59
- }
60
- }
61
-
62
- export class Tracer {
63
- private queueName: string;
64
- private sqs: SQS;
65
- private buffer: TracerLog[];
66
-
67
- constructor(queueName: string, sqs: SQS) {
68
- this.queueName = queueName;
69
- this.sqs = sqs;
70
- this.buffer = [];
71
- }
72
-
73
- public push = (log: TracerLog) => this.buffer.push(log);
74
-
75
- public flush = async () => {
76
- if (this.buffer.length === 0) {
77
- return;
78
- }
79
- try {
80
- const urlResult = await this.sqs.getQueueUrl({
81
- QueueName: this.queueName,
82
- });
83
- logger.stupid(`urlResult`, urlResult);
84
- if (!urlResult.QueueUrl) {
85
- throw new Error(`No queue url with name[${this.queueName}]`);
86
- }
87
- const eventQueueUrl = urlResult.QueueUrl;
88
-
89
- const chunkSize = 10;
90
- for (let begin = 0; begin < this.buffer.length; begin += chunkSize) {
91
- const end = Math.min(this.buffer.length, begin + chunkSize);
92
- const subset = this.buffer.slice(begin, end);
93
- const sendBatchResult = await this.sqs.sendMessageBatch({
94
- QueueUrl: eventQueueUrl,
95
- Entries: subset.map((each) => ({
96
- Id: `${each.key}_${each.uuid}`,
97
- MessageBody: JSON.stringify(each),
98
- })),
99
- });
100
- logger.stupid(`sendBatchResult`, sendBatchResult);
101
- }
102
-
103
- this.buffer = [];
104
- } catch (error) {
105
- logger.warn(`Error in eventSource: ${error}`);
106
- }
107
- };
108
- }
109
-
110
- export class TracerWrapper {
111
- constructor(
112
- private tracer: Tracer,
113
- private route: string,
114
- private system: string,
115
- private key: string,
116
- private action: string,
117
- private client: string,
118
- private version: string,
119
- ) {}
120
-
121
- public push = (attribute: string, body: string, error: boolean = false) => {
122
- this.tracer.push(
123
- new TracerLog(
124
- this.route,
125
- this.key,
126
- this.system,
127
- this.action,
128
- attribute,
129
- body,
130
- error,
131
- this.client,
132
- this.version,
133
- ),
134
- );
135
- };
136
-
137
- public send = (log: ITracerLogInput) => {
138
- this.tracer.push(
139
- new TracerLog(
140
- log.route || this.route,
141
- log.key || this.key,
142
- log.system || this.system,
143
- log.action || this.action,
144
- log.attribute,
145
- log.body,
146
- log.error || false,
147
- log.client || this.client,
148
- log.version || this.version,
149
- ),
150
- );
151
- };
152
- }
153
-
154
- export interface TracerPluginOptions {
155
- route: string;
156
- queueName: string;
157
- system: string;
158
-
159
- awsConfig?: SimpleAWSConfigLoadParam;
160
- region?: string;
161
- }
162
-
163
- export interface TracerPluginAux extends HandlerAuxBase {
164
- tracer: (key: string, action: string) => TracerWrapper;
165
- }
166
-
167
- export class TracerPlugin extends HandlerPluginBase<TracerPluginAux> {
168
- private tracer: Tracer;
169
- private options: TracerPluginOptions;
170
- private last: { key: string; action: string };
171
- private client: { agent: string; version: string };
172
-
173
- constructor(options: TracerPluginOptions) {
174
- super();
175
- this.options = options;
176
- this.last = {
177
- key: 'nothing',
178
- action: 'unknown',
179
- };
180
- this.client = {
181
- agent: '',
182
- version: '',
183
- };
184
- }
185
-
186
- public create = async () => {
187
- const awsConfig = this.options.awsConfig
188
- ? await loadAWSConfig(this.options.awsConfig)
189
- : undefined;
190
-
191
- const sqs = (() => {
192
- if (!awsConfig) {
193
- return new SQS({
194
- region: this.options.region,
195
- });
196
- }
197
- $enum(AWSComponent).forEach((eachComponent) => {
198
- const config = awsConfig.get(eachComponent);
199
- if (config) {
200
- config.region = this.options.region;
201
- }
202
- });
203
- return new SimpleAWS(awsConfig).sqs;
204
- })();
205
-
206
- this.tracer = new Tracer(this.options.queueName, sqs);
207
- const tracer = (key: string, action: string) => {
208
- this.last = { key, action };
209
- return new TracerWrapper(
210
- this.tracer,
211
- this.options.route,
212
- this.options.system,
213
- key,
214
- action,
215
- this.client.agent,
216
- this.client.version,
217
- );
218
- };
219
- return { tracer };
220
- };
221
-
222
- public begin = ({ request }: HandlerContext<TracerPluginAux>) => {
223
- this.client.version = request.header('X-Version') || '0.0.0';
224
- this.client.agent = (() => {
225
- const fromHeader = request.header('User-Agent');
226
- if (fromHeader) {
227
- return fromHeader;
228
- }
229
- if (
230
- request.context &&
231
- request.context.identity &&
232
- request.context.identity.userAgent
233
- ) {
234
- return request.context.identity.userAgent;
235
- }
236
- return '';
237
- })();
238
- };
239
-
240
- public end = () => this.tracer.flush();
241
-
242
- public error = ({ request, aux }: HandlerContext<TracerPluginAux>) => {
243
- if (!aux) {
244
- console.warn('Aux is not initialized');
245
- return;
246
- }
247
- if (!request.lastError) {
248
- return;
249
- }
250
-
251
- const { key, action } = this.last;
252
- aux
253
- .tracer(key, action)
254
- .push(
255
- 'error',
256
- typeof request.lastError === 'string'
257
- ? request.lastError
258
- : stringifyError(request.lastError),
259
- true,
260
- );
261
- };
262
- }
263
-
264
- const build = (options: TracerPluginOptions) => new TracerPlugin(options);
265
- export default build;
1
+ import { v4 as uuid4 } from 'uuid';
2
+ import {
3
+ AWSComponent,
4
+ loadAWSConfig,
5
+ SimpleAWS,
6
+ SimpleAWSConfigLoadParam,
7
+ } from '../aws';
8
+ import { getLogger, stringifyError } from '../utils';
9
+
10
+ import { SQS } from '@aws-sdk/client-sqs';
11
+ import { $enum } from 'ts-enum-util';
12
+ import { HandlerAuxBase, HandlerContext, HandlerPluginBase } from './base';
13
+
14
+ const logger = getLogger(__filename);
15
+
16
+ interface ITracerLog {
17
+ uuid: string;
18
+ timestamp: number;
19
+ route: string;
20
+ key: string;
21
+ system: string;
22
+ action: string;
23
+ attribute: string;
24
+ body: string;
25
+ error: boolean;
26
+ client: string;
27
+ version: string;
28
+ }
29
+
30
+ interface ITracerLogInput {
31
+ route?: string;
32
+ key?: string;
33
+ system?: string;
34
+ action?: string;
35
+ attribute: string;
36
+ body: string;
37
+ error?: boolean;
38
+ client?: string;
39
+ version?: string;
40
+ }
41
+
42
+ export class TracerLog implements ITracerLog {
43
+ public readonly uuid: string;
44
+ public readonly timestamp: number;
45
+
46
+ constructor(
47
+ public readonly route: string,
48
+ public readonly key: string,
49
+ public readonly system: string,
50
+ public readonly action: string,
51
+ public readonly attribute: string,
52
+ public readonly body: string,
53
+ public readonly error: boolean,
54
+ public readonly client: string,
55
+ public readonly version: string,
56
+ ) {
57
+ this.uuid = uuid4();
58
+ this.timestamp = Date.now();
59
+ }
60
+ }
61
+
62
+ export class Tracer {
63
+ private queueName: string;
64
+ private sqs: SQS;
65
+ private buffer: TracerLog[];
66
+
67
+ constructor(queueName: string, sqs: SQS) {
68
+ this.queueName = queueName;
69
+ this.sqs = sqs;
70
+ this.buffer = [];
71
+ }
72
+
73
+ public push = (log: TracerLog) => this.buffer.push(log);
74
+
75
+ public flush = async () => {
76
+ if (this.buffer.length === 0) {
77
+ return;
78
+ }
79
+ try {
80
+ const urlResult = await this.sqs.getQueueUrl({
81
+ QueueName: this.queueName,
82
+ });
83
+ logger.stupid(`urlResult`, urlResult);
84
+ if (!urlResult.QueueUrl) {
85
+ throw new Error(`No queue url with name[${this.queueName}]`);
86
+ }
87
+ const eventQueueUrl = urlResult.QueueUrl;
88
+
89
+ const chunkSize = 10;
90
+ for (let begin = 0; begin < this.buffer.length; begin += chunkSize) {
91
+ const end = Math.min(this.buffer.length, begin + chunkSize);
92
+ const subset = this.buffer.slice(begin, end);
93
+ const sendBatchResult = await this.sqs.sendMessageBatch({
94
+ QueueUrl: eventQueueUrl,
95
+ Entries: subset.map((each) => ({
96
+ Id: `${each.key}_${each.uuid}`,
97
+ MessageBody: JSON.stringify(each),
98
+ })),
99
+ });
100
+ logger.stupid(`sendBatchResult`, sendBatchResult);
101
+ }
102
+
103
+ this.buffer = [];
104
+ } catch (error) {
105
+ logger.warn(`Error in eventSource: ${error}`);
106
+ }
107
+ };
108
+ }
109
+
110
+ export class TracerWrapper {
111
+ constructor(
112
+ private tracer: Tracer,
113
+ private route: string,
114
+ private system: string,
115
+ private key: string,
116
+ private action: string,
117
+ private client: string,
118
+ private version: string,
119
+ ) {}
120
+
121
+ public push = (attribute: string, body: string, error: boolean = false) => {
122
+ this.tracer.push(
123
+ new TracerLog(
124
+ this.route,
125
+ this.key,
126
+ this.system,
127
+ this.action,
128
+ attribute,
129
+ body,
130
+ error,
131
+ this.client,
132
+ this.version,
133
+ ),
134
+ );
135
+ };
136
+
137
+ public send = (log: ITracerLogInput) => {
138
+ this.tracer.push(
139
+ new TracerLog(
140
+ log.route || this.route,
141
+ log.key || this.key,
142
+ log.system || this.system,
143
+ log.action || this.action,
144
+ log.attribute,
145
+ log.body,
146
+ log.error || false,
147
+ log.client || this.client,
148
+ log.version || this.version,
149
+ ),
150
+ );
151
+ };
152
+ }
153
+
154
+ export interface TracerPluginOptions {
155
+ route: string;
156
+ queueName: string;
157
+ system: string;
158
+
159
+ awsConfig?: SimpleAWSConfigLoadParam;
160
+ region?: string;
161
+ }
162
+
163
+ export interface TracerPluginAux extends HandlerAuxBase {
164
+ tracer: (key: string, action: string) => TracerWrapper;
165
+ }
166
+
167
+ export class TracerPlugin extends HandlerPluginBase<TracerPluginAux> {
168
+ private tracer: Tracer;
169
+ private options: TracerPluginOptions;
170
+ private last: { key: string; action: string };
171
+ private client: { agent: string; version: string };
172
+
173
+ constructor(options: TracerPluginOptions) {
174
+ super();
175
+ this.options = options;
176
+ this.last = {
177
+ key: 'nothing',
178
+ action: 'unknown',
179
+ };
180
+ this.client = {
181
+ agent: '',
182
+ version: '',
183
+ };
184
+ }
185
+
186
+ public create = async () => {
187
+ const awsConfig = this.options.awsConfig
188
+ ? await loadAWSConfig(this.options.awsConfig)
189
+ : undefined;
190
+
191
+ const sqs = (() => {
192
+ if (!awsConfig) {
193
+ return new SQS({
194
+ region: this.options.region,
195
+ });
196
+ }
197
+ $enum(AWSComponent).forEach((eachComponent) => {
198
+ const config = awsConfig.get(eachComponent);
199
+ if (config) {
200
+ config.region = this.options.region;
201
+ }
202
+ });
203
+ return new SimpleAWS(awsConfig).sqs;
204
+ })();
205
+
206
+ this.tracer = new Tracer(this.options.queueName, sqs);
207
+ const tracer = (key: string, action: string) => {
208
+ this.last = { key, action };
209
+ return new TracerWrapper(
210
+ this.tracer,
211
+ this.options.route,
212
+ this.options.system,
213
+ key,
214
+ action,
215
+ this.client.agent,
216
+ this.client.version,
217
+ );
218
+ };
219
+ return { tracer };
220
+ };
221
+
222
+ public begin = ({ request }: HandlerContext<TracerPluginAux>) => {
223
+ this.client.version = request.header('X-Version') || '0.0.0';
224
+ this.client.agent = (() => {
225
+ const fromHeader = request.header('User-Agent');
226
+ if (fromHeader) {
227
+ return fromHeader;
228
+ }
229
+ if (
230
+ request.context &&
231
+ request.context.identity &&
232
+ request.context.identity.userAgent
233
+ ) {
234
+ return request.context.identity.userAgent;
235
+ }
236
+ return '';
237
+ })();
238
+ };
239
+
240
+ public end = () => this.tracer.flush();
241
+
242
+ public error = ({ request, aux }: HandlerContext<TracerPluginAux>) => {
243
+ if (!aux) {
244
+ console.warn('Aux is not initialized');
245
+ return;
246
+ }
247
+ if (!request.lastError) {
248
+ return;
249
+ }
250
+
251
+ const { key, action } = this.last;
252
+ aux
253
+ .tracer(key, action)
254
+ .push(
255
+ 'error',
256
+ typeof request.lastError === 'string'
257
+ ? request.lastError
258
+ : stringifyError(request.lastError),
259
+ true,
260
+ );
261
+ };
262
+ }
263
+
264
+ const build = (options: TracerPluginOptions) => new TracerPlugin(options);
265
+ export default build;
@@ -0,0 +1,92 @@
1
+ import type { APIGatewayProxyWebsocketEventV2, Context } from 'aws-lambda';
2
+ import { getLogger } from '../utils/logger';
3
+
4
+ const logger = getLogger(__filename);
5
+
6
+ export interface WebSocketHandlerAuxBase {
7
+ [key: string]: any;
8
+ }
9
+
10
+ export class WebSocketHandlerRequest {
11
+ public event: APIGatewayProxyWebsocketEventV2;
12
+ public context: Context;
13
+ public lastError: Error | string | undefined;
14
+
15
+ private lazyBody?: any;
16
+
17
+ constructor(event: APIGatewayProxyWebsocketEventV2, context: Context) {
18
+ this.event = event;
19
+ this.context = context;
20
+ this.lastError = undefined;
21
+
22
+ const ev = this.event as any;
23
+ if (ev.headers) {
24
+ const normalized: Record<string, string | undefined> = {};
25
+ for (const key of Object.keys(ev.headers)) {
26
+ normalized[key.toLowerCase()] = ev.headers[key];
27
+ }
28
+ ev.headers = normalized;
29
+ }
30
+ }
31
+
32
+ get body() {
33
+ if (!this.event.body) {
34
+ return {};
35
+ }
36
+ if (this.lazyBody === undefined) {
37
+ try {
38
+ this.lazyBody = JSON.parse(this.event.body);
39
+ } catch (error) {
40
+ logger.error(`Failed to parse WebSocket body: ${error}`);
41
+ this.lazyBody = {};
42
+ }
43
+ }
44
+ return this.lazyBody || {};
45
+ }
46
+
47
+ get connectionId(): string {
48
+ return this.event.requestContext.connectionId;
49
+ }
50
+
51
+ get routeKey(): string {
52
+ return this.event.requestContext.routeKey;
53
+ }
54
+
55
+ get domainName(): string {
56
+ return this.event.requestContext.domainName;
57
+ }
58
+
59
+ get stage(): string {
60
+ return this.event.requestContext.stage;
61
+ }
62
+
63
+ // HTTP plugin compatibility methods
64
+
65
+ /**
66
+ * For HTTP plugin compatibility (TracerPlugin uses this).
67
+ * WebSocket events may have headers in $connect route (HTTP handshake),
68
+ * but typically don't have headers in other routes.
69
+ */
70
+ public header(key: string): string | undefined {
71
+ const event = this.event as any;
72
+ if (event.headers) {
73
+ return event.headers[key.toLowerCase()];
74
+ }
75
+ return undefined;
76
+ }
77
+ }
78
+
79
+ export interface WebSocketHandlerResponse {
80
+ statusCode: number;
81
+ body?: string;
82
+ }
83
+
84
+ export interface WebSocketHandlerContext<A extends WebSocketHandlerAuxBase> {
85
+ request: WebSocketHandlerRequest;
86
+ response: undefined; // For HTTP plugin compatibility (not used in WebSocket handlers)
87
+ aux: A;
88
+ }
89
+
90
+ export type WebSocketHandler<A extends WebSocketHandlerAuxBase> = (
91
+ context: WebSocketHandlerContext<A>,
92
+ ) => WebSocketHandlerResponse | Promise<WebSocketHandlerResponse> | undefined;
@@ -1,2 +1,2 @@
1
- export * from './logger';
2
- export * from './misc';
1
+ export * from './logger';
2
+ export * from './misc';