unnbound-events 1.0.19 → 1.0.21
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 +21 -0
- package/README.md +50 -11
- package/dist/events/src/index.d.ts +9 -1
- package/dist/events/src/index.js +23 -8
- package/dist/events/src/lib/adapters/express.d.ts +12 -10
- package/dist/events/src/lib/adapters/express.js +27 -31
- package/dist/events/src/lib/adapters/sqs.d.ts +27 -27
- package/dist/events/src/lib/adapters/sqs.js +39 -39
- package/dist/events/src/lib/client.js +241 -298
- package/dist/events/src/lib/types.d.ts +45 -58
- package/dist/events/src/lib/types.js +2 -2
- package/dist/lib/client.js +71 -67
- package/dist/lib/types.d.ts +1 -0
- package/dist/logger/src/axios.d.ts +5 -4
- package/dist/logger/src/axios.js +70 -64
- package/dist/logger/src/index.js +50 -14
- package/dist/logger/src/logger.d.ts +18 -12
- package/dist/logger/src/logger.js +41 -39
- package/dist/logger/src/middleware.d.ts +5 -9
- package/dist/logger/src/middleware.js +76 -64
- package/dist/logger/src/span.d.ts +14 -8
- package/dist/logger/src/span.js +35 -28
- package/dist/logger/src/storage.d.ts +2 -2
- package/dist/logger/src/storage.js +3 -3
- package/dist/logger/src/trace.d.ts +8 -2
- package/dist/logger/src/trace.js +12 -5
- package/dist/logger/src/types.d.ts +57 -39
- package/dist/logger/src/types.js +2 -2
- package/dist/logger/src/utils.js +24 -26
- package/package.json +22 -22
|
@@ -1,73 +1,60 @@
|
|
|
1
1
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
|
|
2
2
|
export interface EventRequest<Body = unknown, Query = Record<string, unknown>> {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
method: HttpMethod;
|
|
4
|
+
path: string;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
query: Query;
|
|
7
|
+
body: Body;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
9
|
}
|
|
10
10
|
export interface EventResponse<Body = unknown> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
status: number;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
body?: Body;
|
|
14
14
|
}
|
|
15
|
-
export type RouteHandler<ReqBody = unknown, ResBody = unknown> = (
|
|
15
|
+
export type RouteHandler<ReqBody = unknown, ResBody = unknown> = (
|
|
16
|
+
request: EventRequest<ReqBody>
|
|
17
|
+
) => Promise<EventResponse<ResBody>> | EventResponse<ResBody>;
|
|
16
18
|
export type RouteMatcher = string | RegExp | ((path: string) => boolean);
|
|
17
19
|
export interface RegisteredRoute {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
method: HttpMethod;
|
|
21
|
+
matcher: RouteMatcher;
|
|
22
|
+
handler: RouteHandler<any, any>;
|
|
21
23
|
}
|
|
22
24
|
export type SqsRecord = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
messageId: string;
|
|
26
|
+
body: string;
|
|
25
27
|
};
|
|
26
28
|
export interface SqsBatchEvent {
|
|
27
|
-
|
|
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
|
-
};
|
|
29
|
+
Records: SqsRecord[];
|
|
40
30
|
}
|
|
41
31
|
export interface EventsClient {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
messageId: string;
|
|
60
|
-
reason: string;
|
|
61
|
-
}>;
|
|
62
|
-
}>;
|
|
63
|
-
sqsListen: (options?: {
|
|
64
|
-
queueUrl?: string;
|
|
65
|
-
region?: string;
|
|
66
|
-
waitTimeSeconds?: number;
|
|
67
|
-
maxMessages?: number;
|
|
68
|
-
visibilityTimeoutSeconds?: number;
|
|
69
|
-
}) => Promise<{
|
|
70
|
-
stop: () => Promise<void>;
|
|
32
|
+
on: (method: HttpMethod, matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
33
|
+
handle: (request: EventRequest<any>) => Promise<EventResponse<any>>;
|
|
34
|
+
get: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
35
|
+
post: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
36
|
+
put: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
37
|
+
patch: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
38
|
+
delete: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
39
|
+
options: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
40
|
+
head: (matcher: RouteMatcher, handler: RouteHandler<any, any>) => void;
|
|
41
|
+
http: (options?: { port?: number }) => Promise<{
|
|
42
|
+
close: () => Promise<void>;
|
|
43
|
+
}>;
|
|
44
|
+
sqs: () => (event: SqsBatchEvent) => Promise<{
|
|
45
|
+
successes: number;
|
|
46
|
+
failures: Array<{
|
|
47
|
+
messageId: string;
|
|
48
|
+
reason: string;
|
|
71
49
|
}>;
|
|
72
|
-
|
|
50
|
+
}>;
|
|
51
|
+
sqsListen: (options?: {
|
|
52
|
+
queueUrl?: string;
|
|
53
|
+
region?: string;
|
|
54
|
+
waitTimeSeconds?: number;
|
|
55
|
+
maxMessages?: number;
|
|
56
|
+
visibilityTimeoutSeconds?: number;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
stop: () => Promise<void>;
|
|
59
|
+
}>;
|
|
73
60
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports,
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
package/dist/lib/client.js
CHANGED
|
@@ -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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
})();
|
|
9
|
+
const internal_1 = require("unnbound-logger-sdk/dist/internal");
|
|
10
|
+
const types_1 = require("unnbound-logger-sdk/dist/types");
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const logger = unnbound_logger_sdk_1.logger.child((0, internal_1.internal)());
|
|
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
|
|
32
|
+
return;
|
|
35
33
|
};
|
|
36
34
|
const shouldIgnorePath = (path, patterns) => {
|
|
37
35
|
return patterns.some((pattern) => {
|
|
@@ -77,24 +75,14 @@ async function fetchS3Payload(s3Client, bucket, key, versionId) {
|
|
|
77
75
|
return buffer.toString('utf-8');
|
|
78
76
|
}
|
|
79
77
|
catch (error) {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
throw new Error(`Failed to fetch S3 payload: ${
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
logger.error({ err: error, bucket, key }, 'Failed to fetch S3 payload');
|
|
80
|
+
throw new Error(`Failed to fetch S3 payload: ${message}`);
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
// Helper function to decompress payload
|
|
86
84
|
async function decompressPayload(compressed, compressionType) {
|
|
87
|
-
const zlib = (
|
|
88
|
-
try {
|
|
89
|
-
return require('zlib');
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
})();
|
|
95
|
-
if (!zlib) {
|
|
96
|
-
throw new Error('zlib is required for decompression');
|
|
97
|
-
}
|
|
85
|
+
const zlib = require('zlib');
|
|
98
86
|
return new Promise((resolve, reject) => {
|
|
99
87
|
const buffer = Buffer.from(compressed);
|
|
100
88
|
const callback = (err, result) => {
|
|
@@ -116,6 +104,11 @@ async function decompressPayload(compressed, compressionType) {
|
|
|
116
104
|
}
|
|
117
105
|
});
|
|
118
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
|
+
};
|
|
119
112
|
function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes) {
|
|
120
113
|
const routes = [];
|
|
121
114
|
const middlewares = [];
|
|
@@ -133,12 +126,12 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
133
126
|
}
|
|
134
127
|
const metadataTraceId = typeof request.metadata?.traceId === 'string' ? request.metadata.traceId : undefined;
|
|
135
128
|
const metadataMessageId = typeof request.metadata?.messageId === 'string' ? request.metadata.messageId : undefined;
|
|
136
|
-
const headerMessageId = getHeaderValue(request.headers,
|
|
129
|
+
const headerMessageId = getHeaderValue(request.headers, types_1.defaultTraceHeaderKey);
|
|
137
130
|
if (metadataMessageId && headerMessageId && metadataMessageId !== headerMessageId) {
|
|
138
|
-
|
|
131
|
+
logger.warn({ metadataMessageId, headerMessageId }, 'Message ID mismatch detected; defaulting to header value.');
|
|
139
132
|
}
|
|
140
133
|
const messageId = headerMessageId ?? metadataMessageId;
|
|
141
|
-
const headerTraceId = getHeaderValue(request.headers,
|
|
134
|
+
const headerTraceId = getHeaderValue(request.headers, types_1.defaultMessageHeaderKey);
|
|
142
135
|
const traceId = metadataTraceId ?? headerTraceId;
|
|
143
136
|
const context = traceId || messageId
|
|
144
137
|
? {
|
|
@@ -184,6 +177,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
184
177
|
});
|
|
185
178
|
};
|
|
186
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 });
|
|
187
181
|
return {
|
|
188
182
|
on(method, matcher, handler) {
|
|
189
183
|
routes.push({ method, matcher, handler });
|
|
@@ -241,8 +235,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
241
235
|
return response;
|
|
242
236
|
}
|
|
243
237
|
catch (error) {
|
|
244
|
-
|
|
245
|
-
unnbound_logger_sdk_1.logger.error({ err, path: request.path }, 'Handler error');
|
|
238
|
+
logger.error({ err: error, path: request.path }, 'Handler error');
|
|
246
239
|
return { status: 500, body: { message: 'Internal Server Error' } };
|
|
247
240
|
}
|
|
248
241
|
},
|
|
@@ -308,7 +301,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
308
301
|
});
|
|
309
302
|
});
|
|
310
303
|
await new Promise((resolve) => server.listen(port, resolve));
|
|
311
|
-
|
|
304
|
+
logger.info({ port }, 'HTTP server started');
|
|
312
305
|
return {
|
|
313
306
|
close() {
|
|
314
307
|
return new Promise((resolve, reject) => {
|
|
@@ -350,7 +343,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
350
343
|
if (!s3Client) {
|
|
351
344
|
throw new Error('S3 client not initialized but S3 pointer message received');
|
|
352
345
|
}
|
|
353
|
-
|
|
346
|
+
logger.info({ bucket: pointer.s3BucketName, key: pointer.s3Key }, 'Fetching S3 pointer payload');
|
|
354
347
|
recordBody = await fetchS3Payload(s3Client, pointer.s3BucketName, pointer.s3Key);
|
|
355
348
|
envelope = JSON.parse(recordBody);
|
|
356
349
|
}
|
|
@@ -359,8 +352,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
359
352
|
}
|
|
360
353
|
}
|
|
361
354
|
catch (error) {
|
|
362
|
-
|
|
363
|
-
unnbound_logger_sdk_1.logger.error({ err }, 'Failed to parse SQS record body');
|
|
355
|
+
logger.error({ err: error }, 'Failed to parse SQS record body');
|
|
364
356
|
throw error;
|
|
365
357
|
}
|
|
366
358
|
// Check if envelope payload is stored in S3
|
|
@@ -372,7 +364,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
372
364
|
if (!s3Client) {
|
|
373
365
|
throw new Error('S3 client not initialized but S3 payload message received');
|
|
374
366
|
}
|
|
375
|
-
|
|
367
|
+
logger.info({
|
|
376
368
|
type: envelope.payload.type,
|
|
377
369
|
bucket: envelope.payload.location.bucket,
|
|
378
370
|
key: envelope.payload.location.key,
|
|
@@ -380,7 +372,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
380
372
|
let payloadData = await fetchS3Payload(s3Client, envelope.payload.location.bucket, envelope.payload.location.key, envelope.payload.location.versionId);
|
|
381
373
|
// Handle decompression if needed
|
|
382
374
|
if (envelope.payload.compressed && envelope.payload.compressionType) {
|
|
383
|
-
|
|
375
|
+
logger.info({ compressionType: envelope.payload.compressionType }, 'Decompressing payload');
|
|
384
376
|
payloadData = await decompressPayload(new TextEncoder().encode(payloadData), envelope.payload.compressionType);
|
|
385
377
|
}
|
|
386
378
|
// Parse the payload and set it as the request body
|
|
@@ -392,14 +384,14 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
392
384
|
}
|
|
393
385
|
}
|
|
394
386
|
// Continue with standard envelope processing
|
|
395
|
-
const headerMessageId = getHeaderValue(envelope.request.headers,
|
|
387
|
+
const headerMessageId = getHeaderValue(envelope.request.headers, types_1.defaultMessageHeaderKey);
|
|
396
388
|
const envelopeMessageId = typeof envelope.metadata?.messageId === 'string'
|
|
397
389
|
? envelope.metadata.messageId
|
|
398
390
|
: typeof envelope.messageId === 'string'
|
|
399
391
|
? envelope.messageId
|
|
400
392
|
: undefined;
|
|
401
393
|
if (envelopeMessageId && headerMessageId && envelopeMessageId !== headerMessageId) {
|
|
402
|
-
|
|
394
|
+
logger.warn({ envelopeMessageId, headerMessageId }, 'Message ID mismatch detected in SQS envelope; defaulting to header value.');
|
|
403
395
|
}
|
|
404
396
|
const messageId = headerMessageId ?? envelopeMessageId ?? record.messageId ?? undefined;
|
|
405
397
|
const originalMessageId = envelopeMessageId && envelopeMessageId !== messageId
|
|
@@ -447,10 +439,10 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
447
439
|
}, traceContext);
|
|
448
440
|
}
|
|
449
441
|
catch (error) {
|
|
450
|
-
const
|
|
442
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
451
443
|
failures.push({
|
|
452
444
|
messageId: record.messageId,
|
|
453
|
-
reason:
|
|
445
|
+
reason: reason ?? 'Unknown error',
|
|
454
446
|
});
|
|
455
447
|
}
|
|
456
448
|
}));
|
|
@@ -465,38 +457,27 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
465
457
|
if (bucketName || options?.s3) {
|
|
466
458
|
s3Options = options?.s3 || {};
|
|
467
459
|
if (!bucketName) {
|
|
468
|
-
|
|
460
|
+
logger.error('S3 options provided but no bucket name specified. S3 payload support disabled.');
|
|
469
461
|
}
|
|
470
462
|
else {
|
|
471
463
|
try {
|
|
472
464
|
s3Client = createS3Client({ ...s3Options, bucketName });
|
|
473
|
-
|
|
465
|
+
logger.info({ bucket: bucketName }, 'S3 client initialized for payload offloading');
|
|
474
466
|
}
|
|
475
467
|
catch (error) {
|
|
476
|
-
|
|
477
|
-
unnbound_logger_sdk_1.logger.error({ err }, 'Failed to initialize S3 client');
|
|
468
|
+
logger.error({ err: error }, 'Failed to initialize S3 client');
|
|
478
469
|
throw error;
|
|
479
470
|
}
|
|
480
471
|
}
|
|
481
472
|
}
|
|
482
|
-
const awsSqs = (
|
|
483
|
-
try {
|
|
484
|
-
return require('@aws-sdk/client-sqs');
|
|
485
|
-
}
|
|
486
|
-
catch {
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
489
|
-
})();
|
|
490
|
-
if (!awsSqs) {
|
|
491
|
-
throw new Error('@aws-sdk/client-sqs is required to use sqsListen');
|
|
492
|
-
}
|
|
473
|
+
const awsSqs = require('@aws-sdk/client-sqs');
|
|
493
474
|
const queueUrl = options?.queueUrl || env.UNNBOUND_SQS_QUEUE_URL || env.SQS_QUEUE_URL || env.QUEUE_URL;
|
|
494
475
|
if (!queueUrl || queueUrl === undefined) {
|
|
495
476
|
throw new Error('SQS queue URL not provided. Set UNNBOUND_SQS_QUEUE_URL, SQS_QUEUE_URL env or pass options.queueUrl');
|
|
496
477
|
}
|
|
497
478
|
const region = options?.region || env.AWS_REGION || 'us-east-1';
|
|
498
479
|
const endpoint = env.AWS_SQS_ENDPOINT || env.AWS_ENDPOINT_URL || undefined;
|
|
499
|
-
const waitTimeSeconds = options?.waitTimeSeconds ??
|
|
480
|
+
const waitTimeSeconds = options?.waitTimeSeconds ?? 20;
|
|
500
481
|
const maxMessages = options?.maxMessages ?? 10;
|
|
501
482
|
const visibilityTimeout = options?.visibilityTimeoutSeconds;
|
|
502
483
|
const sqs = new awsSqs.SQSClient({ region, endpoint });
|
|
@@ -534,7 +515,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
534
515
|
}
|
|
535
516
|
}
|
|
536
517
|
catch (err) {
|
|
537
|
-
|
|
518
|
+
logger.error({ err }, 'SQS listen loop error');
|
|
538
519
|
}
|
|
539
520
|
}
|
|
540
521
|
}
|
|
@@ -544,12 +525,12 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
544
525
|
return new Promise((resolve, reject) => {
|
|
545
526
|
const testConnection = async () => {
|
|
546
527
|
try {
|
|
547
|
-
const
|
|
528
|
+
const command = new awsSqs.ReceiveMessageCommand({
|
|
548
529
|
QueueUrl: queueUrl,
|
|
549
530
|
MaxNumberOfMessages: 1,
|
|
550
|
-
WaitTimeSeconds: 0,
|
|
551
|
-
};
|
|
552
|
-
await sqs.send(
|
|
531
|
+
WaitTimeSeconds: 0,
|
|
532
|
+
});
|
|
533
|
+
await sqs.send(command);
|
|
553
534
|
resolve({
|
|
554
535
|
stop() {
|
|
555
536
|
stopped = true;
|
|
@@ -558,7 +539,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
558
539
|
});
|
|
559
540
|
}
|
|
560
541
|
catch (error) {
|
|
561
|
-
|
|
542
|
+
logger.error({ err: error, queueUrl: queueUrl.replace(/\/[^/]+$/, '/***') }, 'SQS connection test failed');
|
|
562
543
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
563
544
|
}
|
|
564
545
|
};
|
|
@@ -566,6 +547,29 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
566
547
|
setTimeout(testConnection, 100);
|
|
567
548
|
});
|
|
568
549
|
},
|
|
550
|
+
async enqueue(event) {
|
|
551
|
+
if (!event.path.startsWith('/'))
|
|
552
|
+
throw new Error('Path must be relative.');
|
|
553
|
+
const config = {
|
|
554
|
+
headers: {
|
|
555
|
+
...event.headers,
|
|
556
|
+
[types_1.defaultTraceHeaderKey]: event.metadata?.traceId,
|
|
557
|
+
[types_1.defaultMessageHeaderKey]: event.metadata?.messageId,
|
|
558
|
+
},
|
|
559
|
+
params: event.query,
|
|
560
|
+
};
|
|
561
|
+
const method = event.method.toLowerCase();
|
|
562
|
+
switch (method) {
|
|
563
|
+
case 'get':
|
|
564
|
+
case 'delete':
|
|
565
|
+
return await enqueueClient[method](event.path, config).then(() => void 0);
|
|
566
|
+
case 'post':
|
|
567
|
+
case 'put':
|
|
568
|
+
case 'patch':
|
|
569
|
+
return await enqueueClient[method](event.path, event.body, config).then(() => void 0);
|
|
570
|
+
}
|
|
571
|
+
throw new Error(`Unsupported method: ${event.method}`);
|
|
572
|
+
},
|
|
569
573
|
async start(options) {
|
|
570
574
|
// Default to starting both HTTP and SQS if no options provided
|
|
571
575
|
const httpOptions = options?.http ?? { port: 3000 };
|
|
@@ -578,7 +582,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
578
582
|
httpServer = await this.http(httpOptions);
|
|
579
583
|
}
|
|
580
584
|
catch (error) {
|
|
581
|
-
|
|
585
|
+
logger.error({ err: error }, 'Failed to start HTTP server, continuing without it');
|
|
582
586
|
}
|
|
583
587
|
// Start SQS listener
|
|
584
588
|
let sqsListener;
|
|
@@ -586,7 +590,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
586
590
|
sqsListener = await this.sqsListen(sqsListenOptions);
|
|
587
591
|
}
|
|
588
592
|
catch (error) {
|
|
589
|
-
|
|
593
|
+
logger.error({ err: error }, 'Failed to start SQS listener, continuing without it');
|
|
590
594
|
}
|
|
591
595
|
// If neither HTTP nor SQS started successfully, throw an error
|
|
592
596
|
if (!httpServer && !sqsListener) {
|
|
@@ -595,14 +599,14 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
595
599
|
// Set up graceful shutdown
|
|
596
600
|
const g = globalThis;
|
|
597
601
|
const shutdown = async () => {
|
|
598
|
-
|
|
602
|
+
logger.info('Received shutdown signal, closing servers...');
|
|
599
603
|
const promises = [];
|
|
600
604
|
if (httpServer)
|
|
601
605
|
promises.push(httpServer.close());
|
|
602
606
|
if (sqsListener)
|
|
603
607
|
promises.push(sqsListener.stop());
|
|
604
608
|
await Promise.all(promises);
|
|
605
|
-
|
|
609
|
+
logger.info('All servers closed');
|
|
606
610
|
g.process?.exit?.(0);
|
|
607
611
|
};
|
|
608
612
|
// Handle SIGINT and SIGTERM
|
|
@@ -614,7 +618,7 @@ function createEventsClient(ignoreTraceRoutes = exports.defaultIgnoreTraceRoutes
|
|
|
614
618
|
services.push(`HTTP on port ${httpOptions?.port ?? 3000}`);
|
|
615
619
|
if (sqsListener)
|
|
616
620
|
services.push('SQS listener');
|
|
617
|
-
|
|
621
|
+
logger.info({ services: services.join(' and ') }, 'Services started. Press Ctrl+C to stop.');
|
|
618
622
|
},
|
|
619
623
|
};
|
|
620
624
|
}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { 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: (
|
|
11
|
-
|
|
9
|
+
export declare const traceAxios: (
|
|
10
|
+
client: Axios,
|
|
11
|
+
{ ignoreTraceRoutes, traceHeaderKey }?: HttpOptions
|
|
12
|
+
) => Axios;
|
package/dist/logger/src/axios.js
CHANGED
|
@@ -1,79 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports,
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
3
|
exports.traceAxios = void 0;
|
|
4
|
-
const axios_1 = require(
|
|
5
|
-
const types_1 = require(
|
|
6
|
-
const storage_1 = require(
|
|
7
|
-
const utils_1 = require(
|
|
8
|
-
const span_1 = require(
|
|
4
|
+
const axios_1 = require('axios');
|
|
5
|
+
const types_1 = require('./types');
|
|
6
|
+
const storage_1 = require('./storage');
|
|
7
|
+
const utils_1 = require('./utils');
|
|
8
|
+
const span_1 = require('./span');
|
|
9
9
|
const buildOutgoingHttpPayload = (config, res) => ({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
response: res
|
|
20
|
-
? {
|
|
21
|
-
headers: res?.headers,
|
|
22
|
-
status: res.status,
|
|
23
|
-
body: (0, utils_1.safeJsonParse)(res.data),
|
|
24
|
-
}
|
|
25
|
-
: undefined,
|
|
10
|
+
type: 'http',
|
|
11
|
+
http: {
|
|
12
|
+
url: [config.baseURL, config.url].filter(Boolean).join(''),
|
|
13
|
+
method: config.method?.toLowerCase() ?? 'get',
|
|
14
|
+
incoming: false,
|
|
15
|
+
request: {
|
|
16
|
+
headers: config.headers,
|
|
17
|
+
body: (0, utils_1.safeJsonParse)(config.data),
|
|
26
18
|
},
|
|
19
|
+
response: res
|
|
20
|
+
? {
|
|
21
|
+
headers: res?.headers,
|
|
22
|
+
status: res.status,
|
|
23
|
+
body: (0, utils_1.safeJsonParse)(res.data),
|
|
24
|
+
}
|
|
25
|
+
: undefined,
|
|
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 = (
|
|
34
|
+
const traceAxios = (
|
|
35
|
+
client,
|
|
36
|
+
{
|
|
37
|
+
ignoreTraceRoutes = types_1.defaultIgnoreTraceRoutes,
|
|
38
|
+
traceHeaderKey = types_1.defaultTraceHeaderKey,
|
|
39
|
+
} = {
|
|
36
40
|
ignoreTraceRoutes: types_1.defaultIgnoreTraceRoutes,
|
|
37
41
|
traceHeaderKey: types_1.defaultTraceHeaderKey,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
42
|
+
}
|
|
43
|
+
) => {
|
|
44
|
+
const createSpanWrappedRequest = (originalMethod, method) => {
|
|
45
|
+
const { headers: defaultHeaders, ...partialDefaultConfig } = client.defaults;
|
|
46
|
+
const headers = { ...defaultHeaders.common, ...(method && defaultHeaders[method]) };
|
|
47
|
+
const defaultConfig = { ...partialDefaultConfig, headers };
|
|
48
|
+
return async (config) => {
|
|
49
|
+
config = (0, axios_1.mergeConfig)(defaultConfig, config);
|
|
50
|
+
// Determine the actual config and URL
|
|
51
|
+
const url = [config.baseURL, config.url].filter(Boolean).join('');
|
|
52
|
+
// Check if this request should be ignored
|
|
53
|
+
if ((0, utils_1.shouldIgnorePath)(url, ignoreTraceRoutes)) return originalMethod(config);
|
|
54
|
+
const traceId = storage_1.storage.getStore()?.traceId;
|
|
55
|
+
config = { ...config, headers: { ...config.headers, [traceHeaderKey]: traceId } };
|
|
56
|
+
// Execute the request within a span
|
|
57
|
+
return (0, span_1.startSpan)(
|
|
58
|
+
`Outgoing HTTP request`,
|
|
59
|
+
() => originalMethod(config),
|
|
60
|
+
(options) => {
|
|
61
|
+
if (!options) return buildOutgoingHttpPayload(config);
|
|
62
|
+
if (options.error)
|
|
63
|
+
return buildOutgoingHttpPayload(
|
|
64
|
+
config,
|
|
65
|
+
(0, axios_1.isAxiosError)(options.error) ? options.error.response : undefined
|
|
66
|
+
);
|
|
67
|
+
return buildOutgoingHttpPayload(config, options.result);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
65
70
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
71
|
+
};
|
|
72
|
+
const request = client.request.bind(client);
|
|
73
|
+
const tracedRequest = createSpanWrappedRequest(request);
|
|
74
|
+
client.request = tracedRequest;
|
|
75
|
+
['post', 'put', 'patch'].forEach((method) => {
|
|
76
|
+
const func = createSpanWrappedRequest(request, method);
|
|
77
|
+
client[method] = (url, data, config) => func({ ...config, method, url, data });
|
|
78
|
+
});
|
|
79
|
+
['delete', 'get', 'head', 'options'].forEach((method) => {
|
|
80
|
+
const func = createSpanWrappedRequest(request, method);
|
|
81
|
+
client[method] = (url, config) => func({ ...config, method, url });
|
|
82
|
+
});
|
|
83
|
+
return client;
|
|
78
84
|
};
|
|
79
85
|
exports.traceAxios = traceAxios;
|