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 +21 -0
- package/dist/events/src/index.d.ts +1 -1
- package/dist/events/src/lib/client.js +10 -62
- package/dist/events/src/lib/types.d.ts +0 -13
- package/dist/lib/client.js +1 -9
- package/dist/logger/src/axios.d.ts +2 -4
- package/dist/logger/src/axios.js +6 -11
- package/dist/logger/src/middleware.d.ts +2 -9
- package/dist/logger/src/middleware.js +8 -12
- package/dist/logger/src/types.d.ts +1 -2
- package/package.json +19 -19
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,
|
|
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
|
|
186
|
+
const AWS = (() => {
|
|
187
187
|
try {
|
|
188
|
-
return require('
|
|
188
|
+
return require('aws-sdk');
|
|
189
189
|
}
|
|
190
190
|
catch {
|
|
191
191
|
return null;
|
|
192
192
|
}
|
|
193
193
|
})();
|
|
194
|
-
if (!
|
|
195
|
-
throw new Error('
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
}
|
package/dist/lib/client.js
CHANGED
|
@@ -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
|
|
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
|
|
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,
|
|
11
|
-
export {};
|
|
9
|
+
export declare const traceAxios: (client: Axios, { ignoreTraceRoutes, traceHeaderKey, }?: HttpOptions) => Axios;
|
package/dist/logger/src/axios.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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 {
|
|
1
|
+
import { RequestHandler } from 'express';
|
|
2
2
|
import { HttpOptions } from './types';
|
|
3
|
-
|
|
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
|
|
36
|
-
status: res.
|
|
37
|
-
body: (0, utils_1.safeJsonParse)(
|
|
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
|
|
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
|
-
|
|
63
|
+
return buildIncomingHttpPayload(req, o
|
|
66
64
|
? {
|
|
67
|
-
|
|
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
|
|
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.
|
|
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": "
|
|
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
|
+
}
|