@zlink-systems/framework 0.31.0 → 0.32.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 (171) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
  3. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
  4. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
  5. package/dist/contracts/Actors/index.d.ts +1 -0
  6. package/dist/contracts/Actors/index.js +1 -0
  7. package/dist/contracts/Common/payload-slab.d.ts +4 -0
  8. package/dist/contracts/Common/payload-slab.js +30 -0
  9. package/dist/contracts/Handlers/Contexts.d.ts +1 -0
  10. package/dist/internal.d.ts +9 -0
  11. package/dist/internal.js +23 -1
  12. package/dist/runtime/actors/actor-client.d.ts +1 -1
  13. package/dist/runtime/actors/actor-client.js +32 -23
  14. package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
  15. package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
  16. package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
  17. package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
  18. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
  19. package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
  20. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  21. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
  22. package/dist/runtime/backend/mesh-completion-table.js +1 -0
  23. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
  24. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
  25. package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
  26. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
  28. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
  29. package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
  30. package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
  31. package/dist/runtime/channels/channel-dispatchers.js +27 -19
  32. package/dist/runtime/channels/channel-envelope.js +543 -23
  33. package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
  34. package/dist/runtime/channels/channel-receive-loops.js +59 -18
  35. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
  36. package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
  37. package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
  38. package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
  39. package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
  40. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
  41. package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
  42. package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
  43. package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
  44. package/dist/runtime/foundation/service-mailbox.js +4 -1
  45. package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
  46. package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
  47. package/dist/runtime/foundation/service-stateful-registry.js +28 -12
  48. package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
  49. package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
  50. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
  51. package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
  52. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  53. package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
  54. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
  55. package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
  56. package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
  57. package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
  58. package/dist/runtime/host/application-job-queue.d.ts +1 -1
  59. package/dist/runtime/host/index.d.ts +89 -36
  60. package/dist/runtime/host/index.js +101 -7
  61. package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
  62. package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
  63. package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
  64. package/dist/runtime/host/spot-address-transport.js +50 -42
  65. package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
  66. package/dist/runtime/locations/auto-connect-loop.js +25 -19
  67. package/dist/runtime/locations/auto-connect-planner.js +2 -5
  68. package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
  69. package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
  70. package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
  71. package/dist/runtime/locations/in-memory-location-store.js +1 -38
  72. package/dist/runtime/locations/location-store-repository.d.ts +0 -1
  73. package/dist/runtime/locations/location-store-repository.js +13 -115
  74. package/dist/runtime/locations/runtime.d.ts +3 -3
  75. package/dist/runtime/locations/runtime.js +12 -16
  76. package/dist/runtime/messaging/creation-payload-codec.js +2 -2
  77. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
  78. package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
  79. package/dist/runtime/spots/index.d.ts +20 -5
  80. package/dist/runtime/spots/index.js +14 -0
  81. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
  82. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
  83. package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
  84. package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
  85. package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
  86. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
  87. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
  88. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
  89. package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
  90. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
  91. package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
  92. package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
  93. package/dist/runtime/spots/spot-timer.d.ts +1 -1
  94. package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
  95. package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
  96. package/dist/runtime/streams/index.d.ts +3 -3
  97. package/dist/runtime/streams/index.js +22 -2
  98. package/dist/runtime/streams/managed-stream.js +2 -1
  99. package/dist/runtime/streams/session-calls.d.ts +1 -1
  100. package/dist/runtime/streams/session-calls.js +3 -9
  101. package/dist/runtime/streams/session-context.d.ts +2 -2
  102. package/dist/runtime/streams/session-context.js +2 -2
  103. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
  104. package/dist/runtime/streams/stream-session-runtime.js +37 -12
  105. package/package.json +3 -3
  106. package/src/contracts/Actors/ZLinkActor.ts +6 -0
  107. package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
  108. package/src/contracts/Actors/index.ts +1 -0
  109. package/src/contracts/Common/payload-slab.ts +29 -0
  110. package/src/contracts/Handlers/Contexts.ts +1 -0
  111. package/src/internal.ts +17 -0
  112. package/src/runtime/actors/actor-client.ts +48 -23
  113. package/src/runtime/actors/actor-mailbox.ts +1 -1
  114. package/src/runtime/actors/actor-runtime-state.ts +1 -1
  115. package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
  116. package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
  117. package/src/runtime/backend/contracts/index.ts +2 -0
  118. package/src/runtime/backend/mesh-completion-table.ts +2 -0
  119. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
  120. package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
  121. package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
  122. package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
  123. package/src/runtime/channels/channel-dispatchers.ts +39 -19
  124. package/src/runtime/channels/channel-envelope.ts +547 -20
  125. package/src/runtime/channels/channel-receive-loops.ts +93 -36
  126. package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
  127. package/src/runtime/channels/channel-socket-registry.ts +3 -3
  128. package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
  129. package/src/runtime/execution/serial-execution-queue.ts +13 -13
  130. package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
  131. package/src/runtime/foundation/service-liveness-registry.ts +1 -1
  132. package/src/runtime/foundation/service-mailbox.ts +6 -1
  133. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  134. package/src/runtime/foundation/service-stateful-registry.ts +39 -14
  135. package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
  136. package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
  137. package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
  138. package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
  139. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  140. package/src/runtime/host/actor-transfer-runtime.ts +3 -3
  141. package/src/runtime/host/application-job-queue.ts +1 -1
  142. package/src/runtime/host/index.ts +183 -44
  143. package/src/runtime/host/location-runtime-owner.ts +2 -2
  144. package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
  145. package/src/runtime/host/spot-address-transport.ts +67 -61
  146. package/src/runtime/locations/auto-connect-loop.ts +24 -16
  147. package/src/runtime/locations/auto-connect-planner.ts +2 -5
  148. package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
  149. package/src/runtime/locations/in-memory-location-store.ts +1 -74
  150. package/src/runtime/locations/location-store-repository.ts +32 -153
  151. package/src/runtime/locations/runtime.ts +16 -28
  152. package/src/runtime/messaging/creation-payload-codec.ts +3 -3
  153. package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
  154. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  155. package/src/runtime/spots/index.ts +28 -4
  156. package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
  157. package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
  158. package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
  159. package/src/runtime/spots/spot-entry-activation.ts +1 -1
  160. package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
  161. package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
  162. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
  163. package/src/runtime/spots/spot-serial-executor.ts +1 -1
  164. package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
  165. package/src/runtime/spots/spot-timer.ts +1 -1
  166. package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
  167. package/src/runtime/streams/index.ts +30 -4
  168. package/src/runtime/streams/managed-stream.ts +2 -1
  169. package/src/runtime/streams/session-calls.ts +9 -14
  170. package/src/runtime/streams/session-context.ts +6 -11
  171. package/src/runtime/streams/stream-session-runtime.ts +49 -20
@@ -42,6 +42,13 @@ import type {
42
42
  ZLinkRawRouterRoute,
43
43
  ZLinkRawSocketPort
44
44
  } from '../raw-binding-port';
45
+
46
+ interface ZLinkNodeRawRouterSocketOptions {
47
+ readonly sendHighWaterMark?: number;
48
+ readonly receiveHighWaterMark?: number;
49
+ readonly receiveTimeoutMs?: number;
50
+ }
51
+
45
52
  export type {
46
53
  ZLinkRawBindingPort,
47
54
  ZLinkRawDealerPort,
@@ -57,14 +64,14 @@ export type {
57
64
  export class ZLinkNodeRawBindingPort implements ZLinkRawBindingPort {
58
65
  constructor(
59
66
  private readonly sharedContext?: Context,
60
- private readonly routerReceiveTimeoutMs?: number
67
+ private readonly routerSocketOptions?: ZLinkNodeRawRouterSocketOptions
61
68
  ) {}
62
69
 
63
70
  createHost(): ZLinkRawHostPort {
64
71
  return new NodeRawHostPort(
65
72
  this.sharedContext ?? createContext(),
66
73
  this.sharedContext === undefined,
67
- this.routerReceiveTimeoutMs
74
+ this.routerSocketOptions
68
75
  );
69
76
  }
70
77
  }
@@ -76,13 +83,13 @@ class NodeRawHostPort implements ZLinkRawHostPort {
76
83
  constructor(
77
84
  private readonly context: Context,
78
85
  private readonly ownsContext: boolean,
79
- private readonly routerReceiveTimeoutMs?: number
86
+ private readonly routerSocketOptions?: ZLinkNodeRawRouterSocketOptions
80
87
  ) {}
81
88
 
82
89
  createRouter(): ZLinkRawRouterPort {
83
90
  this.requireOpen();
84
91
  return this.own(
85
- new NodeRawRouterPort(createRouterSocket(this.context), this.routerReceiveTimeoutMs)
92
+ new NodeRawRouterPort(createRouterSocket(this.context), this.routerSocketOptions)
86
93
  );
87
94
  }
88
95
 
@@ -281,13 +288,19 @@ class NodeRawRouterPort extends NodeRawSocketPort<RouterSocket> implements ZLink
281
288
  disconnectSocketPeer(this.socket, this.preparedRoutingId(routingId));
282
289
  }
283
290
 
284
- constructor(socket: RouterSocket, receiveTimeoutMs?: number) {
291
+ constructor(socket: RouterSocket, options?: ZLinkNodeRawRouterSocketOptions) {
285
292
  super(socket, true);
286
293
  socket.options.handover = true;
287
294
  socket.options.mandatory = true;
288
295
  socket.options.probe = true;
289
- if (receiveTimeoutMs !== undefined) {
290
- socket.options.recvTimeout = receiveTimeoutMs;
296
+ if (options?.sendHighWaterMark !== undefined) {
297
+ socket.options.sendHwm = BigInt(options.sendHighWaterMark);
298
+ }
299
+ if (options?.receiveHighWaterMark !== undefined) {
300
+ socket.options.recvHwm = BigInt(options.receiveHighWaterMark);
301
+ }
302
+ if (options?.receiveTimeoutMs !== undefined) {
303
+ socket.options.recvTimeout = options.receiveTimeoutMs;
291
304
  }
292
305
  }
293
306
 
@@ -190,7 +190,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
190
190
  private readonly lifecycleGeneration: bigint;
191
191
  private bindEndpoint?: string;
192
192
  private advertiseHost?: string;
193
- private runtime?: RawServiceMeshRuntime;
193
+ runtime?: RawServiceMeshRuntime;
194
194
  private stateful?: ServiceStatefulRuntime;
195
195
  private readyHandler?: (domains: number) => number;
196
196
  private maintenanceTimer?: NodeJS.Timeout;
@@ -629,6 +629,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
629
629
  peer.securityIdentity,
630
630
  peer.lifecycleGeneration
631
631
  );
632
+ this.scheduleMaintenance();
632
633
  }
633
634
 
634
635
  removePeerConnection(intentId: bigint): void {
@@ -1690,7 +1691,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1690
1691
  this.requireStateful().clearSubscriptions(state.ref.spotId);
1691
1692
  }
1692
1693
 
1693
- private createDescriptor(): ServiceNodeDescriptor {
1694
+ createDescriptor(): ServiceNodeDescriptor {
1694
1695
  const channels: ServiceChannelDescriptor[] = [...this.channels]
1695
1696
  .map(([name, weight]) => ({ name, weight }))
1696
1697
  .sort((left, right) => left.name.localeCompare(right.name));
@@ -2559,6 +2560,9 @@ function completionRecord(
2559
2560
  kindData: 'kindData' in result ? (result.kindData ?? null) : null,
2560
2561
  terminalResult: result.terminalResult,
2561
2562
  failureErrno: result.failureCode,
2563
+ ...('failureMessage' in result && result.failureMessage !== undefined
2564
+ ? { failureMessage: result.failureMessage }
2565
+ : {}),
2562
2566
  parts:
2563
2567
  payload === undefined
2564
2568
  ? []
@@ -2808,9 +2812,9 @@ function decodeStatefulRecord(
2808
2812
  });
2809
2813
  return accepted ? SubmitResult.Ok : SubmitResult.InvalidState;
2810
2814
  },
2811
- replyFailure(terminalResult, failureCode) {
2815
+ replyFailure(terminalResult, failureCode, tail) {
2812
2816
  if (stateful.reply === undefined) return SubmitResult.InvalidState;
2813
- return stateful.reply(terminalResult, failureCode)
2817
+ return stateful.reply(terminalResult, failureCode, undefined, tail)
2814
2818
  ? SubmitResult.Ok
2815
2819
  : SubmitResult.InvalidState;
2816
2820
  }
@@ -2818,16 +2822,20 @@ function decodeStatefulRecord(
2818
2822
  }
2819
2823
 
2820
2824
  function receiveIngressLifecycle(record: ServiceMailboxRecord): {
2825
+ readonly receivedAt?: Date;
2821
2826
  readonly applicationJobPermit?: import('../../application-jobs/contracts').ApplicationJobPermitPort;
2822
2827
  readonly releaseRetainedIngress?: () => void;
2823
2828
  } {
2824
2829
  const applicationJob = record.applicationJob;
2825
- return applicationJob === undefined
2826
- ? {}
2827
- : {
2828
- applicationJobPermit: applicationJob,
2829
- releaseRetainedIngress: () => applicationJob.close()
2830
- };
2830
+ return {
2831
+ ...(record.receivedAt === undefined ? {} : { receivedAt: record.receivedAt }),
2832
+ ...(applicationJob === undefined
2833
+ ? {}
2834
+ : {
2835
+ applicationJobPermit: applicationJob,
2836
+ releaseRetainedIngress: () => applicationJob.close()
2837
+ })
2838
+ };
2831
2839
  }
2832
2840
 
2833
2841
  function prepareStatefulMailboxDispatch(
@@ -84,10 +84,10 @@ export interface ZLinkRequestDispatch<TContext extends ZLinkMessageContext> {
84
84
  }
85
85
 
86
86
  export class ZLinkChannelDispatchPipeline {
87
- private readonly filters: readonly ZLinkHandlerFilter[];
87
+ readonly filters: readonly ZLinkHandlerFilter[];
88
88
  private readonly unhandled: ZLinkUnhandledDispatchOptions;
89
89
 
90
- constructor(private readonly options: ZLinkChannelDispatchPipelineOptions) {
90
+ constructor(readonly options: ZLinkChannelDispatchPipelineOptions) {
91
91
  this.filters = options.filters ?? [];
92
92
  this.unhandled = options.unhandled ?? DEFAULT_UNHANDLED_DISPATCH;
93
93
  }
@@ -203,7 +203,7 @@ export class ZLinkChannelDispatchPipeline {
203
203
  }
204
204
  }
205
205
 
206
- private invokeInFlow<TContext extends ZLinkMessageContext, TResult>(
206
+ invokeInFlow<TContext extends ZLinkMessageContext, TResult>(
207
207
  dispatch: ZLinkOneWayDispatch<TContext> | ZLinkRequestDispatch<TContext>,
208
208
  handler: ZLinkChannelDispatchHandler<TContext, TResult>
209
209
  ): unknown {
@@ -330,7 +330,7 @@ export class ZLinkChannelDispatchPipeline {
330
330
  });
331
331
  }
332
332
 
333
- private invoke<TContext extends ZLinkMessageContext, TResult>(
333
+ invoke<TContext extends ZLinkMessageContext, TResult>(
334
334
  dispatch: ZLinkOneWayDispatch<TContext> | ZLinkRequestDispatch<TContext>,
335
335
  handler: ZLinkChannelDispatchHandler<TContext, TResult>
336
336
  ):
@@ -409,6 +409,7 @@ export class ZLinkChannelDispatchPipeline {
409
409
  contentType: context.contentType,
410
410
  metadata: context.metadata,
411
411
  correlationId: context.correlationId,
412
+ receivedAt: context.receivedAt,
412
413
  dispatchKind
413
414
  };
414
415
  }
@@ -456,7 +457,7 @@ export class ZLinkChannelDispatchPipeline {
456
457
  });
457
458
  }
458
459
 
459
- private trace(outcome: ZLinkMessageFlowOutcome, fields: ZLinkChannelDispatchFields): void {
460
+ trace(outcome: ZLinkMessageFlowOutcome, fields: ZLinkChannelDispatchFields): void {
460
461
  if (fields.messageKind === ZLinkDispatchMessageKind.Publish) return;
461
462
  const flow = this.options.dispatchErrors.flow;
462
463
  const tracePoint = flow.begin(outcome, undefined, fields.flowId);
@@ -50,6 +50,7 @@ import {
50
50
  export interface ZLinkChannelRequestDispatcherOptions {
51
51
  readonly meshName?: string;
52
52
  readonly channelName: string;
53
+ readonly receivedAtClock?: () => Date;
53
54
  /**
54
55
  * Mesh channels use the routed message context so a handler can associate
55
56
  * a report with the node that sent it.
@@ -124,7 +125,8 @@ export class ZLinkChannelRequestDispatcher {
124
125
  reply(routingId: unknown, replyToken: unknown): ZLinkMultipartReplyOperation;
125
126
  },
126
127
  signal?: AbortSignal,
127
- decodedHeader?: ZLinkChannelEnvelopeHeader
128
+ decodedHeader?: ZLinkChannelEnvelopeHeader,
129
+ receivedAt: Date = this.options.receivedAtClock?.() ?? new Date()
128
130
  ): Promise<boolean | void> {
129
131
  if (received.spotId !== null && received.spotId !== undefined) {
130
132
  if (received.send === undefined) {
@@ -185,7 +187,8 @@ export class ZLinkChannelRequestDispatcher {
185
187
  contentType: envelope.header.contentType,
186
188
  packetName,
187
189
  metadata: zlinkMessageMetadata(envelope.header.metadata),
188
- correlationId
190
+ correlationId,
191
+ receivedAt
189
192
  };
190
193
  await this.pipeline.dispatchOneWay({
191
194
  fields,
@@ -221,7 +224,8 @@ export class ZLinkChannelRequestDispatcher {
221
224
  contentType: envelope.header.contentType,
222
225
  packetName,
223
226
  metadata: zlinkMessageMetadata(envelope.header.metadata),
224
- correlationId
227
+ correlationId,
228
+ receivedAt
225
229
  };
226
230
  await this.pipeline.dispatchRequest({
227
231
  fields: requestFields,
@@ -301,7 +305,7 @@ export class ZLinkChannelRequestDispatcher {
301
305
  codecs: this.options.codecs,
302
306
  handler: handler?.handler,
303
307
  messageType: handler?.messageType,
304
- context: this.createMeshContext(envelope, record.sourceNodeRid),
308
+ context: this.createMeshContext(envelope, record.sourceNodeRid, record.receivedAt),
305
309
  signal
306
310
  },
307
311
  admit
@@ -323,7 +327,7 @@ export class ZLinkChannelRequestDispatcher {
323
327
  envelope,
324
328
  this.options.codecs,
325
329
  this.options.handlers.get(packetName),
326
- this.createMeshContext(envelope, record.sourceNodeRid),
330
+ this.createMeshContext(envelope, record.sourceNodeRid, record.receivedAt),
327
331
  signal,
328
332
  record,
329
333
  'channel',
@@ -346,7 +350,8 @@ export class ZLinkChannelRequestDispatcher {
346
350
 
347
351
  private createMeshContext(
348
352
  envelope: ReturnType<typeof decodeChannelEnvelope>,
349
- sourceNodeRid: unknown
353
+ sourceNodeRid: unknown,
354
+ receivedAt?: Date
350
355
  ): ZLinkMessageContext {
351
356
  const base = {
352
357
  meshName: this.options.meshName,
@@ -354,7 +359,8 @@ export class ZLinkChannelRequestDispatcher {
354
359
  contentType: envelope.header.contentType,
355
360
  packetName: envelope.packetName!,
356
361
  metadata: zlinkMessageMetadata(envelope.header.metadata),
357
- correlationId: envelope.header.correlationId ?? undefined
362
+ correlationId: envelope.header.correlationId ?? undefined,
363
+ receivedAt: receivedAt ?? this.options.receivedAtClock?.() ?? new Date()
358
364
  };
359
365
  if (this.options.routeMesh !== true) {
360
366
  return base;
@@ -424,6 +430,7 @@ function requireMeshReplyAccepted(result: number): void {
424
430
  export interface ZLinkChannelPublishDispatcherOptions {
425
431
  readonly meshName?: string;
426
432
  readonly channelName: string;
433
+ readonly receivedAtClock?: () => Date;
427
434
  readonly codecs?: ZLinkChannelEnvelopeCodecRegistry;
428
435
  readonly dispatchErrors: ZLinkDispatchErrorReporter;
429
436
  readonly handlers: ReadonlyMap<
@@ -455,7 +462,8 @@ export class ZLinkChannelPublishDispatcher {
455
462
  async dispatch(
456
463
  topicMessage: { readonly topic: string; readonly parts: readonly Message[] },
457
464
  signal?: AbortSignal,
458
- decodedHeader?: ZLinkChannelEnvelopeHeader
465
+ decodedHeader?: ZLinkChannelEnvelopeHeader,
466
+ receivedAt: Date = this.options.receivedAtClock?.() ?? new Date()
459
467
  ): Promise<void> {
460
468
  if (topicMessage.parts.length === 0 || topicMessage.parts[0].data().length === 0) {
461
469
  return;
@@ -507,7 +515,8 @@ export class ZLinkChannelPublishDispatcher {
507
515
  topic: publishTopic,
508
516
  source: publishSource,
509
517
  metadata: zlinkMessageMetadata(envelope.header.metadata),
510
- correlationId: publishCorr
518
+ correlationId: publishCorr,
519
+ receivedAt
511
520
  };
512
521
  await this.pipeline.dispatchOneWay({
513
522
  fields,
@@ -523,6 +532,7 @@ export class ZLinkChannelPublishDispatcher {
523
532
 
524
533
  export interface ZLinkRoutePacketDispatcherOptions {
525
534
  readonly routerChannelId: string;
535
+ readonly receivedAtClock?: () => Date;
526
536
  readonly codecs?: ZLinkChannelEnvelopeCodecRegistry;
527
537
  readonly dispatchErrors: ZLinkDispatchErrorReporter;
528
538
  readonly handlers: readonly ZLinkRouteHandlerRegistration[];
@@ -557,11 +567,13 @@ export class ZLinkRoutePacketDispatcher {
557
567
  Pick<ZLinkRequestDispatch<ZLinkRouteMessageContext>, 'handler' | 'messageType'>
558
568
  >();
559
569
  private readonly codecs?: ZLinkChannelEnvelopeCodecRegistry;
570
+ private readonly receivedAtClock: () => Date;
560
571
  private readonly pipeline: ZLinkChannelDispatchPipeline;
561
572
 
562
573
  constructor(options: ZLinkRoutePacketDispatcherOptions) {
563
574
  this.routerChannelId = options.routerChannelId;
564
575
  this.codecs = options.codecs;
576
+ this.receivedAtClock = options.receivedAtClock ?? (() => new Date());
565
577
  this.dispatchErrors = options.dispatchErrors;
566
578
  this.pipeline = new ZLinkChannelDispatchPipeline({
567
579
  dispatchErrors: options.dispatchErrors,
@@ -635,7 +647,8 @@ export class ZLinkRoutePacketDispatcher {
635
647
  },
636
648
  signal?: AbortSignal,
637
649
  decodedHeader?: ZLinkChannelEnvelopeHeader,
638
- infrastructureChecked = false
650
+ infrastructureChecked = false,
651
+ receivedAt: Date = this.receivedAtClock()
639
652
  ): Promise<boolean | void> {
640
653
  const channelHeader =
641
654
  decodedHeader ?? tryDecodeChannelHeader(received.parts, this.flowEnabled());
@@ -699,7 +712,7 @@ export class ZLinkRoutePacketDispatcher {
699
712
  codecs: codecsForFrameworkPacket(packetName, this.codecs),
700
713
  handler: handler?.handler,
701
714
  messageType: handler?.messageType,
702
- context: this.createRouteContext(envelope, received.routingId),
715
+ context: this.createRouteContext(envelope, received.routingId, receivedAt),
703
716
  signal
704
717
  });
705
718
  return;
@@ -738,7 +751,7 @@ export class ZLinkRoutePacketDispatcher {
738
751
  codecs,
739
752
  handler: handler?.handler,
740
753
  messageType: handler?.messageType,
741
- context: this.createRouteContext(envelope, received.routingId),
754
+ context: this.createRouteContext(envelope, received.routingId, receivedAt),
742
755
  signal,
743
756
  missingHandlerMessage: `No routed request handler is registered for '${this.routerChannelId}:${packetName}'.`,
744
757
  writeReply: (reply) =>
@@ -801,7 +814,8 @@ export class ZLinkRoutePacketDispatcher {
801
814
  sourceNodeRid,
802
815
  record.sourceNodeRid,
803
816
  signal,
804
- admit
817
+ admit,
818
+ record.receivedAt ?? this.receivedAtClock()
805
819
  );
806
820
  }
807
821
  if (envelope.header.kind !== ZLinkChannelMessageKind.Request) {
@@ -821,7 +835,7 @@ export class ZLinkRoutePacketDispatcher {
821
835
  envelope,
822
836
  codecsForFrameworkPacket(packetName, this.codecs),
823
837
  this.requestHandlers.get(packetName),
824
- this.createRouteContext(envelope, record.sourceNodeRid),
838
+ this.createRouteContext(envelope, record.sourceNodeRid, record.receivedAt),
825
839
  signal,
826
840
  record,
827
841
  'routed',
@@ -836,6 +850,7 @@ export class ZLinkRoutePacketDispatcher {
836
850
  sourceNodeRid: RoutingId,
837
851
  signal?: AbortSignal
838
852
  ): Promise<void> {
853
+ const receivedAt = this.receivedAtClock();
839
854
  if (parts.length === 0 || parts[0].data().length === 0) return;
840
855
  let envelope: ReturnType<typeof decodeChannelEnvelope>;
841
856
  try {
@@ -864,7 +879,9 @@ export class ZLinkRoutePacketDispatcher {
864
879
  packetName,
865
880
  String(sourceNodeRid),
866
881
  sourceNodeRid,
867
- signal
882
+ signal,
883
+ undefined,
884
+ receivedAt
868
885
  );
869
886
  }
870
887
 
@@ -874,7 +891,8 @@ export class ZLinkRoutePacketDispatcher {
874
891
  sourceRid: string,
875
892
  sourceNodeRid: unknown,
876
893
  signal?: AbortSignal,
877
- admit?: ZLinkMeshDispatchAdmission
894
+ admit?: ZLinkMeshDispatchAdmission,
895
+ receivedAt: Date = this.receivedAtClock()
878
896
  ): void | Promise<void> {
879
897
  const handler = this.sendHandlers.get(packetName);
880
898
  return this.pipeline.dispatchOneWay(
@@ -891,7 +909,7 @@ export class ZLinkRoutePacketDispatcher {
891
909
  codecs: codecsForFrameworkPacket(packetName, this.codecs),
892
910
  handler: handler?.handler,
893
911
  messageType: handler?.messageType,
894
- context: this.createRouteContext(envelope, sourceNodeRid),
912
+ context: this.createRouteContext(envelope, sourceNodeRid, receivedAt),
895
913
  signal
896
914
  },
897
915
  admit
@@ -907,7 +925,8 @@ export class ZLinkRoutePacketDispatcher {
907
925
 
908
926
  private createRouteContext(
909
927
  envelope: ReturnType<typeof decodeChannelEnvelope>,
910
- sourceRid: unknown
928
+ sourceRid: unknown,
929
+ receivedAt: Date = this.receivedAtClock()
911
930
  ): ZLinkRouteMessageContext {
912
931
  const sourceNodeRid = String(sourceRid ?? '');
913
932
  return {
@@ -916,7 +935,8 @@ export class ZLinkRoutePacketDispatcher {
916
935
  contentType: envelope.header.contentType,
917
936
  sourceNodeRid,
918
937
  metadata: zlinkMessageMetadata(envelope.header.metadata),
919
- correlationId: envelope.header.correlationId ?? undefined
938
+ correlationId: envelope.header.correlationId ?? undefined,
939
+ receivedAt
920
940
  };
921
941
  }
922
942
  }