unnbound-events 1.0.11 → 1.0.12

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Unnbound Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,4 +1,4 @@
1
1
  export { createEventsClient } from './lib/client';
2
- export type { EventsClient, EventRequest, EventResponse, RouteHandler, RouteMatcher, RegisteredRoute, SqsBatchEvent, StartOptions, } from './lib/types';
2
+ export type { EventsClient, EventRequest, EventResponse, RouteHandler, RouteMatcher, RegisteredRoute, SqsBatchEvent, } from './lib/types';
3
3
  export { createExpressMiddleware } from './lib/adapters/express';
4
4
  export { createSqsConsumer } from './lib/adapters/sqs';
@@ -183,16 +183,16 @@ function createEventsClient() {
183
183
  };
184
184
  },
185
185
  sqsListen(options) {
186
- const awsSqs = (() => {
186
+ const AWS = (() => {
187
187
  try {
188
- return require('@aws-sdk/client-sqs');
188
+ return require('aws-sdk');
189
189
  }
190
190
  catch {
191
191
  return null;
192
192
  }
193
193
  })();
194
- if (!awsSqs) {
195
- throw new Error('@aws-sdk/client-sqs is required to use sqsListen');
194
+ if (!AWS) {
195
+ throw new Error('aws-sdk is required to use sqsListen');
196
196
  }
197
197
  const env = globalThis
198
198
  .process?.env ?? {};
@@ -205,7 +205,8 @@ function createEventsClient() {
205
205
  const waitTimeSeconds = options?.waitTimeSeconds ?? 10;
206
206
  const maxMessages = options?.maxMessages ?? 10;
207
207
  const visibilityTimeout = options?.visibilityTimeoutSeconds;
208
- const sqs = new awsSqs.SQSClient({ region, endpoint });
208
+ const AWSLib = AWS;
209
+ const sqs = new AWSLib.SQS({ region, endpoint });
209
210
  let stopped = false;
210
211
  const consume = this.sqs();
211
212
  async function loop() {
@@ -215,9 +216,10 @@ function createEventsClient() {
215
216
  QueueUrl: queueUrl,
216
217
  MaxNumberOfMessages: maxMessages,
217
218
  WaitTimeSeconds: waitTimeSeconds,
218
- ...(visibilityTimeout && { VisibilityTimeout: visibilityTimeout }),
219
219
  };
220
- const resp = (await sqs.send(new awsSqs.ReceiveMessageCommand(params)));
220
+ if (visibilityTimeout)
221
+ params.VisibilityTimeout = visibilityTimeout;
222
+ const resp = await sqs.receiveMessage(params).promise();
221
223
  const messages = (resp.Messages ?? []).map((m) => ({
222
224
  id: m.MessageId ?? '',
223
225
  body: String(m.Body ?? ''),
@@ -235,7 +237,7 @@ function createEventsClient() {
235
237
  for (let i = 0; i < toDelete.length; i += 10) {
236
238
  const chunk = toDelete.slice(i, i + 10);
237
239
  if (chunk.length > 0) {
238
- await sqs.send(new awsSqs.DeleteMessageBatchCommand({ QueueUrl: queueUrl, Entries: chunk }));
240
+ await sqs.deleteMessageBatch({ QueueUrl: queueUrl, Entries: chunk }).promise();
239
241
  }
240
242
  }
241
243
  }
@@ -254,59 +256,5 @@ function createEventsClient() {
254
256
  },
255
257
  });
256
258
  },
257
- async start(options) {
258
- // Default to starting both HTTP and SQS if no options provided
259
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
260
- const httpOptions = options?.http ?? { port: 3000 };
261
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
262
- const sqsOptions = options?.sqs ?? {};
263
- // Start HTTP server
264
- let httpServer;
265
- try {
266
- httpServer = await this.http(httpOptions);
267
- }
268
- catch (error) {
269
- unnbound_logger_sdk_1.logger.warn({ error }, 'Failed to start HTTP server, continuing without it');
270
- }
271
- // Start SQS listener
272
- let sqsListener;
273
- try {
274
- sqsListener = await this.sqsListen(sqsOptions);
275
- }
276
- catch (error) {
277
- unnbound_logger_sdk_1.logger.warn({ error }, 'Failed to start SQS listener, continuing without it');
278
- }
279
- // If neither HTTP nor SQS started successfully, throw an error
280
- if (!httpServer && !sqsListener) {
281
- throw new Error('Failed to start any transport. Please check your configuration and try again.');
282
- }
283
- // Set up graceful shutdown
284
- const g = globalThis;
285
- const shutdown = async () => {
286
- unnbound_logger_sdk_1.logger.info('Received shutdown signal, closing servers...');
287
- const promises = [];
288
- if (httpServer)
289
- promises.push(httpServer.close());
290
- if (sqsListener)
291
- promises.push(sqsListener.stop());
292
- await Promise.all(promises);
293
- unnbound_logger_sdk_1.logger.info('All servers closed');
294
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
295
- g.process?.exit?.(0);
296
- };
297
- // Handle SIGINT and SIGTERM
298
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
299
- g.process?.on?.('SIGINT', shutdown);
300
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
301
- g.process?.on?.('SIGTERM', shutdown);
302
- // Log startup information
303
- const services = [];
304
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
305
- if (httpServer)
306
- services.push(`HTTP on port ${httpOptions?.port ?? 3000}`);
307
- if (sqsListener)
308
- services.push('SQS listener');
309
- unnbound_logger_sdk_1.logger.info({ services: services.join(' and ') }, 'Services started. Press Ctrl+C to stop.');
310
- },
311
259
  };
312
260
  }
@@ -26,18 +26,6 @@ export type SqsRecord = {
26
26
  export interface SqsBatchEvent {
27
27
  Records: SqsRecord[];
28
28
  }
29
- export interface StartOptions {
30
- http?: {
31
- port?: number;
32
- };
33
- sqs?: {
34
- queueUrl?: string;
35
- region?: string;
36
- waitTimeSeconds?: number;
37
- maxMessages?: number;
38
- visibilityTimeoutSeconds?: number;
39
- };
40
- }
41
29
  export interface EventsClient {
42
30
  on: (method: HttpMethod, matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
43
31
  handle: (request: EventRequest<any>) => Promise<EventResponse<any>>;
@@ -69,5 +57,4 @@ export interface EventsClient {
69
57
  }) => Promise<{
70
58
  stop: () => Promise<void>;
71
59
  }>;
72
- start: (options?: StartOptions) => Promise<void>;
73
60
  }
@@ -248,12 +248,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
248
248
  await Promise.all(event.Records.map(async (record) => {
249
249
  try {
250
250
  const envelope = JSON.parse(record.body);
251
- const envelopeMessageId = typeof envelope.metadata?.messageId === 'string'
252
- ? envelope.metadata.messageId
253
- : typeof envelope.messageId === 'string'
254
- ? envelope.messageId
255
- : undefined;
256
- const messageId = envelopeMessageId ?? record.messageId;
251
+ const messageId = record.messageId;
257
252
  const originalTraceId = typeof envelope.metadata?.traceId === 'string'
258
253
  ? envelope.metadata.traceId
259
254
  : undefined;
@@ -275,9 +270,6 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
275
270
  version: envelope.version,
276
271
  traceId,
277
272
  messageId,
278
- ...(envelopeMessageId && envelopeMessageId !== record.messageId
279
- ? { deliveryMessageId: record.messageId }
280
- : {}),
281
273
  ...(originalTraceId && originalTraceId !== traceId ? { originalTraceId } : {}),
282
274
  },
283
275
  };
@@ -1,11 +1,9 @@
1
- import { Axios, type AxiosRequestConfig, type AxiosResponse } from 'axios';
1
+ import { Axios } from 'axios';
2
2
  import { HttpOptions } from './types';
3
- type GetPayload = (config: AxiosRequestConfig, res?: AxiosResponse) => object;
4
3
  /**
5
4
  * Wraps an axios instance to add tracing and span tracking
6
5
  * @param axios - The axios instance to wrap
7
6
  * @param options - Configuration options for HTTP tracing
8
7
  * @returns The wrapped axios instance with span tracking
9
8
  */
10
- export declare const traceAxios: (client: Axios, { ignoreTraceRoutes, traceHeaderKey, getPayload, }?: HttpOptions<GetPayload>) => Axios;
11
- export {};
9
+ export declare const traceAxios: (client: Axios, { ignoreTraceRoutes, traceHeaderKey, }?: HttpOptions) => Axios;
@@ -25,17 +25,15 @@ const buildOutgoingHttpPayload = (config, res) => ({
25
25
  : undefined,
26
26
  },
27
27
  });
28
- const getNoopPayload = () => ({});
29
28
  /**
30
29
  * Wraps an axios instance to add tracing and span tracking
31
30
  * @param axios - The axios instance to wrap
32
31
  * @param options - Configuration options for HTTP tracing
33
32
  * @returns The wrapped axios instance with span tracking
34
33
  */
35
- const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, getPayload = getNoopPayload, } = {
34
+ const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, } = {
36
35
  ignoreTraceRoutes: types_1.defaultIgnoreTraceRoutes,
37
36
  traceHeaderKey: types_1.defaultTraceHeaderKey,
38
- getPayload: getNoopPayload,
39
37
  }) => {
40
38
  const createSpanWrappedRequest = (originalMethod, method) => {
41
39
  const { headers: defaultHeaders, ...partialDefaultConfig } = client.defaults;
@@ -52,14 +50,11 @@ const traceAxios = (client, { ignoreTraceRoutes = types_1.defaultIgnoreTraceRout
52
50
  config = { ...config, headers: { ...config.headers, [traceHeaderKey]: traceId } };
53
51
  // Execute the request within a span
54
52
  return (0, span_1.startSpan)(`Outgoing HTTP request`, () => originalMethod(config), (options) => {
55
- const response = options
56
- ? options.error
57
- ? (0, axios_1.isAxiosError)(options.error)
58
- ? options.error.response
59
- : undefined
60
- : options.result
61
- : undefined;
62
- return { ...buildOutgoingHttpPayload(config, response), ...getPayload(config, response) };
53
+ if (!options)
54
+ return buildOutgoingHttpPayload(config);
55
+ if (options.error)
56
+ return buildOutgoingHttpPayload(config, (0, axios_1.isAxiosError)(options.error) ? options.error.response : undefined);
57
+ return buildOutgoingHttpPayload(config, options.result);
63
58
  });
64
59
  };
65
60
  };
@@ -1,10 +1,3 @@
1
- import { Request, RequestHandler } from 'express';
1
+ import { RequestHandler } from 'express';
2
2
  import { HttpOptions } from './types';
3
- interface Response {
4
- status: number;
5
- headers?: Record<string, string | undefined>;
6
- body?: unknown;
7
- }
8
- type GetPayload = (config: Request, res?: Response) => object;
9
- export declare const traceMiddleware: ({ ignoreTraceRoutes, traceHeaderKey, getPayload, }?: HttpOptions<GetPayload>) => RequestHandler;
10
- export {};
3
+ export declare const traceMiddleware: ({ ignoreTraceRoutes, traceHeaderKey, }?: HttpOptions) => RequestHandler;
@@ -19,7 +19,7 @@ const getFullUrl = (req) => {
19
19
  const host = req.get('host') || req.get('x-forwarded-host') || 'localhost';
20
20
  return `${protocol}://${host}${url}`;
21
21
  };
22
- const buildIncomingHttpPayload = (req, res) => ({
22
+ const buildIncomingHttpPayload = (req, res, body) => ({
23
23
  type: 'http',
24
24
  http: {
25
25
  url: getFullUrl(req),
@@ -32,18 +32,16 @@ const buildIncomingHttpPayload = (req, res) => ({
32
32
  },
33
33
  response: res
34
34
  ? {
35
- headers: res.headers,
36
- status: res.status,
37
- body: (0, utils_1.safeJsonParse)(res.body),
35
+ headers: res?.headers,
36
+ status: res.statusCode,
37
+ body: (0, utils_1.safeJsonParse)(body),
38
38
  }
39
39
  : undefined,
40
40
  },
41
41
  });
42
- const getNoopPayload = () => ({});
43
- const traceMiddleware = ({ ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, getPayload = getNoopPayload, } = {
42
+ const traceMiddleware = ({ ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes, traceHeaderKey = types_1.defaultTraceHeaderKey, } = {
44
43
  ignoreTraceRoutes: types_1.defaultIgnoreTraceRoutes,
45
44
  traceHeaderKey: types_1.defaultTraceHeaderKey,
46
- getPayload: getNoopPayload,
47
45
  }) => async (req, res, next) => {
48
46
  if ((0, utils_1.shouldIgnorePath)(req.path, ignoreTraceRoutes))
49
47
  return next();
@@ -62,14 +60,12 @@ const traceMiddleware = ({ ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes,
62
60
  return next();
63
61
  });
64
62
  }, (o) => {
65
- const response = o
63
+ return buildIncomingHttpPayload(req, o
66
64
  ? {
67
- status: res.statusCode,
65
+ statusCode: res.statusCode,
68
66
  headers: res.getHeaders(),
69
- body: res.locals.body,
70
67
  }
71
- : undefined;
72
- return { ...buildIncomingHttpPayload(req, response), ...getPayload(req, response) };
68
+ : undefined, res.locals.body);
73
69
  });
74
70
  }, { traceId });
75
71
  };
@@ -53,10 +53,9 @@ export interface SftpPayload {
53
53
  content?: string;
54
54
  exists?: string | false;
55
55
  }
56
- export interface HttpOptions<G extends Function> {
56
+ export interface HttpOptions {
57
57
  ignoreTraceRoutes?: string[];
58
58
  traceHeaderKey?: string;
59
- getPayload?: G;
60
59
  }
61
60
  export declare const defaultIgnoreTraceRoutes: string[];
62
61
  export declare const defaultTraceHeaderKey = "x-unnbound-trace-id";
package/package.json CHANGED
@@ -1,24 +1,9 @@
1
1
  {
2
2
  "name": "unnbound-events",
3
3
  "description": "Unified events SDK to handle HTTP routes and SQS messages with a single routing API.",
4
- "version": "1.0.11",
4
+ "version": "1.0.12",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "test": "jest",
10
- "test:coverage": "jest --coverage",
11
- "test:watch": "jest --watch",
12
- "typecheck": "tsc -noEmit",
13
- "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
14
- "lint:fix": "pnpm lint --fix",
15
- "format": "prettier --write .",
16
- "format:check": "prettier --check .",
17
- "prepublishOnly": "pnpm build",
18
- "start:example:express": "npx --yes tsx --watch examples/express.ts",
19
- "start:example:sqs": "npx --yes tsx --watch examples/sqs.ts",
20
- "start:example:http-and-sqs": "npx --yes tsx --watch examples/http-and-sqs.ts"
21
- },
22
7
  "author": "Unnbound Team",
23
8
  "license": "MIT",
24
9
  "repository": {
@@ -32,7 +17,7 @@
32
17
  "dependencies": {
33
18
  "express": "^4.0.0 || ^5.0.0",
34
19
  "@aws-sdk/client-sqs": "^3.0.0",
35
- "unnbound-logger-sdk": "^3.0.9"
20
+ "unnbound-logger-sdk": "3.0.19"
36
21
  },
37
22
  "peerDependencies": {
38
23
  "express": "^4.0.0 || ^5.0.0"
@@ -55,5 +40,20 @@
55
40
  "engines": {
56
41
  "node": ">=22"
57
42
  },
58
- "sideEffects": false
59
- }
43
+ "sideEffects": false,
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "test": "jest",
47
+ "test:coverage": "jest --coverage",
48
+ "test:watch": "jest --watch",
49
+ "typecheck": "tsc -noEmit",
50
+ "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
51
+ "lint:fix": "pnpm lint --fix",
52
+ "format": "prettier --write .",
53
+ "format:check": "prettier --check .",
54
+ "start:example:express": "npx --yes tsx --watch examples/express.ts",
55
+ "start:example:sqs": "npx --yes tsx --watch examples/sqs.ts",
56
+ "start:example:http-and-sqs": "npx --yes tsx --watch examples/http-and-sqs.ts",
57
+ "version:bump": "npm version patch"
58
+ }
59
+ }