unnbound-events 1.0.18 → 1.0.20

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.
package/README.md CHANGED
@@ -132,6 +132,56 @@ API:
132
132
  - `use(matcher, middleware)` where matcher is `string | RegExp | (path) => boolean`
133
133
  - `middleware(req, next) -> Promise<EventResponse>`
134
134
 
135
+ ## Enqueue Events
136
+
137
+ The `enqueue` function allows you to send events to the queue by using the async endpoint.
138
+
139
+ ### Basic Usage
140
+
141
+ ```ts
142
+ import { createEventsClient } from 'unnbound-events-sdk';
143
+
144
+ const client = createEventsClient();
145
+
146
+ client.post('/process-job', async (req) => {
147
+ console.log('Handle job:', req.body);
148
+
149
+ // Split job into multiple tasks
150
+ const tasks = [...];
151
+
152
+ tasks.map(async (task) => {
153
+ await client.enqueue({
154
+ method: 'POST',
155
+ path: '/process-task',
156
+ body: { jobId: '123', data: 'important data' }
157
+ });
158
+ });
159
+
160
+ return { status: 200, body: { processed: true } };
161
+ });
162
+
163
+ client.post('/process-task', async (req) => {
164
+ console.log('Handle task:', req.body);
165
+
166
+ return { status: 200, body: { processed: true } };
167
+ });
168
+
169
+ await client.start();
170
+ ```
171
+
172
+ ### Scheduled Jobs
173
+
174
+ ```ts
175
+ // Trigger scheduled processing
176
+ setInterval(async () => {
177
+ await client.enqueue({
178
+ method: 'POST',
179
+ path: '/cleanup',
180
+ body: { timestamp: Date.now() },
181
+ });
182
+ }, 60000); // Every minute
183
+ ```
184
+
135
185
  ## Message envelope
136
186
 
137
187
  The SQS record `body` should be a JSON string matching this shape:
@@ -160,7 +210,17 @@ interface QueueEnvelope {
160
210
 
161
211
  The SDK supports automatic handling of large payloads (>256KB) stored in S3, which is necessary since SQS has a 256KB message size limit.
162
212
 
163
- ### Configuration
213
+ The S3 SDK (`@aws-sdk/client-s3`) is included as a dependency, so no additional installation is required.
214
+
215
+ ### Automatic Configuration
216
+
217
+ The SDK will automatically enable S3 support when environment variables are set:
218
+
219
+ - `UNNBOUND_S3_PAYLOAD_BUCKET` or `S3_BUCKET` - S3 bucket name for payload storage
220
+
221
+ No code changes needed! Just set the environment variable and the SDK will handle S3 payloads automatically.
222
+
223
+ ### Manual Configuration
164
224
 
165
225
  Configure S3 support when starting the client:
166
226
 
@@ -191,14 +251,19 @@ await client.sqsListen({
191
251
  The SDK supports two S3 payload formats:
192
252
 
193
253
  1. **ExtendedSQSClient Pointer Format** (AWS Extended Client Library compatible):
254
+
194
255
  ```json
195
- ["software.amazon.payloadoffloading.PayloadS3Pointer", {
196
- "s3BucketName": "bucket-name",
197
- "s3Key": "key-path"
198
- }]
256
+ [
257
+ "software.amazon.payloadoffloading.PayloadS3Pointer",
258
+ {
259
+ "s3BucketName": "bucket-name",
260
+ "s3Key": "key-path"
261
+ }
262
+ ]
199
263
  ```
200
264
 
201
265
  2. **Envelope-based S3 Payloads** (Unnbound format):
266
+
202
267
  ```json
203
268
  {
204
269
  "payload": {
@@ -220,6 +285,7 @@ The SDK supports two S3 payload formats:
220
285
  ### Compression Support
221
286
 
222
287
  The SDK automatically decompresses payloads with:
288
+
223
289
  - `brotli` compression
224
290
  - `gzip` compression
225
291
 
@@ -272,14 +338,3 @@ For S3 payload support, configure:
272
338
  export UNNBOUND_S3_PAYLOAD_BUCKET="my-payload-bucket"
273
339
  export AWS_REGION="us-west-2"
274
340
  ```
275
-
276
- ### AWS Credentials
277
-
278
- When running in ECS with a Task Role, no additional credentials are needed. The AWS SDK will automatically use the ECS Task Role credentials.
279
-
280
- For local development or other environments, set:
281
-
282
- ```bash
283
- export AWS_ACCESS_KEY_ID="your-access-key"
284
- export AWS_SECRET_ACCESS_KEY="your-secret-key"
285
- ```
@@ -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
  }
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.defaultIgnoreTraceRoutes = exports.shouldIgnorePath = void 0;
4
7
  exports.createEventsClient = createEventsClient;
5
8
  const unnbound_logger_sdk_1 = require("unnbound-logger-sdk");
6
- const http = (() => {
7
- try {
8
- return (require && require('http'));
9
- }
10
- catch {
11
- return null;
12
- }
13
- })();
9
+ const internal_1 = require("unnbound-logger-sdk/dist/internal");
10
+ const axios_1 = __importDefault(require("axios"));
11
+ const TRACE_HEADER_KEY = 'x-unnbound-trace-id';
12
+ const MESSAGE_HEADER_KEY = 'x-message-id';
13
+ const http = require('http');
14
14
  function matchPath(matcher, path) {
15
15
  if (typeof matcher === 'string') {
16
16
  return matcher === path;
@@ -23,15 +23,13 @@ function matchPath(matcher, path) {
23
23
  function isMiddleware(value) {
24
24
  return typeof value === 'function' && value.length >= 2;
25
25
  }
26
- const TRACE_HEADER_KEY = 'x-unnbound-trace-id';
27
- const MESSAGE_HEADER_KEY = 'x-message-id';
28
26
  const getHeaderValue = (headers, name) => {
29
27
  const needle = name.toLowerCase();
30
28
  for (const [key, value] of Object.entries(headers)) {
31
29
  if (key.toLowerCase() === needle)
32
30
  return value;
33
31
  }
34
- return undefined;
32
+ return;
35
33
  };
36
34
  const shouldIgnorePath = (path, patterns) => {
37
35
  return patterns.some((pattern) => {
@@ -48,17 +46,7 @@ exports.shouldIgnorePath = shouldIgnorePath;
48
46
  exports.defaultIgnoreTraceRoutes = ['/health', '/healthcheck'];
49
47
  // Helper function to create S3 client
50
48
  function createS3Client(options) {
51
- const awsS3 = (() => {
52
- try {
53
- return require('@aws-sdk/client-s3');
54
- }
55
- catch {
56
- return null;
57
- }
58
- })();
59
- if (!awsS3) {
60
- throw new Error('@aws-sdk/client-s3 is required for S3 payload support');
61
- }
49
+ const awsS3 = require('@aws-sdk/client-s3');
62
50
  const env = globalThis.process
63
51
  ?.env ?? {};
64
52
  const region = options.region || env.AWS_REGION || 'us-east-1';
@@ -76,7 +64,7 @@ async function fetchS3Payload(s3Client, bucket, key, versionId) {
76
64
  params.VersionId = versionId;
77
65
  }
78
66
  try {
79
- const response = await s3Client.send(new awsS3.GetObjectCommand(params));
67
+ const response = (await s3Client.send(new awsS3.GetObjectCommand(params)));
80
68
  const body = response.Body;
81
69
  // Convert stream to string
82
70
  const chunks = [];
@@ -94,17 +82,7 @@ async function fetchS3Payload(s3Client, bucket, key, versionId) {
94
82
  }
95
83
  // Helper function to decompress payload
96
84
  async function decompressPayload(compressed, compressionType) {
97
- const zlib = (() => {
98
- try {
99
- return require('zlib');
100
- }
101
- catch {
102
- return null;
103
- }
104
- })();
105
- if (!zlib) {
106
- throw new Error('zlib is required for decompression');
107
- }
85
+ const zlib = require('zlib');
108
86
  return new Promise((resolve, reject) => {
109
87
  const buffer = Buffer.from(compressed);
110
88
  const callback = (err, result) => {
@@ -126,6 +104,11 @@ async function decompressPayload(compressed, compressionType) {
126
104
  }
127
105
  });
128
106
  }
107
+ const getWorkspaceId = () => process.env.UNNBOUND_WORKFLOW_URL.replace(/^https?:\/\//, '').replace(/\.unnbound\.ai$/, '');
108
+ const getEnvironment = () => process.env.UNNBOUND_WORKFLOW_URL.includes('stg.unnbound.ai') ? 'stg' : 'prod';
109
+ const getWorkflowDomain = () => {
110
+ return getEnvironment() === 'stg' ? 'workflow.stg.unnbound.ai' : 'workflow.unnbound.ai';
111
+ };
129
112
  function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes) {
130
113
  const routes = [];
131
114
  const middlewares = [];
@@ -194,6 +177,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
194
177
  });
195
178
  };
196
179
  middlewares.push({ fn: requestLoggerMiddleware });
180
+ const enqueueClient = (0, unnbound_logger_sdk_1.traceAxios)(axios_1.default.create({ baseURL: `https://${getWorkflowDomain()}/async/${getWorkspaceId()}` }), { getPayload: internal_1.internal });
197
181
  return {
198
182
  on(method, matcher, handler) {
199
183
  routes.push({ method, matcher, handler });
@@ -353,7 +337,8 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
353
337
  try {
354
338
  const parsed = JSON.parse(recordBody);
355
339
  // Detect S3 pointer format: ["software.amazon.payloadoffloading.PayloadS3Pointer", {...}]
356
- if (Array.isArray(parsed) && parsed.length === 2 &&
340
+ if (Array.isArray(parsed) &&
341
+ parsed.length === 2 &&
357
342
  parsed[0] === 'software.amazon.payloadoffloading.PayloadS3Pointer') {
358
343
  const pointer = parsed[1];
359
344
  if (!s3Client) {
@@ -373,7 +358,8 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
373
358
  throw error;
374
359
  }
375
360
  // Check if envelope payload is stored in S3
376
- if (envelope.payload.type === 's3-single' || envelope.payload.type === 's3-multipart') {
361
+ if (envelope.payload.type === 's3-single' ||
362
+ envelope.payload.type === 's3-multipart') {
377
363
  if (!envelope.payload.location) {
378
364
  throw new Error(`Payload type is ${envelope.payload.type} but no location specified`);
379
365
  }
@@ -383,7 +369,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
383
369
  unnbound_logger_sdk_1.logger.info({
384
370
  type: envelope.payload.type,
385
371
  bucket: envelope.payload.location.bucket,
386
- key: envelope.payload.location.key
372
+ key: envelope.payload.location.key,
387
373
  }, 'Fetching S3 envelope payload');
388
374
  let payloadData = await fetchS3Payload(s3Client, envelope.payload.location.bucket, envelope.payload.location.key, envelope.payload.location.versionId);
389
375
  // Handle decompression if needed
@@ -487,24 +473,14 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
487
473
  }
488
474
  }
489
475
  }
490
- const awsSqs = (() => {
491
- try {
492
- return require('@aws-sdk/client-sqs');
493
- }
494
- catch {
495
- return null;
496
- }
497
- })();
498
- if (!awsSqs) {
499
- throw new Error('@aws-sdk/client-sqs is required to use sqsListen');
500
- }
476
+ const awsSqs = require('@aws-sdk/client-sqs');
501
477
  const queueUrl = options?.queueUrl || env.UNNBOUND_SQS_QUEUE_URL || env.SQS_QUEUE_URL || env.QUEUE_URL;
502
478
  if (!queueUrl || queueUrl === undefined) {
503
479
  throw new Error('SQS queue URL not provided. Set UNNBOUND_SQS_QUEUE_URL, SQS_QUEUE_URL env or pass options.queueUrl');
504
480
  }
505
481
  const region = options?.region || env.AWS_REGION || 'us-east-1';
506
482
  const endpoint = env.AWS_SQS_ENDPOINT || env.AWS_ENDPOINT_URL || undefined;
507
- const waitTimeSeconds = options?.waitTimeSeconds ?? 10;
483
+ const waitTimeSeconds = options?.waitTimeSeconds ?? 20;
508
484
  const maxMessages = options?.maxMessages ?? 10;
509
485
  const visibilityTimeout = options?.visibilityTimeoutSeconds;
510
486
  const sqs = new awsSqs.SQSClient({ region, endpoint });
@@ -552,12 +528,12 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
552
528
  return new Promise((resolve, reject) => {
553
529
  const testConnection = async () => {
554
530
  try {
555
- const testParams = {
531
+ const command = new awsSqs.ReceiveMessageCommand({
556
532
  QueueUrl: queueUrl,
557
533
  MaxNumberOfMessages: 1,
558
- WaitTimeSeconds: 0, // Don't wait, just test connection
559
- };
560
- await sqs.send(new awsSqs.ReceiveMessageCommand(testParams));
534
+ WaitTimeSeconds: 0,
535
+ });
536
+ await sqs.send(command);
561
537
  resolve({
562
538
  stop() {
563
539
  stopped = true;
@@ -574,14 +550,35 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
574
550
  setTimeout(testConnection, 100);
575
551
  });
576
552
  },
553
+ async enqueue(event) {
554
+ if (!event.path.startsWith('/'))
555
+ throw new Error('Path must be relative.');
556
+ const config = {
557
+ headers: {
558
+ ...event.headers,
559
+ [TRACE_HEADER_KEY]: event.metadata?.traceId,
560
+ [MESSAGE_HEADER_KEY]: event.metadata?.messageId,
561
+ },
562
+ params: event.query,
563
+ };
564
+ const method = event.method.toLowerCase();
565
+ switch (method) {
566
+ case 'get':
567
+ case 'delete':
568
+ return await enqueueClient[method](event.path, config).then(() => void 0);
569
+ case 'post':
570
+ case 'put':
571
+ case 'patch':
572
+ return await enqueueClient[method](event.path, event.body, config).then(() => void 0);
573
+ }
574
+ throw new Error(`Unsupported method: ${event.method}`);
575
+ },
577
576
  async start(options) {
578
577
  // Default to starting both HTTP and SQS if no options provided
579
578
  const httpOptions = options?.http ?? { port: 3000 };
580
579
  const sqsOptions = options?.sqs ?? {};
581
580
  // Pass S3 options through to SQS listener if provided
582
- const sqsListenOptions = options?.s3
583
- ? { ...sqsOptions, s3: options.s3 }
584
- : sqsOptions;
581
+ const sqsListenOptions = options?.s3 ? { ...sqsOptions, s3: options.s3 } : sqsOptions;
585
582
  // Start HTTP server
586
583
  let httpServer;
587
584
  try {
@@ -87,4 +87,5 @@ export interface EventsClient {
87
87
  stop: () => Promise<void>;
88
88
  }>;
89
89
  start: (options?: StartOptions) => Promise<void>;
90
+ enqueue(event: EventRequest<any>): Promise<void>;
90
91
  }
@@ -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,25 +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.18",
4
+ "version": "1.0.20",
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
- "version:bump": "npm version patch"
22
- },
23
7
  "author": "Unnbound Team",
24
8
  "license": "MIT",
25
9
  "repository": {
@@ -31,20 +15,18 @@
31
15
  "url": "https://github.com/unnbounddev/unnbound-sdks/issues"
32
16
  },
33
17
  "dependencies": {
34
- "express": "^4.0.0 || ^5.0.0",
18
+ "@aws-sdk/client-s3": "^3.0.0",
35
19
  "@aws-sdk/client-sqs": "^3.0.0",
36
- "unnbound-logger-sdk": "^3.0.22"
20
+ "axios": "^1.12.2",
21
+ "express": "^4.0.0 || ^5.0.0",
22
+ "unnbound-logger-sdk": "3.0.22"
37
23
  },
38
24
  "peerDependencies": {
39
- "express": "^4.0.0 || ^5.0.0",
40
- "@aws-sdk/client-s3": "^3.0.0"
25
+ "express": "^4.0.0 || ^5.0.0"
41
26
  },
42
27
  "peerDependenciesMeta": {
43
28
  "express": {
44
29
  "optional": true
45
- },
46
- "@aws-sdk/client-s3": {
47
- "optional": true
48
30
  }
49
31
  },
50
32
  "devDependencies": {
@@ -60,5 +42,20 @@
60
42
  "engines": {
61
43
  "node": ">=22"
62
44
  },
63
- "sideEffects": false
64
- }
45
+ "sideEffects": false,
46
+ "scripts": {
47
+ "build": "tsc",
48
+ "test": "jest",
49
+ "test:coverage": "jest --coverage",
50
+ "test:watch": "jest --watch",
51
+ "typecheck": "tsc -noEmit",
52
+ "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
53
+ "lint:fix": "pnpm lint --fix",
54
+ "format": "prettier --write .",
55
+ "format:check": "prettier --check .",
56
+ "start:example:express": "npx --yes tsx --watch examples/express.ts",
57
+ "start:example:sqs": "npx --yes tsx --watch examples/sqs.ts",
58
+ "start:example:http-and-sqs": "npx --yes tsx --watch examples/http-and-sqs.ts",
59
+ "version:bump": "npm version patch"
60
+ }
61
+ }