@zlink-systems/framework 0.30.0 → 0.30.1

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 (245) hide show
  1. package/LICENSE +150 -68
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/contracts/Actors/ZLinkActorClient.d.ts +2 -1
  4. package/dist/contracts/Channels/Calls.d.ts +2 -1
  5. package/dist/contracts/Common/ZLinkMessageMetadata.js +11 -5
  6. package/dist/contracts/Handlers/Attributes.d.ts +5 -13
  7. package/dist/contracts/Handlers/Attributes.js +7 -11
  8. package/dist/contracts/Handlers/JsonContract.d.ts +2 -46
  9. package/dist/contracts/Handlers/JsonContract.js +4 -153
  10. package/dist/contracts/Spots/Contracts.d.ts +2 -1
  11. package/dist/nest-integration.d.ts +1 -1
  12. package/dist/nest-integration.js +2 -1
  13. package/dist/runtime/actors/actor-client.d.ts +1 -1
  14. package/dist/runtime/actors/actor-client.js +14 -12
  15. package/dist/runtime/actors/actor-handoff.d.ts +1 -1
  16. package/dist/runtime/actors/actor-handoff.js +7 -4
  17. package/dist/runtime/actors/actor-join-relocation.d.ts +1 -0
  18. package/dist/runtime/actors/actor-local-native-join.js +9 -12
  19. package/dist/runtime/actors/actor-remote-wire.js +15 -0
  20. package/dist/runtime/actors/actor-routed-join-transport.d.ts +6 -2
  21. package/dist/runtime/actors/actor-routed-json-request.d.ts +2 -0
  22. package/dist/runtime/actors/index.d.ts +2 -1
  23. package/dist/runtime/actors/index.js +18 -6
  24. package/dist/runtime/actors/spot-actor-dispatch.js +2 -2
  25. package/dist/runtime/admission.d.ts +1 -1
  26. package/dist/runtime/admission.js +15 -5
  27. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +6 -8
  28. package/dist/runtime/application-jobs/application-ingress-record-owner.js +39 -22
  29. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +2 -1
  30. package/dist/runtime/application-jobs/application-job-queue-scope.js +37 -22
  31. package/dist/runtime/application-jobs/contracts.d.ts +10 -1
  32. package/dist/runtime/backend/contracts/index.d.ts +18 -9
  33. package/dist/runtime/backend/mesh-completion-table.d.ts +9 -1
  34. package/dist/runtime/backend/mesh-completion-table.js +149 -49
  35. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +3 -0
  36. package/dist/runtime/backend/mesh-dispatch-pump.js +32 -11
  37. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
  38. package/dist/runtime/backend/node/node-backend-adapter-support.js +16 -9
  39. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  40. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +5 -2
  41. package/dist/runtime/backend/node/node-raw-binding-port.js +42 -22
  42. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +13 -5
  43. package/dist/runtime/backend/node/node-raw-mesh-backend.js +69 -51
  44. package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -6
  45. package/dist/runtime/backend/raw-binding-port.d.ts +2 -2
  46. package/dist/runtime/backend/runtime-message.d.ts +1 -0
  47. package/dist/runtime/backend/runtime-message.js +5 -0
  48. package/dist/runtime/backend/runtime-values.d.ts +1 -0
  49. package/dist/runtime/backend/runtime-values.js +2 -1
  50. package/dist/runtime/channels/channel-clients.d.ts +1 -0
  51. package/dist/runtime/channels/channel-clients.js +58 -21
  52. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +3 -2
  53. package/dist/runtime/channels/channel-dispatch-pipeline.js +20 -11
  54. package/dist/runtime/channels/channel-dispatch-services.d.ts +1 -0
  55. package/dist/runtime/channels/channel-dispatch-services.js +45 -0
  56. package/dist/runtime/channels/channel-dispatchers.d.ts +1 -1
  57. package/dist/runtime/channels/channel-dispatchers.js +27 -26
  58. package/dist/runtime/channels/channel-envelope-inspection.d.ts +1 -2
  59. package/dist/runtime/channels/channel-envelope-inspection.js +1 -16
  60. package/dist/runtime/channels/channel-envelope.d.ts +1 -0
  61. package/dist/runtime/channels/channel-envelope.js +83 -8
  62. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  63. package/dist/runtime/channels/channel-outbound-operations.js +8 -8
  64. package/dist/runtime/channels/channel-runtime-manager.d.ts +6 -6
  65. package/dist/runtime/channels/channel-runtime-manager.js +12 -12
  66. package/dist/runtime/channels/channel-transports.d.ts +18 -15
  67. package/dist/runtime/channels/channel-transports.js +48 -31
  68. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  69. package/dist/runtime/channels/local-spot-route-transport.js +2 -1
  70. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +2 -2
  71. package/dist/runtime/channels/router-socket-spot-route-transport.js +4 -4
  72. package/dist/runtime/channels/source-spot-router.d.ts +2 -2
  73. package/dist/runtime/channels/source-spot-router.js +4 -4
  74. package/dist/runtime/channels/spot-node-route-transport.d.ts +2 -2
  75. package/dist/runtime/channels/spot-node-route-transport.js +4 -4
  76. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +2 -2
  77. package/dist/runtime/channels/spot-route-bridge-transport.js +4 -4
  78. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +5 -4
  79. package/dist/runtime/channels/spot-route-dispatch-strategy.js +18 -18
  80. package/dist/runtime/execution/serial-execution-queue.d.ts +1 -0
  81. package/dist/runtime/execution/serial-execution-queue.js +1 -0
  82. package/dist/runtime/foundation/actor-join-recovery-codec.js +2 -2
  83. package/dist/runtime/foundation/durable-service-request.d.ts +14 -0
  84. package/dist/runtime/foundation/durable-service-request.js +98 -0
  85. package/dist/runtime/foundation/operation-identity.d.ts +1 -0
  86. package/dist/runtime/foundation/operation-identity.js +5 -1
  87. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +22 -5
  88. package/dist/runtime/foundation/raw-service-mesh-runtime.js +356 -232
  89. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +1 -0
  90. package/dist/runtime/foundation/service-relocation-object-owner.js +1 -0
  91. package/dist/runtime/foundation/service-relocation-runtime.d.ts +2 -0
  92. package/dist/runtime/foundation/service-relocation-runtime.js +13 -1
  93. package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -0
  94. package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -4
  95. package/dist/runtime/foundation/service-stateful-runtime.js +44 -103
  96. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -5
  97. package/dist/runtime/foundation/service-topology-registry.d.ts +12 -2
  98. package/dist/runtime/foundation/service-topology-registry.js +61 -27
  99. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +3 -3
  100. package/dist/runtime/foundation/service-wire-m6a-codec.js +36 -38
  101. package/dist/runtime/handlers/handler-instance-scope.d.ts +8 -0
  102. package/dist/runtime/handlers/handler-instance-scope.js +54 -11
  103. package/dist/runtime/host/actor-runtime-options-factory.js +2 -2
  104. package/dist/runtime/host/application-job-queue.d.ts +19 -7
  105. package/dist/runtime/host/application-job-queue.js +150 -42
  106. package/dist/runtime/host/entry-actor-runtime.d.ts +1 -0
  107. package/dist/runtime/host/entry-actor-runtime.js +3 -0
  108. package/dist/runtime/host/index.d.ts +7 -1
  109. package/dist/runtime/host/index.js +142 -118
  110. package/dist/runtime/host/remote-bound-session-relay.js +2 -18
  111. package/dist/runtime/host/route-mesh-runtime.js +1 -1
  112. package/dist/runtime/host/service-relocation-control.d.ts +1 -0
  113. package/dist/runtime/host/service-relocation-control.js +10 -4
  114. package/dist/runtime/host/service-relocation-host-runtime.d.ts +6 -8
  115. package/dist/runtime/host/service-relocation-host-runtime.js +213 -241
  116. package/dist/runtime/host/spot-address-transport.js +3 -2
  117. package/dist/runtime/host/spot-runtime-options-factory.js +1 -0
  118. package/dist/runtime/locations/actor-location-claims.d.ts +2 -2
  119. package/dist/runtime/locations/actor-location-claims.js +34 -19
  120. package/dist/runtime/locations/lifecycle.d.ts +1 -2
  121. package/dist/runtime/locations/lifecycle.js +2 -5
  122. package/dist/runtime/locations/runtime.d.ts +1 -1
  123. package/dist/runtime/locations/runtime.js +24 -23
  124. package/dist/runtime/messaging/framework-json-v1.d.ts +4 -6
  125. package/dist/runtime/messaging/framework-json-v1.js +38 -294
  126. package/dist/runtime/messaging/packet-name.d.ts +1 -1
  127. package/dist/runtime/messaging/packet-name.js +8 -24
  128. package/dist/runtime/messaging/request-result.d.ts +2 -0
  129. package/dist/runtime/messaging/request-result.js +54 -0
  130. package/dist/runtime/messaging/submission-result.d.ts +1 -0
  131. package/dist/runtime/messaging/submission-result.js +5 -2
  132. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +15 -0
  133. package/dist/runtime/protocol/service_wire_codec.generated.js +144 -0
  134. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +9 -0
  135. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +14 -1
  136. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +7 -2
  137. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +33 -19
  138. package/dist/runtime/spots/index.d.ts +3 -0
  139. package/dist/runtime/spots/index.js +9 -2
  140. package/dist/runtime/spots/spot-activation.d.ts +1 -0
  141. package/dist/runtime/spots/spot-activation.js +17 -8
  142. package/dist/runtime/spots/spot-entry-activation.js +6 -2
  143. package/dist/runtime/spots/spot-handler-registry.d.ts +3 -1
  144. package/dist/runtime/spots/spot-handler-registry.js +18 -8
  145. package/dist/runtime/spots/spot-node-autoconnect.js +4 -3
  146. package/dist/runtime/spots/spot-outbound.d.ts +3 -0
  147. package/dist/runtime/spots/spot-outbound.js +24 -15
  148. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +1 -0
  149. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +1 -1
  150. package/dist/runtime/spots/spot-runtime-ports.d.ts +1 -0
  151. package/dist/runtime/spots/spot-subscription-dispatch.js +0 -3
  152. package/dist/runtime/spots/spot-timer.js +1 -0
  153. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -1
  154. package/dist/runtime/streams/stream-session-runtime.js +13 -27
  155. package/package.json +2 -2
  156. package/src/contracts/Actors/ZLinkActorClient.ts +2 -1
  157. package/src/contracts/Channels/Calls.ts +2 -1
  158. package/src/contracts/Common/ZLinkMessageMetadata.ts +13 -8
  159. package/src/contracts/Handlers/Attributes.ts +14 -21
  160. package/src/contracts/Handlers/JsonContract.ts +8 -216
  161. package/src/contracts/Spots/Contracts.ts +2 -1
  162. package/src/nest-integration.ts +1 -0
  163. package/src/runtime/actors/actor-client.ts +40 -16
  164. package/src/runtime/actors/actor-handoff.ts +9 -4
  165. package/src/runtime/actors/actor-join-relocation.ts +1 -0
  166. package/src/runtime/actors/actor-local-native-join.ts +17 -26
  167. package/src/runtime/actors/actor-remote-wire.ts +17 -0
  168. package/src/runtime/actors/actor-routed-join-transport.ts +14 -4
  169. package/src/runtime/actors/actor-routed-json-request.ts +10 -2
  170. package/src/runtime/actors/index.ts +19 -5
  171. package/src/runtime/actors/spot-actor-dispatch.ts +4 -2
  172. package/src/runtime/admission.ts +18 -5
  173. package/src/runtime/application-jobs/application-ingress-record-owner.ts +41 -25
  174. package/src/runtime/application-jobs/application-job-queue-scope.ts +51 -22
  175. package/src/runtime/application-jobs/contracts.ts +14 -1
  176. package/src/runtime/backend/contracts/index.ts +53 -14
  177. package/src/runtime/backend/mesh-completion-table.ts +156 -57
  178. package/src/runtime/backend/mesh-dispatch-pump.ts +40 -10
  179. package/src/runtime/backend/node/node-backend-adapter-support.ts +17 -8
  180. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +12 -2
  181. package/src/runtime/backend/node/node-raw-binding-port.ts +57 -30
  182. package/src/runtime/backend/node/node-raw-mesh-backend.ts +128 -65
  183. package/src/runtime/backend/node/node-socket-backend-adapter.ts +21 -6
  184. package/src/runtime/backend/raw-binding-port.ts +7 -2
  185. package/src/runtime/backend/runtime-message.ts +6 -0
  186. package/src/runtime/backend/runtime-values.ts +1 -0
  187. package/src/runtime/channels/channel-clients.ts +90 -41
  188. package/src/runtime/channels/channel-dispatch-pipeline.ts +69 -34
  189. package/src/runtime/channels/channel-dispatch-services.ts +69 -1
  190. package/src/runtime/channels/channel-dispatchers.ts +29 -27
  191. package/src/runtime/channels/channel-envelope-inspection.ts +2 -16
  192. package/src/runtime/channels/channel-envelope.ts +133 -8
  193. package/src/runtime/channels/channel-outbound-operations.ts +22 -8
  194. package/src/runtime/channels/channel-runtime-manager.ts +29 -12
  195. package/src/runtime/channels/channel-transports.ts +128 -60
  196. package/src/runtime/channels/local-spot-route-transport.ts +3 -1
  197. package/src/runtime/channels/router-socket-spot-route-transport.ts +13 -4
  198. package/src/runtime/channels/source-spot-router.ts +8 -4
  199. package/src/runtime/channels/spot-node-route-transport.ts +8 -4
  200. package/src/runtime/channels/spot-route-bridge-transport.ts +6 -4
  201. package/src/runtime/channels/spot-route-dispatch-strategy.ts +47 -18
  202. package/src/runtime/execution/serial-execution-queue.ts +2 -0
  203. package/src/runtime/foundation/actor-join-recovery-codec.ts +2 -2
  204. package/src/runtime/foundation/durable-service-request.ts +137 -0
  205. package/src/runtime/foundation/operation-identity.ts +5 -1
  206. package/src/runtime/foundation/raw-service-mesh-runtime.ts +484 -293
  207. package/src/runtime/foundation/service-relocation-object-owner.ts +2 -0
  208. package/src/runtime/foundation/service-relocation-runtime.ts +18 -1
  209. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  210. package/src/runtime/foundation/service-stateful-runtime.ts +67 -125
  211. package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -5
  212. package/src/runtime/foundation/service-topology-registry.ts +91 -36
  213. package/src/runtime/foundation/service-wire-m6a-codec.ts +57 -38
  214. package/src/runtime/handlers/handler-instance-scope.ts +67 -16
  215. package/src/runtime/host/actor-runtime-options-factory.ts +13 -2
  216. package/src/runtime/host/application-job-queue.ts +175 -41
  217. package/src/runtime/host/entry-actor-runtime.ts +4 -0
  218. package/src/runtime/host/index.ts +201 -156
  219. package/src/runtime/host/remote-bound-session-relay.ts +2 -23
  220. package/src/runtime/host/route-mesh-runtime.ts +1 -1
  221. package/src/runtime/host/service-relocation-control.ts +11 -5
  222. package/src/runtime/host/service-relocation-host-runtime.ts +315 -291
  223. package/src/runtime/host/spot-address-transport.ts +4 -2
  224. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  225. package/src/runtime/locations/actor-location-claims.ts +52 -30
  226. package/src/runtime/locations/lifecycle.ts +8 -26
  227. package/src/runtime/locations/runtime.ts +28 -24
  228. package/src/runtime/messaging/framework-json-v1.ts +5 -325
  229. package/src/runtime/messaging/packet-name.ts +5 -25
  230. package/src/runtime/messaging/request-result.ts +58 -0
  231. package/src/runtime/messaging/submission-result.ts +5 -1
  232. package/src/runtime/protocol/service_wire_codec.generated.ts +131 -1
  233. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
  234. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +43 -23
  235. package/src/runtime/spots/index.ts +14 -2
  236. package/src/runtime/spots/spot-activation.ts +28 -10
  237. package/src/runtime/spots/spot-entry-activation.ts +9 -2
  238. package/src/runtime/spots/spot-handler-registry.ts +41 -8
  239. package/src/runtime/spots/spot-node-autoconnect.ts +6 -2
  240. package/src/runtime/spots/spot-outbound.ts +36 -16
  241. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +2 -1
  242. package/src/runtime/spots/spot-runtime-ports.ts +1 -0
  243. package/src/runtime/spots/spot-subscription-dispatch.ts +0 -3
  244. package/src/runtime/spots/spot-timer.ts +5 -1
  245. package/src/runtime/streams/stream-session-runtime.ts +12 -29
@@ -33,6 +33,10 @@ export class ZLinkEntryActorRuntimeService implements ZLinkEntryActorRuntime {
33
33
  return this.options.actorManager()?.getState(actorId)?.actor;
34
34
  }
35
35
 
36
+ async releaseMembers(meshName: string): Promise<void> {
37
+ await this.options.actorManager()?.releaseSpotActorMembers(meshName);
38
+ }
39
+
36
40
  async commitActorTransaction(actor: ZLinkActor, onJoined: () => Promise<void>): Promise<void> {
37
41
  const state = this.options.actorManager()?.getState(actor.context.actorId);
38
42
  const entryNode = this.options.spotNodeRuntime()?.primaryMeshNode;
@@ -3,6 +3,7 @@ import { currentApplicationJobOrigin } from '../application-jobs/application-job
3
3
  import { listAllMeshNodeDescriptors } from '../locations/location-store-pages';
4
4
  import { ZlinkStreamContentType } from '@zlink-systems/stream-wire';
5
5
  import { randomBytes, randomUUID } from 'node:crypto';
6
+ import { isDeepStrictEqual } from 'node:util';
6
7
  import {
7
8
  type ActorRef,
8
9
  type RoutingId,
@@ -244,7 +245,7 @@ export class ZLinkFrameworkRuntimeHost
244
245
  private readonly lifecycleSink?: string[];
245
246
  private executionState?: ZLinkFrameworkExecutionState;
246
247
  private runtimeState = ZLinkFrameworkRuntimeState.Preparing;
247
- private runtimeSequence = 0n;
248
+ private runtimeStatus?: ZLinkFrameworkRuntimeStatus;
248
249
  private readonly runtimeObservationSource = Symbol('zlink.framework-runtime');
249
250
  private runtimeDeadline?: Date;
250
251
  private shutdownDeadline?: AbortController;
@@ -334,24 +335,15 @@ export class ZLinkFrameworkRuntimeHost
334
335
  this.runtimeEventPublisher =
335
336
  options.runtimeEventPublisher ?? new DefaultZLinkRuntimeEventPublisher();
336
337
  this.metrics = new ZLinkRuntimeMetrics(options.registration.metrics?.meterProvider);
338
+ const handlerStartGate = options.providerResolver?.get?.(
339
+ ZLINK_INTERNAL_APPLICATION_JOB_QUEUE_HANDLER_START_GATE as unknown as Type<ZLinkInternalApplicationJobQueueHandlerStartGate>
340
+ );
337
341
  this.applicationJobQueue = new ApplicationJobQueue(
338
342
  resolveApplicationJobQueueConfiguration(options.registration.applicationJobQueue, () =>
339
343
  nodeEffectiveProcessorCount(options.registration.worker?.maxThreads)
340
344
  ),
341
345
  undefined,
342
- () => {
343
- try {
344
- return (
345
- this.options.providerResolver
346
- ?.get?.(
347
- ZLINK_INTERNAL_APPLICATION_JOB_QUEUE_HANDLER_START_GATE as unknown as Type<ZLinkInternalApplicationJobQueueHandlerStartGate>
348
- )
349
- ?.shouldHoldPermitBeforeHandler() === true
350
- );
351
- } catch {
352
- return false;
353
- }
354
- },
346
+ handlerStartGate?.shouldHoldPermitBeforeHandler.bind(handlerStartGate),
355
347
  (error) =>
356
348
  (this.errorSink ?? this.preStartErrorSink).reportRuntimeTaskException(
357
349
  'logger-provider',
@@ -786,6 +778,7 @@ export class ZLinkFrameworkRuntimeHost
786
778
  this.serviceRelocation = new ZLinkHostServiceRelocationRuntime({
787
779
  registration: options.registration,
788
780
  targetAdmissionSealed: () => this.shutdownOperationStartedAt !== undefined,
781
+ sourceQuiescenceChanged: () => this.publishCurrentRuntimeStatus(),
789
782
  providerResolver: options.providerResolver,
790
783
  locationStore: () => this.locationOwner.currentStores?.locationStore,
791
784
  currentOwner: () => this.locationOwner.currentRuntime?.currentOwnerToken,
@@ -876,6 +869,10 @@ export class ZLinkFrameworkRuntimeHost
876
869
  }
877
870
 
878
871
  get status(): ZLinkFrameworkRuntimeStatus {
872
+ return this.publishCurrentRuntimeStatus();
873
+ }
874
+
875
+ private buildRuntimeStatus(): ZLinkFrameworkRuntimeStatus {
879
876
  return {
880
877
  state: this.runtimeState,
881
878
  isReady: runtimeStateIsReady(this.runtimeState),
@@ -885,8 +882,8 @@ export class ZLinkFrameworkRuntimeHost
885
882
  deadline: this.runtimeDeadline,
886
883
  relocationResult: this.runtimeRelocationResult,
887
884
  terminationResult: this.runtimeTerminationResult,
888
- sequence: this.runtimeSequence,
889
- observedAt: new Date()
885
+ sequence: this.runtimeStatus?.sequence ?? 0n,
886
+ observedAt: this.runtimeStatus?.observedAt ?? new Date()
890
887
  };
891
888
  }
892
889
 
@@ -904,10 +901,11 @@ export class ZLinkFrameworkRuntimeHost
904
901
  }
905
902
 
906
903
  observe(signal?: AbortSignal): AsyncIterable<ZLinkObservedStatus<ZLinkFrameworkRuntimeStatus>> {
904
+ const status = this.status;
907
905
  const queue = new RuntimeEventQueue<ZLinkFrameworkRuntimeStatus>(undefined, signal);
908
906
  this.runtimeObservers.add(queue);
909
907
  queue.onClose(() => this.runtimeObservers.delete(queue));
910
- this.publishRuntimeStatus(queue, this.status);
908
+ this.publishRuntimeStatus(queue, status);
911
909
  return queue;
912
910
  }
913
911
 
@@ -1130,6 +1128,9 @@ export class ZLinkFrameworkRuntimeHost
1130
1128
  );
1131
1129
  const drain = await shutdown;
1132
1130
  await unscopedStreamDrain;
1131
+ if (drain.kind === 'forceStopped' && drain.error !== undefined) {
1132
+ this.runtimeOrPreStartErrorSink.reportRuntimeTaskException('host shutdown', drain.error);
1133
+ }
1133
1134
  await this.stop();
1134
1135
  if (drain.kind === 'forceStopped') {
1135
1136
  return this.completeTermination({
@@ -1277,12 +1278,25 @@ export class ZLinkFrameworkRuntimeHost
1277
1278
  private setRuntimeState(state: ZLinkFrameworkRuntimeState): void {
1278
1279
  if (this.runtimeState === state) return;
1279
1280
  this.runtimeState = state;
1280
- this.runtimeSequence += 1n;
1281
1281
  this.notifyTopologyHostStateChanged();
1282
- const status = this.status;
1282
+ this.publishCurrentRuntimeStatus();
1283
+ }
1284
+
1285
+ private publishCurrentRuntimeStatus(): ZLinkFrameworkRuntimeStatus {
1286
+ const current = this.buildRuntimeStatus();
1287
+ if (this.runtimeStatus !== undefined && isDeepStrictEqual(this.runtimeStatus, current)) {
1288
+ return this.runtimeStatus;
1289
+ }
1290
+ const status = {
1291
+ ...current,
1292
+ sequence: (this.runtimeStatus?.sequence ?? 0n) + 1n,
1293
+ observedAt: new Date()
1294
+ };
1295
+ this.runtimeStatus = status;
1283
1296
  for (const observer of this.runtimeObservers) {
1284
1297
  this.publishRuntimeStatus(observer, status);
1285
1298
  }
1299
+ return status;
1286
1300
  }
1287
1301
 
1288
1302
  private publishRuntimeStatus(
@@ -2781,13 +2795,68 @@ export class ZLinkFrameworkRuntimeHost
2781
2795
  };
2782
2796
  }
2783
2797
 
2784
- private async dispatchMeshRecord(
2798
+ private dispatchMeshNodeRecord(
2799
+ meshName: string,
2800
+ record: ReceiveRecord,
2801
+ signal?: AbortSignal
2802
+ ): void | Promise<void> {
2803
+ const control = this.serviceRelocation.tryHandleControl(meshName, record, signal);
2804
+ if (control instanceof Promise)
2805
+ return control.then((handled) =>
2806
+ handled ? undefined : this.dispatchMeshNodePayload(meshName, record, signal)
2807
+ );
2808
+ if (!control) return this.dispatchMeshNodePayload(meshName, record, signal);
2809
+ }
2810
+
2811
+ private dispatchMeshNodePayload(
2812
+ meshName: string,
2813
+ record: ReceiveRecord,
2814
+ signal?: AbortSignal
2815
+ ): void | Promise<void> {
2816
+ if (record.kind === ReceiveKind.NodeRequest && record.parts.length === 1) {
2817
+ const terminal = decodeRemoteActorSourceLeaveTerminal(record.parts[0]!.data());
2818
+ if (terminal !== undefined) {
2819
+ return this.completeFormalSourceLeave(record, terminal);
2820
+ }
2821
+ }
2822
+ const channelRuntime = this.channelRuntime;
2823
+ if (channelRuntime === undefined) {
2824
+ throw new ZLinkConfigurationException(
2825
+ 'MeshNode node-direct dispatch requires the channel runtime.'
2826
+ );
2827
+ }
2828
+ return channelRuntime.dispatchMeshRoute(meshName, record, signal);
2829
+ }
2830
+
2831
+ private async completeFormalSourceLeave(
2832
+ record: ReceiveRecord,
2833
+ terminal: NonNullable<ReturnType<typeof decodeRemoteActorSourceLeaveTerminal>>
2834
+ ): Promise<void> {
2835
+ const completed = await this.spotManager?.completeFormalSourceLeaveTerminal(
2836
+ terminal.actorId,
2837
+ terminal.transferId,
2838
+ terminal.succeeded
2839
+ );
2840
+ if (completed !== true) {
2841
+ throw new ZLinkConfigurationException(
2842
+ `Actor '${terminal.actorId}' has no matching formal transfer terminal gate.`
2843
+ );
2844
+ }
2845
+ if (record.reply(Buffer.alloc(0)) !== SubmitResult.Ok) {
2846
+ throw new ZLinkConfigurationException(
2847
+ `Actor '${terminal.actorId}' formal transfer terminal acknowledgement failed.`
2848
+ );
2849
+ }
2850
+ return;
2851
+ }
2852
+
2853
+ private dispatchMeshRecord(
2785
2854
  applicationJobOrigin: () => ApplicationJobOrigin,
2786
2855
  meshName: string,
2787
2856
  owner: ReadyRecord,
2788
2857
  record: ReceiveRecord,
2789
2858
  signal?: AbortSignal
2790
- ): Promise<void> {
2859
+ ): void | Promise<void> {
2791
2860
  if (record.operationKind === OperationKind.ActorJoin) {
2792
2861
  if (this.spotManager === undefined) {
2793
2862
  throw new ZLinkConfigurationException(
@@ -2796,107 +2865,8 @@ export class ZLinkFrameworkRuntimeHost
2796
2865
  }
2797
2866
  return this.spotManager.dispatchMeshActorJoin(meshName, owner, record);
2798
2867
  }
2799
- if (record.kind === ReceiveKind.ActorBinding) {
2800
- if (record.kindData?.kind !== 'actorBinding') {
2801
- throw new ZLinkConfigurationException(
2802
- 'MeshNode Actor binding record has no binding generation.'
2803
- );
2804
- }
2805
- const binding = record.kindData;
2806
- const replyBindingFailure = (kind: ZLinkFrameworkInternalErrorKind): void => {
2807
- const terminal = internalFrameworkWireReply(kind);
2808
- if (
2809
- record.replyFailure?.(terminal.terminalResult, terminal.failureCode) !== SubmitResult.Ok
2810
- ) {
2811
- throw new ZLinkConfigurationException(
2812
- `MeshNode Actor binding target '${binding.actor.actorId}' failure acknowledgement failed.`
2813
- );
2814
- }
2815
- };
2816
- const replyBindingSuccess = (): void => {
2817
- if (record.reply(Buffer.alloc(0)) !== SubmitResult.Ok) {
2818
- throw new ZLinkConfigurationException(
2819
- `MeshNode Actor binding target '${binding.actor.actorId}' acknowledgement failed.`
2820
- );
2821
- }
2822
- };
2823
- const state = this.actorManager?.getState(binding.actor.actorId);
2824
- const nativeActorRef = state?.nativeActorRef;
2825
- if (
2826
- state === undefined ||
2827
- nativeActorRef === undefined ||
2828
- nativeActorRef.actorId !== binding.actor.actorId ||
2829
- !routingIdsEqual(nativeActorRef.nodeRid, binding.actor.nodeRid)
2830
- ) {
2831
- replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2832
- return;
2833
- }
2834
- if (nativeActorRef.generation !== binding.actor.generation) {
2835
- replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorGenerationStale);
2836
- return;
2837
- }
2838
- const node =
2839
- state.meshName === meshName ? this.spotNodeRuntime?.meshNode(meshName) : undefined;
2840
- const localStatus = node?.status();
2841
- // ownsLocation tracks local cleanup responsibility, not Actor authority.
2842
- // A relocation target can exact-read committed authority without owning that cleanup.
2843
- if (
2844
- localStatus === undefined ||
2845
- !routingIdsEqual(localStatus.routingId, binding.actor.nodeRid) ||
2846
- localStatus.lifecycleGeneration !== binding.actorNodeGeneration ||
2847
- state.locationGeneration !== binding.authorityOwnerGeneration ||
2848
- state.isMoving
2849
- ) {
2850
- replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2851
- return;
2852
- }
2853
- const sessionOwnerIsLocal =
2854
- routingIdsEqual(localStatus.routingId, binding.sessionNodeRid) &&
2855
- localStatus.lifecycleGeneration === binding.sessionOwnerNodeGeneration;
2856
- if (binding.transition === 'tombstone') {
2857
- const actorRef: ActorRef = {
2858
- actorId: binding.actor.actorId,
2859
- objectGeneration: binding.actor.generation,
2860
- meshName,
2861
- nodeRid: binding.actor.nodeRid
2862
- };
2863
- if (sessionOwnerIsLocal) {
2864
- releaseApplicationJobPermitBeforeHandler();
2865
- await this.streamBindingRuntime.retireRemoteBinding(
2866
- actorRef,
2867
- binding.sessionRid,
2868
- binding.bindingGeneration,
2869
- signal
2870
- );
2871
- }
2872
- state.retireBoundSessionBinding(binding);
2873
- replyBindingSuccess();
2874
- return;
2875
- }
2876
- const target = this.boundSessionRelay.boundSessions.resolveRemoteBoundSessionTarget(
2877
- binding.sessionNodeRid,
2878
- binding.sessionRid
2879
- );
2880
- if (target === undefined) {
2881
- replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2882
- return;
2883
- }
2884
- const installed = state.installBoundSessionBinding({
2885
- ...target,
2886
- sessionNodeRid: binding.sessionNodeRid,
2887
- sessionRid: binding.sessionRid,
2888
- sessionOwnerNodeGeneration: binding.sessionOwnerNodeGeneration,
2889
- sessionOwnerId: binding.sessionOwnerId,
2890
- sessionOwnerLeaseGeneration: binding.sessionOwnerLeaseGeneration,
2891
- bindingGeneration: binding.bindingGeneration
2892
- });
2893
- if (!installed) {
2894
- replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2895
- return;
2896
- }
2897
- replyBindingSuccess();
2898
- return Promise.resolve();
2899
- }
2868
+ if (record.kind === ReceiveKind.ActorBinding)
2869
+ return this.dispatchMeshActorBinding(meshName, record, signal);
2900
2870
  switch (record.kind) {
2901
2871
  case ReceiveKind.TransferControl:
2902
2872
  if (record.kindData?.kind !== 'transferControl') {
@@ -2919,41 +2889,11 @@ export class ZLinkFrameworkRuntimeHost
2919
2889
  }
2920
2890
  case ReceiveKind.NodeSend:
2921
2891
  case ReceiveKind.NodeRequest: {
2922
- return this.admission.run(meshName, 'RouteMesh node dispatch', async () => {
2923
- if (await this.serviceRelocation.tryHandleControl(meshName, record, signal)) {
2924
- return;
2925
- }
2926
- if (record.kind === ReceiveKind.NodeRequest && record.parts.length === 1) {
2927
- const terminal = decodeRemoteActorSourceLeaveTerminal(record.parts[0]!.data());
2928
- if (terminal !== undefined) {
2929
- if (
2930
- (await this.spotManager?.completeFormalSourceLeaveTerminal(
2931
- terminal.actorId,
2932
- terminal.transferId,
2933
- terminal.succeeded
2934
- )) !== true
2935
- ) {
2936
- throw new ZLinkConfigurationException(
2937
- `Actor '${terminal.actorId}' has no matching formal transfer terminal gate.`
2938
- );
2939
- }
2940
- if (record.reply(Buffer.alloc(0)) !== SubmitResult.Ok) {
2941
- throw new ZLinkConfigurationException(
2942
- `Actor '${terminal.actorId}' formal transfer terminal acknowledgement failed.`
2943
- );
2944
- }
2945
- return;
2946
- }
2947
- }
2948
- const channelRuntime = this.channelRuntime;
2949
- if (channelRuntime === undefined) {
2950
- throw new ZLinkConfigurationException(
2951
- 'MeshNode node-direct dispatch requires the channel runtime.'
2952
- );
2953
- }
2954
- return await channelRuntime.dispatchMeshRoute(meshName, record, signal);
2955
- });
2892
+ return this.admission.run(meshName, 'RouteMesh node dispatch', () =>
2893
+ this.dispatchMeshNodeRecord(meshName, record, signal)
2894
+ );
2956
2895
  }
2896
+
2957
2897
  case ReceiveKind.SpotSend:
2958
2898
  case ReceiveKind.SpotRequest:
2959
2899
  case ReceiveKind.SpotMulticast:
@@ -2995,6 +2935,111 @@ export class ZLinkFrameworkRuntimeHost
2995
2935
  }
2996
2936
  }
2997
2937
 
2938
+ private async dispatchMeshActorBinding(
2939
+ meshName: string,
2940
+ record: ReceiveRecord,
2941
+ signal?: AbortSignal
2942
+ ): Promise<void> {
2943
+ if (record.kindData?.kind !== 'actorBinding') {
2944
+ throw new ZLinkConfigurationException(
2945
+ 'MeshNode Actor binding record has no binding generation.'
2946
+ );
2947
+ }
2948
+ const binding = record.kindData;
2949
+ const replyBindingFailure = (kind: ZLinkFrameworkInternalErrorKind): void => {
2950
+ const terminal = internalFrameworkWireReply(kind);
2951
+ if (
2952
+ record.replyFailure?.(terminal.terminalResult, terminal.failureCode) !== SubmitResult.Ok
2953
+ ) {
2954
+ throw new ZLinkConfigurationException(
2955
+ `MeshNode Actor binding target '${binding.actor.actorId}' failure acknowledgement failed.`
2956
+ );
2957
+ }
2958
+ };
2959
+ const replyBindingSuccess = (): void => {
2960
+ if (record.reply(Buffer.alloc(0)) !== SubmitResult.Ok) {
2961
+ throw new ZLinkConfigurationException(
2962
+ `MeshNode Actor binding target '${binding.actor.actorId}' acknowledgement failed.`
2963
+ );
2964
+ }
2965
+ };
2966
+ const state = this.actorManager?.getState(binding.actor.actorId);
2967
+ const nativeActorRef = state?.nativeActorRef;
2968
+ if (
2969
+ state === undefined ||
2970
+ nativeActorRef === undefined ||
2971
+ nativeActorRef.actorId !== binding.actor.actorId ||
2972
+ !routingIdsEqual(nativeActorRef.nodeRid, binding.actor.nodeRid)
2973
+ ) {
2974
+ replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2975
+ return;
2976
+ }
2977
+ if (nativeActorRef.generation !== binding.actor.generation) {
2978
+ replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorGenerationStale);
2979
+ return;
2980
+ }
2981
+ const node = state.meshName === meshName ? this.spotNodeRuntime?.meshNode(meshName) : undefined;
2982
+ const localStatus = node?.status();
2983
+ // ownsLocation tracks local cleanup responsibility, not Actor authority.
2984
+ // A relocation target can exact-read committed authority without owning that cleanup.
2985
+ if (
2986
+ localStatus === undefined ||
2987
+ !routingIdsEqual(localStatus.routingId, binding.actor.nodeRid) ||
2988
+ localStatus.lifecycleGeneration !== binding.actorNodeGeneration ||
2989
+ state.locationGeneration !== binding.authorityOwnerGeneration ||
2990
+ state.isMoving
2991
+ ) {
2992
+ replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
2993
+ return;
2994
+ }
2995
+ const sessionOwnerIsLocal =
2996
+ routingIdsEqual(localStatus.routingId, binding.sessionNodeRid) &&
2997
+ localStatus.lifecycleGeneration === binding.sessionOwnerNodeGeneration;
2998
+ if (binding.transition === 'tombstone') {
2999
+ const actorRef: ActorRef = {
3000
+ actorId: binding.actor.actorId,
3001
+ objectGeneration: binding.actor.generation,
3002
+ meshName,
3003
+ nodeRid: binding.actor.nodeRid
3004
+ };
3005
+ if (sessionOwnerIsLocal) {
3006
+ releaseApplicationJobPermitBeforeHandler();
3007
+ await this.streamBindingRuntime.retireRemoteBinding(
3008
+ actorRef,
3009
+ binding.sessionRid,
3010
+ binding.bindingGeneration,
3011
+ signal
3012
+ );
3013
+ }
3014
+ state.retireBoundSessionBinding(binding);
3015
+ replyBindingSuccess();
3016
+ return;
3017
+ }
3018
+ const target = this.boundSessionRelay.boundSessions.resolveRemoteBoundSessionTarget(
3019
+ binding.sessionNodeRid,
3020
+ binding.sessionRid
3021
+ );
3022
+ if (target === undefined) {
3023
+ replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
3024
+ return;
3025
+ }
3026
+ const installed = state.installBoundSessionBinding({
3027
+ ...target,
3028
+ sessionNodeRid: binding.sessionNodeRid,
3029
+ sessionRid: binding.sessionRid,
3030
+ sessionOwnerNodeGeneration: binding.sessionOwnerNodeGeneration,
3031
+ sessionOwnerId: binding.sessionOwnerId,
3032
+ sessionOwnerLeaseGeneration: binding.sessionOwnerLeaseGeneration,
3033
+ bindingGeneration: binding.bindingGeneration
3034
+ });
3035
+ if (!installed) {
3036
+ replyBindingFailure(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
3037
+ return;
3038
+ }
3039
+ replyBindingSuccess();
3040
+ return Promise.resolve();
3041
+ }
3042
+
2998
3043
  private submitLocalMeshRoute(
2999
3044
  meshName: string,
3000
3045
  sourceNodeRid: string,
@@ -845,24 +845,16 @@ function validateSessionRelocationRouteAgainstSeal(
845
845
  route: ServiceSessionRelocationRoute,
846
846
  sealed: ServiceSessionRelocationSealed
847
847
  ): void {
848
- const sameCoordinator =
849
- route.coordinator.ownerId === sealed.coordinator.ownerId &&
850
- route.coordinator.leaseGeneration === sealed.coordinator.leaseGeneration &&
851
- route.coordinator.nodeRid === sealed.coordinator.nodeRid &&
852
- route.coordinator.nodeGeneration === sealed.coordinator.nodeGeneration &&
853
- route.coordinator.expectedAuthorityStoreVersion ===
854
- sealed.coordinator.expectedAuthorityStoreVersion;
855
848
  const sameSession =
856
- route.session.sessionOwnerNodeRid === sealed.session.sessionOwnerNodeRid &&
849
+ routingIdsEqual(route.session.sessionOwnerNodeRid, sealed.session.sessionOwnerNodeRid) &&
857
850
  route.session.sessionOwnerNodeGeneration === sealed.session.sessionOwnerNodeGeneration &&
858
851
  route.session.sessionOwnerId === sealed.session.sessionOwnerId &&
859
852
  route.session.sessionOwnerLeaseGeneration === sealed.session.sessionOwnerLeaseGeneration &&
860
- route.session.sessionRid === sealed.session.sessionRid &&
853
+ routingIdsEqual(route.session.sessionRid, sealed.session.sessionRid) &&
861
854
  route.session.bindingGeneration === sealed.session.bindingGeneration;
862
855
  if (
863
856
  route.relocation.high !== sealed.relocation.high ||
864
857
  route.relocation.low !== sealed.relocation.low ||
865
- !sameCoordinator ||
866
858
  !sameSession ||
867
859
  route.actor.actorId !== sealed.actor.actor.actorId ||
868
860
  route.actor.generation !== sealed.actor.actor.generation
@@ -871,17 +863,4 @@ function validateSessionRelocationRouteAgainstSeal(
871
863
  `Session relocation '${serviceWireBarrierKey(route)}' changed its command 42 fence.`
872
864
  );
873
865
  }
874
- if (route.route.action === 'commit') {
875
- if (route.route.previousAuthorityOwnerGeneration !== sealed.actor.authorityOwnerGeneration) {
876
- throw new ServiceWireProtocolError(
877
- `Session relocation '${serviceWireBarrierKey(route)}' changed its source authority fence.`
878
- );
879
- }
880
- return;
881
- }
882
- if (route.route.currentAuthorityOwnerGeneration !== sealed.actor.authorityOwnerGeneration) {
883
- throw new ServiceWireProtocolError(
884
- `Session relocation '${serviceWireBarrierKey(route)}' abort changed its authority fence.`
885
- );
886
- }
887
866
  }
@@ -588,7 +588,7 @@ export class ZLinkRouteMeshRuntimeCoordinator implements ZLinkRouteMeshRuntime {
588
588
  this.options.forceStopResources(meshName).catch(() => undefined)
589
589
  )
590
590
  );
591
- result = { kind: 'forceStopped', reason };
591
+ result = { kind: 'forceStopped', reason, error };
592
592
  for (const [meshName, state] of entries) {
593
593
  this.transition(meshName, state, ZLinkTopologyState.Failed);
594
594
  }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  SERVICE_WIRE_MAJOR_OFFSET,
3
+ SERVICE_WIRE_COMMAND_OFFSET,
3
4
  SERVICE_WIRE_PREFIX_SIZE
4
5
  } from '../foundation/service-wire-binary-primitives';
5
6
  import {
@@ -27,14 +28,19 @@ export function decodeServiceRelocationControlRequest(
27
28
  const bytes = Buffer.isBuffer(payload)
28
29
  ? payload
29
30
  : Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength);
31
+ if (serviceRelocationControlCommand(bytes) === undefined) return undefined;
32
+ return decodeMaintenanceRelocationControl(bytes);
33
+ }
34
+
35
+ export function serviceRelocationControlCommand(payload: Uint8Array): number | undefined {
30
36
  if (
31
- bytes.byteLength < SERVICE_WIRE_PREFIX_SIZE ||
32
- bytes[0] !== SERVICE_WIRE_MAGIC[0] ||
33
- bytes[1] !== SERVICE_WIRE_MAGIC[1] ||
34
- bytes[SERVICE_WIRE_MAJOR_OFFSET] !== SERVICE_WIRE_MAJOR
37
+ payload.byteLength < SERVICE_WIRE_PREFIX_SIZE ||
38
+ payload[0] !== SERVICE_WIRE_MAGIC[0] ||
39
+ payload[1] !== SERVICE_WIRE_MAGIC[1] ||
40
+ payload[SERVICE_WIRE_MAJOR_OFFSET] !== SERVICE_WIRE_MAJOR
35
41
  )
36
42
  return undefined;
37
- return decodeMaintenanceRelocationControl(bytes);
43
+ return payload[SERVICE_WIRE_COMMAND_OFFSET];
38
44
  }
39
45
 
40
46
  export function encodeServiceRelocationControlResponse(