@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
@@ -19,8 +19,9 @@ import {
19
19
  } from './channel-receive-task-tracker';
20
20
  import { ApplicationJobQueue, type ApplicationJobQueuePermit } from '../host/application-job-queue';
21
21
  import {
22
- bindApplicationJobPermit,
23
- runWithApplicationJobPermit
22
+ enterApplicationJobPermitScope,
23
+ leaveApplicationJobPermitScope,
24
+ releaseApplicationJobPermitTurn
24
25
  } from '../application-jobs/application-job-queue-scope';
25
26
  import {
26
27
  ZLinkSerialExecutionQueue,
@@ -29,20 +30,45 @@ import {
29
30
 
30
31
  export function createChannelApplicationDispatchQueue(): ZLinkSerialExecutionQueue {
31
32
  return new ZLinkSerialExecutionQueue(async (record: ZLinkSerialWorkRecord<unknown>) => {
33
+ const permit = record.context as ApplicationJobQueuePermit;
34
+ const previousPermit = enterApplicationJobPermitScope(permit);
32
35
  try {
33
36
  void Promise.resolve(record.operation()).then(
34
37
  (value) => {
35
- record.resolve(value);
36
- record.release();
38
+ try {
39
+ record.resolve(value);
40
+ } finally {
41
+ try {
42
+ record.release();
43
+ } finally {
44
+ releaseApplicationJobPermitTurn(permit);
45
+ }
46
+ }
37
47
  },
38
48
  (error) => {
39
- record.reject(error);
40
- record.release();
49
+ try {
50
+ record.reject(error);
51
+ } finally {
52
+ try {
53
+ record.release();
54
+ } finally {
55
+ releaseApplicationJobPermitTurn(permit);
56
+ }
57
+ }
41
58
  }
42
59
  );
43
60
  } catch (error) {
44
- record.reject(error);
45
- record.release();
61
+ try {
62
+ record.reject(error);
63
+ } finally {
64
+ try {
65
+ record.release();
66
+ } finally {
67
+ releaseApplicationJobPermitTurn(permit);
68
+ }
69
+ }
70
+ } finally {
71
+ leaveApplicationJobPermitScope(previousPermit);
46
72
  }
47
73
  });
48
74
  }
@@ -79,7 +105,8 @@ interface ZLinkChannelRequestDispatchLoop {
79
105
  reply(routingId: unknown, replyToken: unknown): ZLinkMultipartReplyOperation;
80
106
  },
81
107
  signal?: AbortSignal,
82
- decodedHeader?: ZLinkChannelEnvelopeHeader
108
+ decodedHeader?: ZLinkChannelEnvelopeHeader,
109
+ receivedAt?: Date
83
110
  ): Promise<boolean | void>;
84
111
  }
85
112
 
@@ -87,7 +114,8 @@ interface ZLinkChannelPublishDispatchLoop {
87
114
  dispatch(
88
115
  topicMessage: { readonly topic: string; readonly parts: readonly Message[] },
89
116
  signal?: AbortSignal,
90
- decodedHeader?: ZLinkChannelEnvelopeHeader
117
+ decodedHeader?: ZLinkChannelEnvelopeHeader,
118
+ receivedAt?: Date
91
119
  ): Promise<void>;
92
120
  }
93
121
 
@@ -118,7 +146,8 @@ interface ZLinkRoutePacketDispatchLoop {
118
146
  },
119
147
  signal?: AbortSignal,
120
148
  decodedHeader?: ZLinkChannelEnvelopeHeader,
121
- infrastructureChecked?: boolean
149
+ infrastructureChecked?: boolean,
150
+ receivedAt?: Date
122
151
  ): Promise<boolean | void>;
123
152
  }
124
153
 
@@ -240,7 +269,8 @@ export class ZLinkChannelReceiveLoop {
240
269
  private readonly applicationJobQueue: ApplicationJobQueue,
241
270
  reportError?: ZLinkReceiveTaskErrorReporter,
242
271
  private readonly roundRobin?: ZLinkReceiveRoundRobinCoordinator,
243
- private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue()
272
+ private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue(),
273
+ private readonly receivedAtClock: () => Date = () => new Date()
244
274
  ) {
245
275
  this.inFlight = new ZLinkReceiveTaskTracker(reportError);
246
276
  this.receiveOwner = roundRobin?.register() ?? Symbol('channel-receive-owner');
@@ -304,6 +334,7 @@ export class ZLinkChannelReceiveLoop {
304
334
  this.roundRobin?.setReady(this.receiveOwner, false);
305
335
  break;
306
336
  }
337
+ const receivedAt = this.receivedAtClock();
307
338
  const receivedBytes = messageBytes(received.parts);
308
339
  let classification: ReturnType<ZLinkChannelReceiveLoop['classify']>;
309
340
  try {
@@ -322,12 +353,18 @@ export class ZLinkChannelReceiveLoop {
322
353
  break;
323
354
  } else {
324
355
  applicationJobPermit.markApplicationQueued();
325
- const task = runWithApplicationJobPermit(applicationJobPermit, () =>
326
- this.applicationDispatchQueue.submitPreAdmitted(
327
- bindApplicationJobPermit(() =>
328
- this.dispatchAndClose(received, signal, classification.decodedHeader, true)
329
- )
330
- )
356
+ const task = this.applicationDispatchQueue.submitPreAdmitted(
357
+ () =>
358
+ this.dispatchAndClose(
359
+ received,
360
+ signal,
361
+ classification.decodedHeader,
362
+ true,
363
+ undefined,
364
+ receivedAt
365
+ ),
366
+ undefined,
367
+ applicationJobPermit
331
368
  );
332
369
  this.inFlight.track(task);
333
370
  }
@@ -374,7 +411,8 @@ export class ZLinkChannelReceiveLoop {
374
411
  signal?: AbortSignal,
375
412
  decodedHeader?: ZLinkChannelEnvelopeHeader,
376
413
  infrastructureChecked = false,
377
- releaseRawReceive?: () => void
414
+ releaseRawReceive?: () => void,
415
+ receivedAt: Date = this.receivedAtClock()
378
416
  ): Promise<void> {
379
417
  let closeReceived = true;
380
418
  try {
@@ -386,7 +424,13 @@ export class ZLinkChannelReceiveLoop {
386
424
  }
387
425
  decodedHeader = classification.decodedHeader;
388
426
  }
389
- const consumed = await this.dispatcher.dispatch(received, this.router, signal, decodedHeader);
427
+ const consumed = await this.dispatcher.dispatch(
428
+ received,
429
+ this.router,
430
+ signal,
431
+ decodedHeader,
432
+ receivedAt
433
+ );
390
434
  if (consumed === true) {
391
435
  closeReceived = false;
392
436
  }
@@ -452,7 +496,8 @@ export class ZLinkSubscriberReceiveLoop {
452
496
  ) => boolean | ZLinkSubscriberInfrastructureResult,
453
497
  private readonly roundRobin?: ZLinkReceiveRoundRobinCoordinator,
454
498
  reportError?: ZLinkReceiveTaskErrorReporter,
455
- private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue()
499
+ private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue(),
500
+ private readonly receivedAtClock: () => Date = () => new Date()
456
501
  ) {
457
502
  this.inFlight = new ZLinkReceiveTaskTracker(reportError);
458
503
  this.poller = adapter.createReadablePoller(subscriber);
@@ -523,6 +568,7 @@ export class ZLinkSubscriberReceiveLoop {
523
568
  if (!(await this.poller.waitForReadable(capacitySignal))) break;
524
569
  continue;
525
570
  }
571
+ const receivedAt = this.receivedAtClock();
526
572
  const receivedBytes = messageBytes(topicMessage.parts as readonly Message[]);
527
573
  let classification: ReturnType<ZLinkSubscriberReceiveLoop['classify']>;
528
574
  try {
@@ -551,12 +597,18 @@ export class ZLinkSubscriberReceiveLoop {
551
597
  break;
552
598
  }
553
599
  applicationJobPermit.markApplicationQueued();
554
- const task = runWithApplicationJobPermit(applicationJobPermit, () =>
555
- this.applicationDispatchQueue.submitPreAdmitted(
556
- bindApplicationJobPermit(() =>
557
- this.dispatchAndClose(topicMessage, signal, classification.decodedHeader, true)
558
- )
559
- )
600
+ const task = this.applicationDispatchQueue.submitPreAdmitted(
601
+ () =>
602
+ this.dispatchAndClose(
603
+ topicMessage,
604
+ signal,
605
+ classification.decodedHeader,
606
+ true,
607
+ undefined,
608
+ receivedAt
609
+ ),
610
+ undefined,
611
+ applicationJobPermit
560
612
  );
561
613
  releaseRawReceive();
562
614
  this.inFlight.track(task);
@@ -593,7 +645,8 @@ export class ZLinkSubscriberReceiveLoop {
593
645
  signal?: AbortSignal,
594
646
  decodedHeader?: ZLinkChannelEnvelopeHeader,
595
647
  infrastructureChecked = false,
596
- releaseRawReceive?: () => void
648
+ releaseRawReceive?: () => void,
649
+ receivedAt: Date = this.receivedAtClock()
597
650
  ): Promise<void> {
598
651
  try {
599
652
  if (!infrastructureChecked) {
@@ -601,7 +654,7 @@ export class ZLinkSubscriberReceiveLoop {
601
654
  if (classification.kind === 'consumed') return;
602
655
  decodedHeader = classification.decodedHeader;
603
656
  }
604
- await this.dispatcher.dispatch(topicMessage, signal, decodedHeader);
657
+ await this.dispatcher.dispatch(topicMessage, signal, decodedHeader, receivedAt);
605
658
  } finally {
606
659
  releaseRawReceive?.();
607
660
  closeMessages(topicMessage.parts as readonly Message[]);
@@ -650,7 +703,8 @@ export class ZLinkRouteReceiveLoop {
650
703
  private readonly applicationJobQueue: ApplicationJobQueue,
651
704
  private readonly roundRobin?: ZLinkReceiveRoundRobinCoordinator,
652
705
  reportError?: ZLinkReceiveTaskErrorReporter,
653
- private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue()
706
+ private readonly applicationDispatchQueue = createChannelApplicationDispatchQueue(),
707
+ private readonly receivedAtClock: () => Date = () => new Date()
654
708
  ) {
655
709
  this.inFlight = new ZLinkReceiveTaskTracker(reportError);
656
710
  this.receiveOwner = roundRobin?.register() ?? Symbol('route-receive-owner');
@@ -712,6 +766,7 @@ export class ZLinkRouteReceiveLoop {
712
766
  if (!(await this.poller.waitForReadable(capacitySignal))) break;
713
767
  continue;
714
768
  }
769
+ const receivedAt = this.receivedAtClock();
715
770
  const receivedBytes = messageBytes(received.parts);
716
771
  let infrastructure: boolean | Promise<void> | undefined;
717
772
  try {
@@ -763,10 +818,10 @@ export class ZLinkRouteReceiveLoop {
763
818
  break;
764
819
  }
765
820
  applicationJobPermit.markApplicationQueued();
766
- const task = runWithApplicationJobPermit(applicationJobPermit, () =>
767
- this.applicationDispatchQueue.submitPreAdmitted(
768
- bindApplicationJobPermit(() => this.dispatchAndClose(received, signal, true))
769
- )
821
+ const task = this.applicationDispatchQueue.submitPreAdmitted(
822
+ () => this.dispatchAndClose(received, signal, true, undefined, receivedAt),
823
+ undefined,
824
+ applicationJobPermit
770
825
  );
771
826
  releaseRawReceive();
772
827
  this.inFlight.track(task);
@@ -808,7 +863,8 @@ export class ZLinkRouteReceiveLoop {
808
863
  },
809
864
  signal?: AbortSignal,
810
865
  infrastructureChecked = false,
811
- releaseRawReceive?: () => void
866
+ releaseRawReceive?: () => void,
867
+ receivedAt: Date = this.receivedAtClock()
812
868
  ): Promise<void> {
813
869
  let closeReceived = true;
814
870
  try {
@@ -817,7 +873,8 @@ export class ZLinkRouteReceiveLoop {
817
873
  this.router,
818
874
  signal,
819
875
  tryDecodeChannelHeader(received.parts, this.dispatcher.flowEnabled?.() ?? true),
820
- infrastructureChecked
876
+ infrastructureChecked,
877
+ receivedAt
821
878
  );
822
879
  if (consumed === true) {
823
880
  closeReceived = false;
@@ -104,7 +104,7 @@ export class ZLinkChannelRuntimeLifecycle {
104
104
  private readonly receiveRoundRobin = new ZLinkReceiveRoundRobinCoordinator();
105
105
  private readonly channelReceiveLoops: ZLinkChannelReceiveLoop[] = [];
106
106
  private readonly subscriberReceiveLoops: ZLinkSubscriberReceiveLoop[] = [];
107
- private readonly manualFanoutSubscribers = new Map<string, ManualFanoutSubscriberState>();
107
+ readonly manualFanoutSubscribers = new Map<string, ManualFanoutSubscriberState>();
108
108
  private readonly manualFanoutSubscriberOwners = new Set<object>();
109
109
  private readonly routeReceiveLoops: Array<{ stop(): Promise<void> }> = [];
110
110
  private readonly meshChannelDispatchers = new Map<string, ZLinkChannelRequestDispatcher>();
@@ -664,7 +664,7 @@ export class ZLinkChannelRuntimeLifecycle {
664
664
  return state;
665
665
  }
666
666
 
667
- private requestManualFanoutConnect(
667
+ requestManualFanoutConnect(
668
668
  channelName: string,
669
669
  endpoint: string,
670
670
  taskRunner: ZLinkRuntimeTaskRunner,
@@ -156,7 +156,7 @@ export class ZLinkChannelSocketRegistry {
156
156
  readonly lifecycleGeneration: bigint;
157
157
  }
158
158
  >();
159
- private readonly clientServerConnections = new Map<string, ClientServerPhysicalConnection>();
159
+ readonly clientServerConnections = new Map<string, ClientServerPhysicalConnection>();
160
160
  private readonly clientServerDiscovery = new ServiceDiscoveryRegistry();
161
161
  private readonly clientServerReadyIdentities = new Map<
162
162
  string,
@@ -1455,7 +1455,7 @@ export class ZLinkChannelSocketRegistry {
1455
1455
  }
1456
1456
  }
1457
1457
 
1458
- private applyClientServerDescriptorUpdate(
1458
+ applyClientServerDescriptorUpdate(
1459
1459
  connectionId: string,
1460
1460
  connection: { readonly channelName: string; readonly physicalConnectionId: symbol },
1461
1461
  admission: ZLinkClientServerAdmission
@@ -1518,7 +1518,7 @@ export class ZLinkChannelSocketRegistry {
1518
1518
  this.clientServerLivenessTimer.unref();
1519
1519
  }
1520
1520
 
1521
- private drainSocketMonitors(): void {
1521
+ drainSocketMonitors(): void {
1522
1522
  for (const monitor of this.ownedResources) if ('drain' in monitor) monitor.drain();
1523
1523
  }
1524
1524
 
@@ -574,7 +574,8 @@ export class ZLinkRuntimeMetrics {
574
574
  startRelocation(
575
575
  meshName: string,
576
576
  objectKind: 'actor' | 'user_spot' | 'instance_spot',
577
- policy: 'recreate' | 'snapshot'
577
+ policy: 'recreate' | 'snapshot',
578
+ onComplete?: (outcome: string) => void
578
579
  ): ZLinkRelocationMetricOperation {
579
580
  const started = process.hrtime.bigint();
580
581
  const startAttributes = { mesh_name: meshName, object_kind: objectKind, policy };
@@ -595,6 +596,7 @@ export class ZLinkRuntimeMetrics {
595
596
  Number(process.hrtime.bigint() - started) / NANOSECONDS_PER_SECOND,
596
597
  terminal
597
598
  );
599
+ onComplete?.(outcome);
598
600
  }
599
601
  };
600
602
  }
@@ -72,9 +72,9 @@ interface ZLinkSerialAdmissionLane {
72
72
  * Application work carries the host permit through the queue until the handler starts.
73
73
  */
74
74
  export class ZLinkSerialExecutionQueue {
75
- private readonly application: ZLinkSerialAdmissionLane;
76
- private readonly lifecycle: ZLinkSerialAdmissionLane;
77
- private activeLifecycle?: ZLinkSerialWorkRecord<unknown>;
75
+ readonly application: ZLinkSerialAdmissionLane;
76
+ readonly lifecycle: ZLinkSerialAdmissionLane;
77
+ activeLifecycle?: ZLinkSerialWorkRecord<unknown>;
78
78
  private activeRecord?: ZLinkSerialWorkRecord<unknown>;
79
79
  /** The admitted node itself owns ready membership; no second readiness flag. */
80
80
  private ownerMembership?: SerialWorkRecord<unknown>;
@@ -86,10 +86,10 @@ export class ZLinkSerialExecutionQueue {
86
86
  private lifecycleDebt = false;
87
87
  private claimStartedAt?: number;
88
88
  private closed = false;
89
- private readonly idleWaiters: Array<() => void> = [];
89
+ readonly idleWaiters: Array<() => void> = [];
90
90
 
91
91
  constructor(
92
- private readonly executeRecord: (record: ZLinkSerialWorkRecord<unknown>) => Promise<void>,
92
+ readonly executeRecord: (record: ZLinkSerialWorkRecord<unknown>) => Promise<void>,
93
93
  options: ZLinkSerialSchedulerOptions = {},
94
94
  private readonly sharedOwner?: ZLinkSerialExecutionQueue
95
95
  ) {
@@ -217,7 +217,7 @@ export class ZLinkSerialExecutionQueue {
217
217
  return this.closed;
218
218
  }
219
219
 
220
- private admit<T>(
220
+ admit<T>(
221
221
  operation: () => Promise<T> | T,
222
222
  options: ZLinkSerialWorkOptions | undefined,
223
223
  context?: unknown,
@@ -291,7 +291,7 @@ export class ZLinkSerialExecutionQueue {
291
291
  return result;
292
292
  }
293
293
 
294
- private scheduleDrain(): void {
294
+ scheduleDrain(): void {
295
295
  if (this.sharedOwner !== undefined) {
296
296
  if (this.activeRecord !== undefined) return;
297
297
  // Ready membership in the existing owner queue is the only notification state.
@@ -320,7 +320,7 @@ export class ZLinkSerialExecutionQueue {
320
320
  }
321
321
 
322
322
  /** Notifications carry no payload, admission sequence, result Promise, or host permit. */
323
- private enqueueMailboxTurn(operation: () => void): void {
323
+ enqueueMailboxTurn(operation: () => void): void {
324
324
  const entry: SerialWorkRecord<unknown> = {
325
325
  acceptedSequence: 0n,
326
326
  lane: 'application',
@@ -384,7 +384,7 @@ export class ZLinkSerialExecutionQueue {
384
384
  });
385
385
  }
386
386
 
387
- private async drain(): Promise<void> {
387
+ async drain(): Promise<void> {
388
388
  const consumer = this.consumer!;
389
389
  if (consumer.running) return;
390
390
  consumer.running = true;
@@ -461,7 +461,7 @@ export class ZLinkSerialExecutionQueue {
461
461
  return record.preparationState !== 'pending' && record.preparationState !== 'canceling';
462
462
  }
463
463
 
464
- private selectNext(): SerialWorkRecord<unknown> | undefined {
464
+ selectNext(): SerialWorkRecord<unknown> | undefined {
465
465
  const lifecycleOwner = this.activeLifecycle?.context as ZLinkSerialLifecycleContext | undefined;
466
466
  let applicationRecord: SerialWorkRecord<unknown> | undefined;
467
467
  if (lifecycleOwner === undefined) {
@@ -488,7 +488,7 @@ export class ZLinkSerialExecutionQueue {
488
488
  return applicationRecord!;
489
489
  }
490
490
 
491
- private takeSelected(selected: SerialWorkRecord<unknown>): ZLinkSerialWorkRecord<unknown> {
491
+ takeSelected(selected: SerialWorkRecord<unknown>): ZLinkSerialWorkRecord<unknown> {
492
492
  const record =
493
493
  selected.lane === 'lifecycle' ? this.takeLifecycle() : this.takeApplication(selected);
494
494
  if (record !== selected) {
@@ -508,7 +508,7 @@ export class ZLinkSerialExecutionQueue {
508
508
  return record;
509
509
  }
510
510
 
511
- private takeApplication(selected: SerialWorkRecord<unknown>): ZLinkSerialWorkRecord<unknown> {
511
+ takeApplication(selected: SerialWorkRecord<unknown>): ZLinkSerialWorkRecord<unknown> {
512
512
  this.lifecycleStreak = 0;
513
513
  this.lifecycleDebt = false;
514
514
  if (this.application.records[0] === selected) return this.application.records.shift()!;
@@ -577,7 +577,7 @@ export class ZLinkSerialExecutionQueue {
577
577
  return performance.now() - (this.claimStartedAt ?? performance.now()) >= this.ownerTimeBudget;
578
578
  }
579
579
 
580
- private resolveIdleWaiters(): void {
580
+ resolveIdleWaiters(): void {
581
581
  for (let index = 0; index < this.idleWaiters.length; index++) this.idleWaiters[index]!();
582
582
  this.idleWaiters.length = 0;
583
583
  }
@@ -22,6 +22,7 @@ import type {
22
22
  ZLinkRawRouterPort
23
23
  } from '../backend/raw-binding-port';
24
24
  import { submitToRequestResult } from '../messaging/submission-result';
25
+ import { copyPayloadBytes } from '../../contracts/Common/payload-slab';
25
26
  import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
26
27
  import { enumWireRejectReason } from '../protocol/service_wire_codec.generated';
27
28
  import { SERVICE_WIRE_PREFIX_SIZE } from './service-wire-binary-primitives';
@@ -46,9 +47,11 @@ import {
46
47
  SERVICE_WIRE_MAGIC,
47
48
  SERVICE_WIRE_MAJOR,
48
49
  ServiceWireCommand,
49
- ServiceWireFrameworkErrorCode
50
+ ServiceWireFrameworkErrorCode,
51
+ recoverRequestCorrelation
50
52
  } from './service-wire-constants.generated';
51
53
  import {
54
+ decodeApplicationPayload,
52
55
  decodeApplicationPayloadView,
53
56
  decodeChannelRequestHeader,
54
57
  decodeChannelSendHeader,
@@ -204,7 +207,7 @@ export class RawServiceMeshRuntime {
204
207
  private readonly releaseOperations: () => void;
205
208
 
206
209
  private readonly operations: OperationRegistry<RawServiceRequestResult, number>;
207
- private readonly expectedPeers = new Map<
210
+ readonly expectedPeers = new Map<
208
211
  string,
209
212
  {
210
213
  readonly meshName: string;
@@ -221,7 +224,7 @@ export class RawServiceMeshRuntime {
221
224
  * The last observed Core selected route of each RID (Core ROUTER §10.1):
222
225
  * RID -> route generation. Only `observeSelectedRoutes` replaces it.
223
226
  */
224
- private selectedRoutes = new Map<string, bigint>();
227
+ selectedRoutes = new Map<string, bigint>();
225
228
  private connectionAttempts: readonly ConnectionAttempt[] = [];
226
229
  private readonly bindingPort: ZLinkRawBindingPort;
227
230
  private readonly applicationJobQueue: ApplicationJobQueuePort;
@@ -231,7 +234,7 @@ export class RawServiceMeshRuntime {
231
234
  private receiveAcquisition?: Promise<ApplicationJobPermitPort>;
232
235
  private receivePermit?: ApplicationJobPermitPort;
233
236
  private receiveRotation?: ApplicationJobReceiveRotationPort;
234
- private readonly controlSends = new Map<string, ControlSendState>();
237
+ readonly controlSends = new Map<string, ControlSendState>();
235
238
  private infrastructureFailure?: { readonly error: unknown };
236
239
  private readonly onReceiveReady?: () => void;
237
240
  private readonly infrastructureTaskRunner?: RawServiceMeshRuntimeOptions['infrastructureTaskRunner'];
@@ -242,7 +245,7 @@ export class RawServiceMeshRuntime {
242
245
  private readonly resolveAdvertisedEndpoint?: RawServiceMeshRuntimeOptions['resolveAdvertisedEndpoint'];
243
246
  private descriptor: ServiceNodeDescriptor;
244
247
  private host?: ZLinkRawHostPort;
245
- private router?: ZLinkRawRouterPort;
248
+ router?: ZLinkRawRouterPort;
246
249
  private nextCorrelation = 1n;
247
250
  private serviceIngress?: RawServiceIngressHandler;
248
251
  private serviceIngressAccepts?: (command: number) => boolean;
@@ -1189,7 +1192,7 @@ export class RawServiceMeshRuntime {
1189
1192
  }
1190
1193
  }
1191
1194
 
1192
- private reportProtocolError(
1195
+ reportProtocolError(
1193
1196
  received: import('../backend/node/node-raw-binding-port').ZLinkRawReceivedRecord
1194
1197
  ): void {
1195
1198
  const request = received.requestSeq !== undefined;
@@ -1206,32 +1209,19 @@ export class RawServiceMeshRuntime {
1206
1209
  received: import('../backend/node/node-raw-binding-port').ZLinkRawReceivedRecord
1207
1210
  ): boolean {
1208
1211
  if (received.requestSeq === undefined || received.parts.length === 0) return false;
1209
- try {
1210
- const header = decodeHeader(received.parts[0]!);
1211
- const correlation =
1212
- header.command === M6aServiceWireCommand.nodeRequest
1213
- ? decodeNodeRequestHeader(received.parts[0]!)
1214
- : header.command === M6aServiceWireCommand.channelRequest
1215
- ? decodeChannelRequestHeader(received.parts[0]!).correlation
1216
- : undefined;
1217
- if (correlation === undefined) return false;
1218
- const reply = [
1219
- encodeReplyHeader(
1220
- correlation,
1221
- RequestResult.ProtocolError,
1222
- ServiceWireFrameworkErrorCode.requestProtocolError
1223
- )
1224
- ];
1225
- if (received.reply === undefined) return false;
1226
- received.reply(reply);
1227
- return true;
1228
- } catch (error) {
1229
- if (error instanceof ServiceWireProtocolError) return false;
1230
- throw error;
1231
- }
1212
+ const correlation = recoverRequestCorrelation(received.parts[0]!);
1213
+ if (correlation === undefined || received.reply === undefined) return false;
1214
+ received.reply([
1215
+ encodeReplyHeader(
1216
+ correlation,
1217
+ RequestResult.ProtocolError,
1218
+ ServiceWireFrameworkErrorCode.requestProtocolError
1219
+ )
1220
+ ]);
1221
+ return true;
1232
1222
  }
1233
1223
 
1234
- private processReceived(
1224
+ processReceived(
1235
1225
  received: ZLinkRawReceivedRecord,
1236
1226
  nowMs: number,
1237
1227
  applicationJobOwner: ApplicationIngressRecordOwner
@@ -1604,11 +1594,24 @@ export class RawServiceMeshRuntime {
1604
1594
  this.submitControl(probe.nodeRoutingId, parts)
1605
1595
  );
1606
1596
  }
1597
+ const failures: unknown[] = [];
1607
1598
  for (const nodeRoutingId of result.timedOutNodes) {
1608
1599
  const peer = this.topology.peer(nodeRoutingId);
1609
1600
  if (peer === undefined) continue;
1610
- this.removePeer(peer, () => this.requireStarted().disconnectRid!(nodeRoutingId));
1601
+ try {
1602
+ this.removePeer(peer, () => {
1603
+ try {
1604
+ this.requireStarted().disconnectRid!(nodeRoutingId);
1605
+ } catch (error) {
1606
+ failures.push(error);
1607
+ }
1608
+ });
1609
+ } catch (error) {
1610
+ failures.push(error);
1611
+ }
1611
1612
  }
1613
+ if (failures.length === 1) throw failures[0];
1614
+ if (failures.length > 1) throw new AggregateError(failures, 'Liveness peer cleanup failed.');
1612
1615
  return result;
1613
1616
  }
1614
1617
 
@@ -1698,7 +1701,7 @@ export class RawServiceMeshRuntime {
1698
1701
  channelName === undefined
1699
1702
  ? encodeNodeRequestHeader(correlation)
1700
1703
  : encodeChannelRequestHeader(correlation, channelName);
1701
- const encodedPayload = this.applicationFrame(payload);
1704
+ let encodedPayload = this.applicationFrame(payload);
1702
1705
  let selectedTargetNodeRoutingId = targetNodeRoutingId;
1703
1706
  if (
1704
1707
  selectedTargetNodeRoutingId !== this.descriptor.nodeRoutingId &&
@@ -1726,6 +1729,7 @@ export class RawServiceMeshRuntime {
1726
1729
  }
1727
1730
  }
1728
1731
  if (selectedTargetNodeRoutingId === this.descriptor.nodeRoutingId) {
1732
+ encodedPayload = copyPayloadBytes(encodedPayload);
1729
1733
  // The local admission path aborts its capacity wait on the terminal
1730
1734
  // Promise, so a local request always keeps the Promise entry.
1731
1735
  const pending =
@@ -1916,10 +1920,10 @@ export class RawServiceMeshRuntime {
1916
1920
  }
1917
1921
 
1918
1922
  private applicationPayload(payload: ServiceApplicationPayloadInput): ServiceApplicationPayload {
1919
- return Buffer.isBuffer(payload) ? decodeApplicationPayloadView(payload) : payload;
1923
+ return Buffer.isBuffer(payload) ? decodeApplicationPayload(payload) : payload;
1920
1924
  }
1921
1925
 
1922
- private admitPeer(
1926
+ admitPeer(
1923
1927
  descriptor: ServiceNodeDescriptor,
1924
1928
  connectionId: string | bigint,
1925
1929
  nowMs: number,
@@ -2021,10 +2025,7 @@ export class RawServiceMeshRuntime {
2021
2025
  return this.router;
2022
2026
  }
2023
2027
 
2024
- private async sendControl(
2025
- targetNodeRoutingId: string,
2026
- parts: readonly Uint8Array[]
2027
- ): Promise<boolean> {
2028
+ async sendControl(targetNodeRoutingId: string, parts: readonly Uint8Array[]): Promise<boolean> {
2028
2029
  return this.startControlSend(targetNodeRoutingId, decodeHeader(parts[0]!).command, () =>
2029
2030
  this.submitControl(targetNodeRoutingId, parts)
2030
2031
  );
@@ -2044,7 +2045,7 @@ export class RawServiceMeshRuntime {
2044
2045
  );
2045
2046
  }
2046
2047
 
2047
- private startControlSend(
2048
+ startControlSend(
2048
2049
  target: string,
2049
2050
  command: number,
2050
2051
  attempt: () => boolean | Promise<boolean>,
@@ -60,7 +60,7 @@ const NO_LIVENESS_WORK: ServiceLivenessTick = Object.freeze({
60
60
  });
61
61
 
62
62
  export class ServiceLivenessRegistry {
63
- private readonly peers = new Map<string, ServiceLivenessConnection>();
63
+ readonly peers = new Map<string, ServiceLivenessConnection>();
64
64
  private nextProbeId = 1n;
65
65
  /**
66
66
  * The earliest probe or timeout among admitted peers. Received traffic only
@@ -9,6 +9,7 @@ export interface ServiceMailboxDispatchOwner {
9
9
  }
10
10
 
11
11
  export interface ServiceMailboxRecord {
12
+ receivedAt?: Date;
12
13
  readonly sourcePreparationOwner?: ServiceMailboxDispatchOwner;
13
14
  readonly ingress?: RawServiceIngressRecord;
14
15
  readonly owner: string;
@@ -82,7 +83,10 @@ export class ServiceMailbox {
82
83
  private nextRelocationSerial = 1n;
83
84
  private closed = false;
84
85
 
85
- constructor(private readonly onReady?: (domain: ServiceMailboxDomain) => void) {
86
+ constructor(
87
+ private readonly onReady?: (domain: ServiceMailboxDomain) => void,
88
+ private readonly receivedAtClock: () => Date = () => new Date()
89
+ ) {
86
90
  // Host ApplicationJobQueue permits own ordinary-record admission. The
87
91
  // Framework-owned payload lifetime does not retain Core HWM credit. A
88
92
  // second mailbox cap would reject already-admitted work before the shared
@@ -105,6 +109,7 @@ export class ServiceMailbox {
105
109
  const target = this.domain(record.domain);
106
110
  const bytes = retainedBytes(record);
107
111
  if (this.closed) return false;
112
+ record.receivedAt ??= this.receivedAtClock();
108
113
  if (record.domain === 'application') {
109
114
  if (this.relocatedOwners.has(record.owner)) return false;
110
115
  const relocation = this.relocationSeals.get(record.owner);
@@ -253,6 +253,7 @@ export interface ZLinkMessageFollowOrigin {
253
253
  }
254
254
 
255
255
  export interface ReceiveRecord {
256
+ readonly receivedAt?: Date;
256
257
  /** Resolve record prerequisites in owner order, outside an application handler turn. */
257
258
  readonly prepareDispatch?: (
258
259
  this: ReceiveRecord,
@@ -276,6 +277,7 @@ export interface ReceiveRecord {
276
277
  readonly kindData: ReceiveKindData | null;
277
278
  readonly terminalResult: number;
278
279
  readonly failureErrno: number;
280
+ readonly failureMessage?: string;
279
281
  readonly parts: Message[];
280
282
  /** Returns whether the local operation can still accept a lifecycle reply. */
281
283
  readonly isPending?: () => boolean;
@@ -299,7 +301,11 @@ export interface ReceiveRecord {
299
301
  replyContentType?: string
300
302
  ): SubmitResult;
301
303
  /** Emits a typed command-20 terminal for a lifecycle request before admission. */
302
- replyFailure?(terminalResult: number, failureCode: number): SubmitResult;
304
+ replyFailure?(
305
+ terminalResult: number,
306
+ failureCode: number,
307
+ tail?: import('./service-stateful-wire-codec').ServiceStatefulReplyTail
308
+ ): SubmitResult;
303
309
  }
304
310
 
305
311
  export interface ClaimReceiveResult {