@kronos-ts/messaging 0.5.1 → 0.7.0

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.
Files changed (69) hide show
  1. package/dist/command-handling-module.d.ts.map +1 -1
  2. package/dist/command-handling-module.js +4 -12
  3. package/dist/command-handling-module.js.map +1 -1
  4. package/dist/correlation-data.d.ts +38 -8
  5. package/dist/correlation-data.d.ts.map +1 -1
  6. package/dist/correlation-data.js +57 -20
  7. package/dist/correlation-data.js.map +1 -1
  8. package/dist/event-gateway.d.ts.map +1 -1
  9. package/dist/event-gateway.js +1 -0
  10. package/dist/event-gateway.js.map +1 -1
  11. package/dist/gateway.d.ts.map +1 -1
  12. package/dist/gateway.js +3 -0
  13. package/dist/gateway.js.map +1 -1
  14. package/dist/handler-enhancer.d.ts +2 -1
  15. package/dist/handler-enhancer.d.ts.map +1 -1
  16. package/dist/handler-enhancer.js.map +1 -1
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/message.d.ts +18 -0
  22. package/dist/message.d.ts.map +1 -1
  23. package/dist/metrics.d.ts +56 -0
  24. package/dist/metrics.d.ts.map +1 -0
  25. package/dist/metrics.js +79 -0
  26. package/dist/metrics.js.map +1 -0
  27. package/dist/send.d.ts.map +1 -1
  28. package/dist/send.js +1 -0
  29. package/dist/send.js.map +1 -1
  30. package/dist/span-factory.d.ts +32 -1
  31. package/dist/span-factory.d.ts.map +1 -1
  32. package/dist/span-factory.js +3 -0
  33. package/dist/span-factory.js.map +1 -1
  34. package/dist/streaming-event-processor.d.ts +7 -0
  35. package/dist/streaming-event-processor.d.ts.map +1 -1
  36. package/dist/streaming-event-processor.js +7 -1
  37. package/dist/streaming-event-processor.js.map +1 -1
  38. package/dist/subscribing-event-processor.d.ts +8 -0
  39. package/dist/subscribing-event-processor.d.ts.map +1 -1
  40. package/dist/subscribing-event-processor.js +7 -1
  41. package/dist/subscribing-event-processor.js.map +1 -1
  42. package/dist/tracing-command-bus.d.ts +8 -5
  43. package/dist/tracing-command-bus.d.ts.map +1 -1
  44. package/dist/tracing-command-bus.js +21 -19
  45. package/dist/tracing-command-bus.js.map +1 -1
  46. package/dist/tracing-handler-enhancer.d.ts +8 -2
  47. package/dist/tracing-handler-enhancer.d.ts.map +1 -1
  48. package/dist/tracing-handler-enhancer.js +45 -4
  49. package/dist/tracing-handler-enhancer.js.map +1 -1
  50. package/dist/tracking-event-processor.d.ts +7 -0
  51. package/dist/tracking-event-processor.d.ts.map +1 -1
  52. package/dist/tracking-event-processor.js +10 -1
  53. package/dist/tracking-event-processor.js.map +1 -1
  54. package/package.json +8 -3
  55. package/src/command-handling-module.ts +4 -12
  56. package/src/correlation-data.ts +67 -25
  57. package/src/event-gateway.ts +1 -0
  58. package/src/gateway.ts +3 -0
  59. package/src/handler-enhancer.ts +3 -1
  60. package/src/index.ts +14 -0
  61. package/src/message.ts +23 -2
  62. package/src/metrics.ts +131 -0
  63. package/src/send.ts +1 -0
  64. package/src/span-factory.ts +37 -1
  65. package/src/streaming-event-processor.ts +13 -0
  66. package/src/subscribing-event-processor.ts +14 -0
  67. package/src/tracing-command-bus.ts +23 -19
  68. package/src/tracing-handler-enhancer.ts +56 -5
  69. package/src/tracking-event-processor.ts +16 -0
@@ -1,18 +1,26 @@
1
1
  import type { CommandBus } from "./command-bus.js"
2
2
  import type { CommandMessage } from "./message.js"
3
- import type { SpanFactory } from "./span-factory.js"
3
+ import type { SpanFactory, Span } from "./span-factory.js"
4
+
5
+ /** Run `fn` with `span` active, falling back to a plain call when the span lacks runActive. */
6
+ function runActive<R>(span: Span, fn: () => R): R {
7
+ return span.runActive ? span.runActive(fn) : fn()
8
+ }
4
9
 
5
10
  /**
6
- * A {@link CommandBus} decorator that wraps dispatch and handler invocations
7
- * with tracing spans.
11
+ * A {@link CommandBus} decorator that traces command dispatch (the producer
12
+ * side).
8
13
  *
9
14
  * Dispatch creates a "dispatch" span and propagates trace context into the
10
- * message metadata. Subscribe wraps each handler with a "handle" span.
11
- *
15
+ * message metadata, so the handler links back to it across the bus boundary.
16
+ * The handler ("handle") span is created by {@link tracingHandlerEnhancerDefinition},
17
+ * the single authority for handler-side spans across command/query/event
18
+ * handlers — so this decorator does not wrap subscribe, avoiding a duplicate
19
+ * command handle span.
12
20
  *
13
21
  * @param delegate The underlying command bus to decorate.
14
22
  * @param spanFactory The span factory for creating tracing spans.
15
- * @returns A decorated command bus with tracing instrumentation.
23
+ * @returns A decorated command bus with dispatch tracing.
16
24
  */
17
25
  export function createTracingCommandBus(
18
26
  delegate: CommandBus,
@@ -22,8 +30,12 @@ export function createTracingCommandBus(
22
30
  async dispatch(message: CommandMessage): Promise<unknown> {
23
31
  const span = spanFactory.createDispatchSpan(`dispatch(${String(message.name)})`, message).start()
24
32
  try {
25
- const propagated = spanFactory.propagateContext(message)
26
- const result = await delegate.dispatch(propagated)
33
+ // Propagate inside the active dispatch span so the outgoing message
34
+ // carries this span's trace context and the handler links to it.
35
+ const result = await runActive(span, () => {
36
+ const propagated = spanFactory.propagateContext(message)
37
+ return delegate.dispatch(propagated)
38
+ })
27
39
  span.end()
28
40
  return result
29
41
  } catch (err) {
@@ -36,17 +48,9 @@ export function createTracingCommandBus(
36
48
  commandName: string,
37
49
  handler: (message: CommandMessage) => Promise<unknown>,
38
50
  ): void {
39
- delegate.subscribe(commandName, async (msg: CommandMessage) => {
40
- const span = spanFactory.createHandlerSpan(`handle(${commandName})`, msg).start()
41
- try {
42
- const result = await handler(msg)
43
- span.end()
44
- return result
45
- } catch (err) {
46
- span.recordException(err instanceof Error ? err : new Error(String(err)))
47
- throw err
48
- }
49
- })
51
+ // Handler-side spans are owned by tracingHandlerEnhancerDefinition; pass
52
+ // the handler through untouched so commands get exactly one handle span.
53
+ delegate.subscribe(commandName, handler)
50
54
  },
51
55
  }
52
56
  }
@@ -1,12 +1,20 @@
1
1
  import type { HandlerEnhancerDefinition, HandlerMetadata } from "./handler-enhancer.js"
2
- import type { SpanFactory } from "./span-factory.js"
2
+ import type { SpanFactory, Span } from "./span-factory.js"
3
+ import type { Message } from "./message.js"
4
+ import { contributeCorrelationData } from "./correlation-data.js"
3
5
 
4
6
  /**
5
7
  * Handler enhancer that wraps message handler invocations with tracing spans.
6
8
  *
7
- * Creates an internal span per handler invocation, recording the handler
8
- * name and message type as context. Errors are recorded on the span.
9
+ * The span is created from the message being handled (extracting any trace
10
+ * context from its metadata) so the handler re-parents onto the dispatcher's
11
+ * trace across the message boundary. Event handlers start a new trace linked to
12
+ * the triggering event; command/query handlers continue the current trace.
9
13
  *
14
+ * The handler runs inside the span's active context, and the active trace
15
+ * context is captured onto the UnitOfWork (via contributeCorrelationData) so
16
+ * appended and dispatched messages carry it — including events published at
17
+ * commit time, after the span has ended.
10
18
  */
11
19
  export function tracingHandlerEnhancerDefinition(
12
20
  spanFactory: SpanFactory,
@@ -19,9 +27,27 @@ export function tracingHandlerEnhancerDefinition(
19
27
  const spanName = `${metadata.handlerGroup}.${metadata.messageName}`
20
28
 
21
29
  return (async (...args: any[]) => {
22
- const span = spanFactory.createInternalSpan(spanName).start()
30
+ const message = args[0]
31
+ const span = createSpan(spanFactory, spanName, message, metadata).start()
32
+ const runActive: <R>(fn: () => R) => R = span.runActive
33
+ ? span.runActive.bind(span)
34
+ : (fn) => fn()
35
+
23
36
  try {
24
- const result = await handler(...args)
37
+ const result = await runActive(() => {
38
+ // Store the active trace context on the UnitOfWork so outgoing and
39
+ // appended messages carry it. Best-effort: tracing must never break
40
+ // handling, and there may be no active UnitOfWork.
41
+ try {
42
+ const traceContext = spanFactory.currentTraceContext?.()
43
+ if (traceContext && Object.keys(traceContext).length > 0) {
44
+ contributeCorrelationData(traceContext)
45
+ }
46
+ } catch {
47
+ // no active UnitOfWork or no tracing context — skip
48
+ }
49
+ return handler(...args)
50
+ })
25
51
  span.end()
26
52
  return result
27
53
  } catch (error) {
@@ -32,3 +58,28 @@ export function tracingHandlerEnhancerDefinition(
32
58
  },
33
59
  }
34
60
  }
61
+
62
+ function isMessage(value: unknown): value is Message {
63
+ return (
64
+ typeof value === "object" &&
65
+ value !== null &&
66
+ "metadata" in value &&
67
+ "identifier" in value
68
+ )
69
+ }
70
+
71
+ function createSpan(
72
+ spanFactory: SpanFactory,
73
+ spanName: string,
74
+ message: unknown,
75
+ metadata: HandlerMetadata,
76
+ ): Span {
77
+ if (!isMessage(message)) {
78
+ // No message to re-parent from (defensive — wired handlers always receive one).
79
+ return spanFactory.createInternalSpan(spanName)
80
+ }
81
+ if (metadata.messageType === "event" && spanFactory.createLinkedHandlerSpan) {
82
+ return spanFactory.createLinkedHandlerSpan(spanName, message)
83
+ }
84
+ return spanFactory.createHandlerSpan(spanName, message)
85
+ }
@@ -22,6 +22,7 @@ import {
22
22
  advanceToken,
23
23
  } from "./tracking-token.js"
24
24
  import { REPLAY_STATE_KEY } from "./replay-token.js"
25
+ import { applyCorrelationData, type CorrelationDataProvider } from "./correlation-data.js"
25
26
  import { setResource, onPrepareCommit } from "./processing-state.js"
26
27
  import type { HandlerEnhancerDefinition } from "./handler-enhancer.js"
27
28
  import type { CommandBus } from "./command-bus.js"
@@ -76,6 +77,12 @@ export interface TrackingEventProcessorOptions {
76
77
  queryBus?: QueryBus
77
78
  /** Event scheduler injected into ALS at handler-invocation entry (read by schedule()). */
78
79
  eventScheduler?: EventScheduler
80
+ /**
81
+ * Correlation data providers run against each event before its handlers are
82
+ * invoked, so commands/events dispatched from an event handler inherit the
83
+ * triggering event's correlationId/causationId.
84
+ */
85
+ correlationDataProviders?: ReadonlyArray<CorrelationDataProvider>
79
86
  /** Optional per-event callback fired inside the UoW before handler invocation (e.g. monitoring). */
80
87
  onEventDelivery?: () => void
81
88
  unitOfWorkRunner?: UoWRunner
@@ -151,6 +158,7 @@ export function createTrackingEventProcessor(
151
158
  commandBus,
152
159
  queryBus,
153
160
  eventScheduler,
161
+ correlationDataProviders,
154
162
  onEventDelivery,
155
163
  unitOfWorkRunner = runInNewUoW,
156
164
  tokenStore,
@@ -341,6 +349,11 @@ export function createTrackingEventProcessor(
341
349
  if (commandBus !== undefined) setResource(COMMAND_BUS_KEY, commandBus)
342
350
  if (queryBus !== undefined) setResource(QUERY_BUS_KEY, queryBus)
343
351
  if (eventScheduler !== undefined) setResource(EVENT_SCHEDULER_KEY, eventScheduler)
352
+ // Seed correlation data from the triggering event so an automation's
353
+ // outgoing commands/events inherit its lineage.
354
+ if (correlationDataProviders && correlationDataProviders.length > 0) {
355
+ applyCorrelationData(event, correlationDataProviders)
356
+ }
344
357
  // Optional per-event callback (e.g. monitoring hooks registered inside the UoW).
345
358
  if (onEventDelivery) onEventDelivery()
346
359
 
@@ -375,6 +388,9 @@ export function createTrackingEventProcessor(
375
388
  if (commandBus !== undefined) setResource(COMMAND_BUS_KEY, commandBus)
376
389
  if (queryBus !== undefined) setResource(QUERY_BUS_KEY, queryBus)
377
390
  if (eventScheduler !== undefined) setResource(EVENT_SCHEDULER_KEY, eventScheduler)
391
+ if (correlationDataProviders && correlationDataProviders.length > 0) {
392
+ applyCorrelationData(event, correlationDataProviders)
393
+ }
378
394
 
379
395
  const position =
380
396
  typeof letter.diagnostics.position === "number" ? BigInt(letter.diagnostics.position) : 0n