@crossdelta/cloudevents 0.1.4 → 0.1.6
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.
|
@@ -29,9 +29,15 @@ const loadHandlers = async (filePath, filter) => {
|
|
|
29
29
|
.filter(([name, handler]) => isValidHandler(handler) && (!filter || filter(name, handler)))
|
|
30
30
|
.map(([name, handler]) => {
|
|
31
31
|
const HandlerClass = handler;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Only override with export name if:
|
|
33
|
+
// 1. Export is named (not 'default')
|
|
34
|
+
// 2. Handler has no meaningful name (empty or generic class names)
|
|
35
|
+
const hasNoMeaningfulName = !HandlerClass.name || HandlerClass.name === '' || HandlerClass.name === 'HandlerClass';
|
|
36
|
+
const isNamedExport = name !== 'default';
|
|
37
|
+
if (hasNoMeaningfulName && isNamedExport) {
|
|
38
|
+
return Object.defineProperty(HandlerClass, 'name', { value: name, configurable: true });
|
|
39
|
+
}
|
|
40
|
+
return HandlerClass;
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
43
|
catch (error) {
|
|
@@ -35,8 +35,12 @@ export function handleEvent(schemaOrOptions, handler, eventType) {
|
|
|
35
35
|
if (!finalEventType) {
|
|
36
36
|
finalEventType = 'unknown.event';
|
|
37
37
|
}
|
|
38
|
-
// Create handler class with proper naming
|
|
39
|
-
const handlerName =
|
|
38
|
+
// Create handler class with proper naming (e.g., "orderboss.orders.created" → "OrdersCreatedHandler")
|
|
39
|
+
const handlerName = finalEventType
|
|
40
|
+
.split('.')
|
|
41
|
+
.slice(-2) // Take last 2 segments (e.g., ["orders", "created"])
|
|
42
|
+
.map(s => s.charAt(0).toUpperCase() + s.slice(1)) // Capitalize
|
|
43
|
+
.join('') + 'Handler';
|
|
40
44
|
const HandlerClass = class extends Object {
|
|
41
45
|
static __eventarcMetadata = {
|
|
42
46
|
schema,
|
|
@@ -4,6 +4,28 @@ import { logger } from '../../infrastructure/logging';
|
|
|
4
4
|
import { processHandler } from '../../processing/handler-cache';
|
|
5
5
|
import { createNatsMessageProcessor } from './nats-message-processor';
|
|
6
6
|
const sc = StringCodec();
|
|
7
|
+
// Use globalThis to persist across hot-reloads
|
|
8
|
+
const CONSUMER_REGISTRY_KEY = '__crossdelta_nats_consumers__';
|
|
9
|
+
function getConsumerRegistry() {
|
|
10
|
+
if (!globalThis[CONSUMER_REGISTRY_KEY]) {
|
|
11
|
+
;
|
|
12
|
+
globalThis[CONSUMER_REGISTRY_KEY] = new Map();
|
|
13
|
+
}
|
|
14
|
+
return globalThis[CONSUMER_REGISTRY_KEY];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Cleanup function to close a consumer by name
|
|
18
|
+
*/
|
|
19
|
+
async function cleanupConsumer(name) {
|
|
20
|
+
const registry = getConsumerRegistry();
|
|
21
|
+
const consumer = registry.get(name);
|
|
22
|
+
if (consumer) {
|
|
23
|
+
logger.info(`[${name}] cleaning up subscription...`);
|
|
24
|
+
consumer.subscription.unsubscribe();
|
|
25
|
+
await consumer.connection.drain();
|
|
26
|
+
registry.delete(name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
7
29
|
/**
|
|
8
30
|
* Connects to NATS, discovers matching event handlers, and processes incoming CloudEvents.
|
|
9
31
|
*
|
|
@@ -17,6 +39,8 @@ export async function consumeNatsEvents(options) {
|
|
|
17
39
|
const servers = options.servers ?? process.env.NATS_URL ?? 'nats://localhost:4222';
|
|
18
40
|
const subject = options.subject;
|
|
19
41
|
const name = options.consumerName ?? `nats-consumer:${subject}`;
|
|
42
|
+
// Cleanup existing consumer with same name (handles hot-reload)
|
|
43
|
+
await cleanupConsumer(name);
|
|
20
44
|
// 1) Discover handler classes from *.event.ts files
|
|
21
45
|
const handlerConstructors = await discoverHandlers(options.discover);
|
|
22
46
|
const processedHandlers = handlerConstructors
|
|
@@ -32,6 +56,8 @@ export async function consumeNatsEvents(options) {
|
|
|
32
56
|
// 3) Subscribe to the subject
|
|
33
57
|
const sub = nc.subscribe(subject);
|
|
34
58
|
logger.info(`[${name}] subscribed to subject: ${subject}`);
|
|
59
|
+
// Track this consumer for cleanup
|
|
60
|
+
getConsumerRegistry().set(name, { subscription: sub, connection: nc });
|
|
35
61
|
const dlqEnabled = Boolean(options.quarantineTopic || options.errorTopic);
|
|
36
62
|
const { handleMessage, handleUnhandledProcessingError } = createNatsMessageProcessor({
|
|
37
63
|
name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crossdelta/cloudevents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "CloudEvents toolkit for TypeScript - handler discovery, DLQ-safe processing, NATS streaming",
|
|
5
5
|
"author": "crossdelta",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
".": {
|
|
28
28
|
"import": "./dist/src/index.js",
|
|
29
29
|
"types": "./dist/src/index.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./transports/nats": {
|
|
32
|
+
"import": "./dist/src/transports/nats/index.js",
|
|
33
|
+
"types": "./dist/src/transports/nats/index.d.ts"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
"publishConfig": {
|