@zlink-systems/framework 0.24.0 → 0.26.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 (215) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Channels/IZLinkFanoutClient.d.ts +0 -6
  3. package/dist/contracts/RouteMesh/RuntimeTopology.d.ts +8 -0
  4. package/dist/internal.d.ts +1 -0
  5. package/dist/internal.js +1 -0
  6. package/dist/runtime/activation-admission.d.ts +29 -0
  7. package/dist/runtime/activation-admission.js +61 -0
  8. package/dist/runtime/actors/actor-context.js +6 -6
  9. package/dist/runtime/actors/actor-handoff.d.ts +9 -3
  10. package/dist/runtime/actors/actor-handoff.js +28 -17
  11. package/dist/runtime/actors/actor-local-native-join.js +2 -18
  12. package/dist/runtime/actors/actor-remote-joiner.d.ts +2 -2
  13. package/dist/runtime/actors/actor-remote-joiner.js +6 -8
  14. package/dist/runtime/actors/actor-runtime-contracts.d.ts +4 -2
  15. package/dist/runtime/actors/actor-source-transfer.d.ts +5 -5
  16. package/dist/runtime/actors/deferred-join-completion.d.ts +14 -0
  17. package/dist/runtime/actors/deferred-join-completion.js +20 -0
  18. package/dist/runtime/actors/index.d.ts +1 -1
  19. package/dist/runtime/actors/index.js +15 -5
  20. package/dist/runtime/admission.d.ts +5 -1
  21. package/dist/runtime/admission.js +4 -0
  22. package/dist/runtime/backend/contracts/index.d.ts +2 -10
  23. package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -9
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +1 -2
  25. package/dist/runtime/backend/node/node-backend-adapter-factory.js +4 -18
  26. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +6 -3
  27. package/dist/runtime/backend/node/node-backend-adapter-support.js +12 -28
  28. package/dist/runtime/backend/node/node-event-loop-poller.d.ts +1 -1
  29. package/dist/runtime/backend/node/node-event-loop-poller.js +8 -2
  30. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +1 -1
  31. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -3
  32. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +12 -9
  33. package/dist/runtime/backend/node/node-raw-mesh-backend.js +61 -29
  34. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  35. package/dist/runtime/backend/raw-binding-port.d.ts +12 -0
  36. package/dist/runtime/channels/channel-clients.d.ts +0 -1
  37. package/dist/runtime/channels/channel-clients.js +0 -8
  38. package/dist/runtime/channels/channel-outbound-operations.js +2 -10
  39. package/dist/runtime/channels/channel-runtime-manager.d.ts +2 -5
  40. package/dist/runtime/channels/channel-runtime-manager.js +1 -12
  41. package/dist/runtime/channels/channel-socket-registry.d.ts +17 -4
  42. package/dist/runtime/channels/channel-socket-registry.js +49 -62
  43. package/dist/runtime/channels/channel-transports.d.ts +0 -3
  44. package/dist/runtime/channels/fanout-location-runtime.js +3 -9
  45. package/dist/runtime/execution/index.d.ts +18 -8
  46. package/dist/runtime/execution/index.js +55 -29
  47. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -6
  48. package/dist/runtime/execution/serial-execution-queue.js +49 -15
  49. package/dist/runtime/foundation/listener-records.d.ts +11 -0
  50. package/dist/runtime/foundation/listener-records.js +20 -0
  51. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +10 -21
  52. package/dist/runtime/foundation/raw-service-mesh-runtime.js +66 -392
  53. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +1 -0
  54. package/dist/runtime/foundation/service-authority-payload-codec.js +8 -0
  55. package/dist/runtime/foundation/service-maintenance-runtime.js +23 -2
  56. package/dist/runtime/foundation/service-relocation-coordinator.d.ts +9 -0
  57. package/dist/runtime/foundation/service-relocation-coordinator.js +14 -1
  58. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +8 -0
  59. package/dist/runtime/foundation/service-relocation-object-owner.js +16 -4
  60. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +6 -13
  61. package/dist/runtime/foundation/service-stateful-runtime.d.ts +10 -7
  62. package/dist/runtime/foundation/service-stateful-runtime.js +100 -60
  63. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +9 -4
  64. package/dist/runtime/foundation/service-stateful-wire-codec.js +153 -90
  65. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -2
  66. package/dist/runtime/foundation/service-topology-registry.js +6 -30
  67. package/dist/runtime/host/actor-transfer-runtime.d.ts +17 -15
  68. package/dist/runtime/host/actor-transfer-runtime.js +73 -86
  69. package/dist/runtime/host/index.d.ts +6 -2
  70. package/dist/runtime/host/index.js +63 -37
  71. package/dist/runtime/host/remote-bound-session-relay.js +4 -3
  72. package/dist/runtime/host/route-mesh-runtime.d.ts +2 -0
  73. package/dist/runtime/host/route-mesh-runtime.js +7 -5
  74. package/dist/runtime/host/service-relocation-host-runtime.d.ts +50 -8
  75. package/dist/runtime/host/service-relocation-host-runtime.js +343 -270
  76. package/dist/runtime/host/spot-runtime-options-factory.d.ts +7 -4
  77. package/dist/runtime/host/spot-runtime-options-factory.js +4 -8
  78. package/dist/runtime/host/stateful-authority-route-runtime.js +6 -2
  79. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +11 -3
  80. package/dist/runtime/host/user-spot-creation-coordinator.js +85 -87
  81. package/dist/runtime/locations/in-memory-provider-location-store.js +8 -6
  82. package/dist/runtime/locations/lifecycle.d.ts +1 -1
  83. package/dist/runtime/locations/lifecycle.js +2 -2
  84. package/dist/runtime/locations/resolvers.js +7 -2
  85. package/dist/runtime/locations/spot-location-claims.d.ts +2 -2
  86. package/dist/runtime/locations/spot-location-claims.js +3 -27
  87. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1 -0
  88. package/dist/runtime/protocol/service_wire_codec.generated.js +5 -1
  89. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +1 -0
  90. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +3 -2
  91. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +3 -3
  92. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +4 -4
  93. package/dist/runtime/spots/formal-remote-actor-transfer-registry.d.ts +3 -3
  94. package/dist/runtime/spots/index.d.ts +25 -19
  95. package/dist/runtime/spots/index.js +207 -205
  96. package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
  97. package/dist/runtime/spots/spot-activation-registry.js +34 -31
  98. package/dist/runtime/spots/spot-activation-state.d.ts +2 -11
  99. package/dist/runtime/spots/spot-activation-state.js +4 -17
  100. package/dist/runtime/spots/spot-activation.d.ts +8 -6
  101. package/dist/runtime/spots/spot-activation.js +54 -49
  102. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -1
  103. package/dist/runtime/spots/spot-actor-admission-coordinator.js +1 -1
  104. package/dist/runtime/spots/spot-actor-join-dispatch.d.ts +2 -2
  105. package/dist/runtime/spots/spot-actor-join-dispatch.js +3 -5
  106. package/dist/runtime/spots/spot-actor-lifecycle-drain.js +2 -2
  107. package/dist/runtime/spots/spot-actor-membership.d.ts +4 -0
  108. package/dist/runtime/spots/spot-actor-membership.js +26 -12
  109. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -2
  110. package/dist/runtime/spots/spot-entry-activation.js +0 -1
  111. package/dist/runtime/spots/spot-manager-public.js +1 -1
  112. package/dist/runtime/spots/spot-native-actor-join-admission.d.ts +1 -1
  113. package/dist/runtime/spots/spot-native-actor-join-admission.js +2 -2
  114. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +5 -5
  115. package/dist/runtime/spots/spot-node-runtime-manager.js +7 -11
  116. package/dist/runtime/spots/spot-outbound.js +1 -29
  117. package/dist/runtime/spots/spot-routed-actor-admission.d.ts +1 -0
  118. package/dist/runtime/spots/spot-routed-actor-admission.js +10 -0
  119. package/dist/runtime/spots/spot-routed-frame-dispatch.d.ts +2 -6
  120. package/dist/runtime/spots/spot-routed-frame-dispatch.js +2 -70
  121. package/dist/runtime/spots/spot-runtime-ports.d.ts +3 -5
  122. package/dist/runtime/spots/spot-serial-executor.d.ts +5 -0
  123. package/dist/runtime/spots/spot-serial-executor.js +8 -1
  124. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  125. package/dist/runtime/spots/spot-serial-turn-executor.js +8 -4
  126. package/dist/runtime/streams/actor-session-binding-registry.d.ts +13 -15
  127. package/dist/runtime/streams/actor-session-binding-registry.js +19 -171
  128. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +3 -4
  129. package/dist/runtime/streams/bound-session-service.js +1 -18
  130. package/dist/runtime/streams/index.d.ts +2 -0
  131. package/dist/runtime/streams/index.js +19 -19
  132. package/dist/runtime/streams/session-serial-executor.js +2 -2
  133. package/package.json +3 -3
  134. package/src/contracts/Channels/IZLinkFanoutClient.ts +0 -7
  135. package/src/contracts/RouteMesh/RuntimeTopology.ts +10 -0
  136. package/src/internal.ts +1 -0
  137. package/src/runtime/activation-admission.ts +78 -0
  138. package/src/runtime/actors/actor-context.ts +6 -6
  139. package/src/runtime/actors/actor-handoff.ts +31 -23
  140. package/src/runtime/actors/actor-local-native-join.ts +3 -42
  141. package/src/runtime/actors/actor-remote-joiner.ts +6 -25
  142. package/src/runtime/actors/actor-runtime-contracts.ts +4 -10
  143. package/src/runtime/actors/actor-source-transfer.ts +5 -18
  144. package/src/runtime/actors/deferred-join-completion.ts +40 -0
  145. package/src/runtime/actors/index.ts +22 -9
  146. package/src/runtime/admission.ts +5 -1
  147. package/src/runtime/backend/contracts/index.ts +2 -13
  148. package/src/runtime/backend/mesh-actor-session-node-adapter.ts +2 -10
  149. package/src/runtime/backend/node/node-backend-adapter-factory.ts +5 -25
  150. package/src/runtime/backend/node/node-backend-adapter-support.ts +9 -29
  151. package/src/runtime/backend/node/node-event-loop-poller.ts +14 -2
  152. package/src/runtime/backend/node/node-raw-binding-port.ts +17 -3
  153. package/src/runtime/backend/node/node-raw-mesh-backend.ts +65 -32
  154. package/src/runtime/backend/node/node-socket-backend-adapter.ts +2 -2
  155. package/src/runtime/backend/raw-binding-port.ts +13 -0
  156. package/src/runtime/channels/channel-clients.ts +0 -11
  157. package/src/runtime/channels/channel-outbound-operations.ts +3 -11
  158. package/src/runtime/channels/channel-runtime-manager.ts +4 -15
  159. package/src/runtime/channels/channel-socket-registry.ts +69 -71
  160. package/src/runtime/channels/channel-transports.ts +0 -3
  161. package/src/runtime/channels/fanout-location-runtime.ts +4 -16
  162. package/src/runtime/execution/index.ts +68 -30
  163. package/src/runtime/execution/serial-execution-queue.ts +52 -20
  164. package/src/runtime/foundation/listener-records.ts +21 -0
  165. package/src/runtime/foundation/raw-service-mesh-runtime.ts +66 -466
  166. package/src/runtime/foundation/service-authority-payload-codec.ts +10 -0
  167. package/src/runtime/foundation/service-maintenance-runtime.ts +30 -9
  168. package/src/runtime/foundation/service-relocation-coordinator.ts +13 -0
  169. package/src/runtime/foundation/service-relocation-object-owner.ts +18 -5
  170. package/src/runtime/foundation/service-session-binding-ingress-port.ts +6 -14
  171. package/src/runtime/foundation/service-stateful-runtime.ts +151 -89
  172. package/src/runtime/foundation/service-stateful-wire-codec.ts +203 -121
  173. package/src/runtime/foundation/service-topology-registry.ts +8 -48
  174. package/src/runtime/host/actor-transfer-runtime.ts +94 -166
  175. package/src/runtime/host/index.ts +119 -54
  176. package/src/runtime/host/remote-bound-session-relay.ts +4 -3
  177. package/src/runtime/host/route-mesh-runtime.ts +9 -5
  178. package/src/runtime/host/service-relocation-host-runtime.ts +495 -376
  179. package/src/runtime/host/spot-runtime-options-factory.ts +24 -13
  180. package/src/runtime/host/stateful-authority-route-runtime.ts +8 -4
  181. package/src/runtime/host/user-spot-creation-coordinator.ts +147 -142
  182. package/src/runtime/locations/in-memory-provider-location-store.ts +11 -7
  183. package/src/runtime/locations/lifecycle.ts +3 -2
  184. package/src/runtime/locations/resolvers.ts +19 -13
  185. package/src/runtime/locations/spot-location-claims.ts +5 -34
  186. package/src/runtime/protocol/service_wire_codec.generated.ts +9 -2
  187. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
  188. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +6 -6
  189. package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +3 -3
  190. package/src/runtime/spots/index.ts +341 -295
  191. package/src/runtime/spots/spot-activation-registry.ts +41 -31
  192. package/src/runtime/spots/spot-activation-state.ts +4 -16
  193. package/src/runtime/spots/spot-activation.ts +102 -80
  194. package/src/runtime/spots/spot-actor-admission-coordinator.ts +2 -2
  195. package/src/runtime/spots/spot-actor-join-dispatch.ts +6 -7
  196. package/src/runtime/spots/spot-actor-lifecycle-drain.ts +20 -18
  197. package/src/runtime/spots/spot-actor-membership.ts +81 -17
  198. package/src/runtime/spots/spot-entry-activation.ts +0 -3
  199. package/src/runtime/spots/spot-manager-public.ts +8 -4
  200. package/src/runtime/spots/spot-native-actor-join-admission.ts +2 -2
  201. package/src/runtime/spots/spot-node-runtime-manager.ts +12 -18
  202. package/src/runtime/spots/spot-outbound.ts +4 -47
  203. package/src/runtime/spots/spot-routed-actor-admission.ts +20 -0
  204. package/src/runtime/spots/spot-routed-frame-dispatch.ts +6 -76
  205. package/src/runtime/spots/spot-runtime-ports.ts +6 -17
  206. package/src/runtime/spots/spot-serial-executor.ts +9 -1
  207. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -4
  208. package/src/runtime/streams/actor-session-binding-registry.ts +20 -308
  209. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +2 -12
  210. package/src/runtime/streams/bound-session-service.ts +2 -36
  211. package/src/runtime/streams/index.ts +29 -27
  212. package/src/runtime/streams/session-serial-executor.ts +2 -2
  213. package/dist/runtime/actors/deferred-join-accepted-journal.d.ts +0 -47
  214. package/dist/runtime/actors/deferred-join-accepted-journal.js +0 -992
  215. package/src/runtime/actors/deferred-join-accepted-journal.ts +0 -1346
@@ -1,4 +1,4 @@
1
- import type { ServiceSessionBindingAdmissionClaim, ServiceSessionBindingAdmissionResult } from '../foundation/service-session-binding-ingress-port';
1
+ import type { ServiceSessionBindingAdmissionResult } from '../foundation/service-session-binding-ingress-port';
2
2
  export interface ZLinkActorSessionBindingActor {
3
3
  readonly actorId: string;
4
4
  }
@@ -34,14 +34,6 @@ export interface ZLinkActorSessionRetainedOutbound {
34
34
  deliver(): Promise<boolean>;
35
35
  fail(error: unknown): void;
36
36
  }
37
- export interface ZLinkActorSessionAcceptedProducerProof {
38
- readonly actorId: string;
39
- readonly objectGeneration: bigint;
40
- readonly actorNodeRid: string;
41
- readonly actorNodeGeneration: bigint;
42
- readonly sessionIdentity: string;
43
- readonly bindingGeneration: bigint;
44
- }
45
37
  export interface ZLinkActorSessionRelocationSnapshot extends ZLinkActorSessionRelocationClaim {
46
38
  readonly phase: 'sealed' | 'applying' | 'applied' | 'terminal';
47
39
  readonly applyFingerprint?: string;
@@ -139,16 +131,24 @@ export declare class ZLinkActorSessionBindingRegistry<TContext extends ZLinkActo
139
131
  private relocationSnapshotCore;
140
132
  retainRelocationOutbound(actorId: string, operation: ZLinkActorSessionRetainedOutbound, sealId?: string): Promise<ServiceSessionBindingAdmissionResult>;
141
133
  private retainRelocationOutboundCorePublic;
142
- admitRelocationOutbound(claim: ServiceSessionBindingAdmissionClaim, operation: ZLinkActorSessionRetainedOutbound): Promise<ServiceSessionBindingAdmissionResult>;
143
- private admitRelocationOutboundCore;
134
+ /**
135
+ * Session–Actor binding §8.1: the Session owner holds a push only while the
136
+ * binding's relocation seal is open, and keeps it behind earlier held pushes.
137
+ * Whether the push names the current binding was decided once by the Session
138
+ * owner's binding record before this call.
139
+ */
144
140
  private retainRelocationOutboundCore;
145
141
  discardRelocationOutbound(actorId: string, sealId: string, error: unknown): Promise<void>;
146
142
  private discardRelocationOutboundCore;
147
- applyRelocation(actorId: string, sealId: string, applyFingerprint: string, action: 'commit' | 'abort', commitOwnerTransition: () => Promise<void>, acceptedProducerProof?: ZLinkActorSessionAcceptedProducerProof): Promise<void>;
143
+ applyRelocation(actorId: string, sealId: string, applyFingerprint: string, commitOwnerTransition: () => Promise<void>): Promise<void>;
148
144
  private prepareRelocationApply;
145
+ /**
146
+ * Command 44 commit or abort releases every push held under this seal in
147
+ * arrival order (Session–Actor binding §8.1). The held pushes were admitted
148
+ * by the current binding; the route change does not judge them again.
149
+ */
149
150
  private completeRelocationApply;
150
151
  private failRelocationApply;
151
- private currentProducerProof;
152
152
  observeRelocationTerminal(actorId: string, sealId: string, applyFingerprint: string): Promise<void>;
153
153
  private observeRelocationTerminalCore;
154
154
  /**
@@ -192,8 +192,6 @@ export declare class ZLinkActorSessionBindingRegistry<TContext extends ZLinkActo
192
192
  private reserveOutbound;
193
193
  private settleOutbound;
194
194
  private failOutbound;
195
- private outboundAdmissionError;
196
- private matchesCurrentProducerNode;
197
195
  private rememberTerminalRelocation;
198
196
  private deleteEmptyRelocationQueue;
199
197
  private resolveSealWaiters;
@@ -5,7 +5,6 @@ const framework_errors_internal_1 = require("../framework-errors-internal");
5
5
  const abort_1 = require("../abort");
6
6
  const node_async_hooks_1 = require("node:async_hooks");
7
7
  const state_lane_1 = require("../execution/state-lane");
8
- const routing_id_1 = require("../routing-id");
9
8
  const detachedStateLaneResource = new node_async_hooks_1.AsyncResource('zlink:actor-session-binding-registry');
10
9
  var ZLinkActorSessionBindingTermination;
11
10
  (function (ZLinkActorSessionBindingTermination) {
@@ -438,42 +437,15 @@ class ZLinkActorSessionBindingRegistry {
438
437
  return 'rejected';
439
438
  }
440
439
  }
441
- return this.retainRelocationOutboundCore(actorId, operation, 'legacy');
440
+ return this.retainRelocationOutboundCore(actorId, operation);
442
441
  }
443
- async admitRelocationOutbound(claim, operation) {
444
- return await this.lane.run(() => this.admitRelocationOutboundCore(claim, operation));
445
- }
446
- admitRelocationOutboundCore(claim, operation) {
447
- const queue = this.relocations.get(claim.actorId);
448
- const state = queue?.activeSealId === undefined ? undefined : queue.seals.get(queue.activeSealId);
449
- const error = this.outboundAdmissionError(claim);
450
- if (error !== undefined) {
451
- failRetainedOutbound(operation, error);
452
- return 'rejected';
453
- }
454
- if (queue === undefined || state === undefined) {
455
- if (!this.matchesCurrentProducerNode(claim)) {
456
- failRetainedOutbound(operation, new Error(`Actor '${claim.actorId}' Session outbound admission was fenced by its current binding.`));
457
- return 'rejected';
458
- }
459
- if (queue === undefined)
460
- return 'passThrough';
461
- return this.retainRelocationOutboundCore(claim.actorId, operation, 'source', claim);
462
- }
463
- if (state.phase === 'sealed' || state.phase === 'applying') {
464
- const matchesSource = matchesRelocationSourceProof(state, claim);
465
- const matchesCurrent = this.matchesCurrentProducerNode(claim);
466
- const authorization = matchesSource || matchesCurrent ? 'source' : 'pendingTarget';
467
- return this.retainRelocationOutboundCore(claim.actorId, operation, authorization, claim);
468
- }
469
- const acceptedProof = state.acceptedProducerProof;
470
- if (acceptedProof === undefined || !matchesAcceptedProducerProof(acceptedProof, claim)) {
471
- failRetainedOutbound(operation, new Error(`Actor '${claim.actorId}' Session outbound admission did not match its accepted producer proof.`));
472
- return 'rejected';
473
- }
474
- return this.retainRelocationOutboundCore(claim.actorId, operation, 'source', claim);
475
- }
476
- retainRelocationOutboundCore(actorId, operation, authorization, claim) {
442
+ /**
443
+ * Session–Actor binding §8.1: the Session owner holds a push only while the
444
+ * binding's relocation seal is open, and keeps it behind earlier held pushes.
445
+ * Whether the push names the current binding was decided once by the Session
446
+ * owner's binding record before this call.
447
+ */
448
+ retainRelocationOutboundCore(actorId, operation) {
477
449
  const queue = this.relocations.get(actorId);
478
450
  const state = queue?.activeSealId === undefined ? undefined : queue.seals.get(queue.activeSealId);
479
451
  if (queue === undefined)
@@ -485,9 +457,7 @@ class ZLinkActorSessionBindingRegistry {
485
457
  return 'rejected';
486
458
  queue.outbound.push({
487
459
  operation,
488
- authorization,
489
460
  arrivalOrder: queue.nextArrivalOrder++,
490
- ...(claim === undefined ? {} : { claim }),
491
461
  released: true,
492
462
  settled: false
493
463
  });
@@ -499,9 +469,7 @@ class ZLinkActorSessionBindingRegistry {
499
469
  queue.outbound.push({
500
470
  sealId: state.sealId,
501
471
  operation,
502
- authorization,
503
472
  arrivalOrder: queue.nextArrivalOrder++,
504
- ...(claim === undefined ? {} : { claim }),
505
473
  released: false,
506
474
  settled: false
507
475
  });
@@ -523,8 +491,8 @@ class ZLinkActorSessionBindingRegistry {
523
491
  }
524
492
  this.deleteEmptyRelocationQueue(queue);
525
493
  }
526
- async applyRelocation(actorId, sealId, applyFingerprint, action, commitOwnerTransition, acceptedProducerProof) {
527
- const prepared = await this.lane.run(() => this.prepareRelocationApply(actorId, sealId, applyFingerprint, action, acceptedProducerProof));
494
+ async applyRelocation(actorId, sealId, applyFingerprint, commitOwnerTransition) {
495
+ const prepared = await this.lane.run(() => this.prepareRelocationApply(actorId, sealId, applyFingerprint));
528
496
  if (prepared.applyPromise !== undefined) {
529
497
  await prepared.applyPromise;
530
498
  return;
@@ -544,7 +512,7 @@ class ZLinkActorSessionBindingRegistry {
544
512
  });
545
513
  await prepared.promise;
546
514
  }
547
- prepareRelocationApply(actorId, sealId, applyFingerprint, action, acceptedProducerProof) {
515
+ prepareRelocationApply(actorId, sealId, applyFingerprint) {
548
516
  const queue = this.relocations.get(actorId);
549
517
  const state = queue?.seals.get(sealId);
550
518
  if (queue === undefined || state === undefined) {
@@ -556,14 +524,6 @@ class ZLinkActorSessionBindingRegistry {
556
524
  }
557
525
  return { queue, state, applyPromise: state.applyPromise };
558
526
  }
559
- if (action === 'commit' &&
560
- acceptedProducerProof !== undefined &&
561
- !validAcceptedTargetProof(state, acceptedProducerProof)) {
562
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorLocationStale, `Actor '${actorId}' command 44 target producer proof did not match its Session seal.`, true);
563
- }
564
- if (action === 'abort' && acceptedProducerProof !== undefined) {
565
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorLocationStale, `Actor '${actorId}' abort route supplied a target producer proof.`, true);
566
- }
567
527
  state.phase = 'applying';
568
528
  state.applyFingerprint = applyFingerprint;
569
529
  let resolve;
@@ -576,38 +536,22 @@ class ZLinkActorSessionBindingRegistry {
576
536
  return {
577
537
  queue,
578
538
  state,
579
- action,
580
- acceptedProducerProof,
581
539
  promise,
582
540
  resolve,
583
541
  reject,
584
- actorId,
585
542
  sealId
586
543
  };
587
544
  }
545
+ /**
546
+ * Command 44 commit or abort releases every push held under this seal in
547
+ * arrival order (Session–Actor binding §8.1). The held pushes were admitted
548
+ * by the current binding; the route change does not judge them again.
549
+ */
588
550
  completeRelocationApply(prepared) {
589
- const { queue, state, action, acceptedProducerProof, actorId, sealId } = prepared;
590
- const proof = action === 'commit'
591
- ? (acceptedProducerProof ?? this.currentProducerProof(actorId))
592
- : relocationSourceProof(state);
593
- state.acceptedProducerProof = proof;
594
- for (let index = queue.outbound.length - 1; index >= 0; index--) {
595
- const entry = queue.outbound[index];
596
- if (entry.sealId !== sealId)
597
- continue;
598
- if (entry.authorization !== 'pendingTarget') {
599
- entry.released = true;
600
- continue;
601
- }
602
- if (action === 'commit' &&
603
- proof !== undefined &&
604
- entry.claim !== undefined &&
605
- matchesAcceptedProducerProof(proof, entry.claim)) {
551
+ const { queue, state, sealId } = prepared;
552
+ for (const entry of queue.outbound) {
553
+ if (entry.sealId === sealId)
606
554
  entry.released = true;
607
- continue;
608
- }
609
- queue.outbound.splice(index, 1);
610
- this.failOutbound(queue, entry, new Error(`Actor '${actorId}' pending Session producer did not match command 44 proof.`));
611
555
  }
612
556
  this.startOutboundDrain(queue);
613
557
  state.phase = 'applied';
@@ -616,27 +560,6 @@ class ZLinkActorSessionBindingRegistry {
616
560
  prepared.state.phase = 'sealed';
617
561
  prepared.state.applyFingerprint = undefined;
618
562
  prepared.state.applyPromise = undefined;
619
- prepared.state.acceptedProducerProof = undefined;
620
- }
621
- currentProducerProof(actorId) {
622
- const route = this.routes.get(actorId);
623
- const actorRef = (route?.actor).ref;
624
- const sessionIdentity = route?.sessionIdentity;
625
- if (route === undefined ||
626
- actorRef === undefined ||
627
- sessionIdentity === undefined ||
628
- actorRef.ownerNodeGeneration === undefined)
629
- return undefined;
630
- return {
631
- actorId,
632
- objectGeneration: BigInt(actorRef.objectGeneration ??
633
- actorRef.generation ??
634
- -1),
635
- actorNodeRid: String(actorRef.nodeRid ?? ''),
636
- actorNodeGeneration: BigInt(actorRef.ownerNodeGeneration),
637
- sessionIdentity: String(sessionIdentity),
638
- bindingGeneration: BigInt(actorRef.bindingGeneration ?? -1)
639
- };
640
563
  }
641
564
  async observeRelocationTerminal(actorId, sealId, applyFingerprint) {
642
565
  await this.lane.run(() => this.observeRelocationTerminalCore(actorId, sealId, applyFingerprint));
@@ -971,42 +894,6 @@ class ZLinkActorSessionBindingRegistry {
971
894
  this.settleOutbound(queue, entry);
972
895
  failRetainedOutbound(entry.operation, error);
973
896
  }
974
- outboundAdmissionError(claim) {
975
- if (claim.actorId.length === 0 ||
976
- claim.objectGeneration <= 0n ||
977
- claim.actorNodeRid.length === 0 ||
978
- claim.actorNodeGeneration <= 0n ||
979
- claim.producerNodeRid.length === 0 ||
980
- claim.producerNodeGeneration <= 0n ||
981
- claim.bindingGeneration <= 0n ||
982
- claim.sessionIdentity.length === 0 ||
983
- !(0, routing_id_1.routingIdsEqual)(claim.producerNodeRid, claim.actorNodeRid) ||
984
- claim.producerNodeGeneration !== claim.actorNodeGeneration) {
985
- return new Error(`Actor '${claim.actorId}' Session outbound admission claim is invalid.`);
986
- }
987
- const route = this.routes.get(claim.actorId);
988
- const ref = route?.actor;
989
- const actorRef = ref?.ref;
990
- const routeSessionIdentity = route?.sessionIdentity;
991
- if (route === undefined ||
992
- actorRef === undefined ||
993
- String(actorRef.actorId ?? claim.actorId) !== claim.actorId ||
994
- BigInt(actorRef.objectGeneration ??
995
- actorRef.generation ??
996
- -1) !== claim.objectGeneration ||
997
- BigInt(actorRef.bindingGeneration ?? -1) !== claim.bindingGeneration ||
998
- String(routeSessionIdentity ?? '') !== claim.sessionIdentity) {
999
- return new Error(`Actor '${claim.actorId}' Session outbound admission was fenced by its current binding.`);
1000
- }
1001
- return undefined;
1002
- }
1003
- matchesCurrentProducerNode(claim) {
1004
- const route = this.routes.get(claim.actorId);
1005
- const actorRef = (route?.actor).ref;
1006
- return (route !== undefined &&
1007
- actorRef !== undefined &&
1008
- (0, routing_id_1.routingIdsEqual)(String(actorRef.nodeRid ?? ''), claim.actorNodeRid));
1009
- }
1010
897
  rememberTerminalRelocation(state) {
1011
898
  this.terminalRelocations.delete(state);
1012
899
  this.terminalRelocations.set(state, true);
@@ -1061,45 +948,6 @@ function assertRelocationClaim(state, claim) {
1061
948
  function startOutsideStateLane(work) {
1062
949
  return detachedStateLaneResource.runInAsyncScope(work);
1063
950
  }
1064
- function matchesRelocationSourceProof(state, claim) {
1065
- return (state.actorId === claim.actorId &&
1066
- state.actorGeneration === claim.objectGeneration &&
1067
- state.bindingGeneration === claim.bindingGeneration &&
1068
- state.sessionIdentity === claim.sessionIdentity &&
1069
- state.actorNodeRid !== undefined &&
1070
- (0, routing_id_1.routingIdsEqual)(state.actorNodeRid, claim.actorNodeRid) &&
1071
- state.actorNodeGeneration === claim.actorNodeGeneration);
1072
- }
1073
- function matchesAcceptedProducerProof(proof, claim) {
1074
- return (proof.actorId === claim.actorId &&
1075
- proof.objectGeneration === claim.objectGeneration &&
1076
- (0, routing_id_1.routingIdsEqual)(proof.actorNodeRid, claim.actorNodeRid) &&
1077
- proof.actorNodeGeneration === claim.actorNodeGeneration &&
1078
- proof.sessionIdentity === claim.sessionIdentity &&
1079
- proof.bindingGeneration === claim.bindingGeneration);
1080
- }
1081
- function validAcceptedTargetProof(state, proof) {
1082
- return (proof.actorId === state.actorId &&
1083
- proof.objectGeneration === state.actorGeneration &&
1084
- proof.actorNodeRid.length > 0 &&
1085
- proof.actorNodeGeneration > 0n &&
1086
- proof.sessionIdentity === state.sessionIdentity &&
1087
- proof.bindingGeneration === state.bindingGeneration);
1088
- }
1089
- function relocationSourceProof(state) {
1090
- if (state.sessionIdentity === undefined ||
1091
- state.actorNodeRid === undefined ||
1092
- state.actorNodeGeneration === undefined)
1093
- return undefined;
1094
- return {
1095
- actorId: state.actorId,
1096
- objectGeneration: state.actorGeneration,
1097
- actorNodeRid: state.actorNodeRid,
1098
- actorNodeGeneration: state.actorNodeGeneration,
1099
- sessionIdentity: state.sessionIdentity,
1100
- bindingGeneration: state.bindingGeneration
1101
- };
1102
- }
1103
951
  function failRetainedOutbound(operation, error) {
1104
952
  try {
1105
953
  operation.fail(error);
@@ -1,14 +1,13 @@
1
1
  import type { ActorRef } from '../../contracts';
2
- import type { ServiceSessionBindingAdmissionClaim, ServiceSessionBindingAdmissionResult } from '../foundation/service-session-binding-ingress-port';
3
- import type { ZLinkActorSessionAuthorityFence, ZLinkActorSessionAcceptedProducerProof, ZLinkActorSessionRelocationClaim, ZLinkActorSessionRelocationSnapshot, ZLinkActorSessionRetainedOutbound, ZLinkActorSessionRouteFence } from './actor-session-binding-registry';
2
+ import type { ServiceSessionBindingAdmissionResult } from '../foundation/service-session-binding-ingress-port';
3
+ import type { ZLinkActorSessionAuthorityFence, ZLinkActorSessionRelocationClaim, ZLinkActorSessionRelocationSnapshot, ZLinkActorSessionRetainedOutbound, ZLinkActorSessionRouteFence } from './actor-session-binding-registry';
4
4
  export interface ZLinkActorSessionBindingRuntimeOwner {
5
5
  actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
6
6
  sealRelocation(claim: ZLinkActorSessionRelocationClaim, expected: ZLinkActorSessionRouteFence, signal?: AbortSignal): Promise<void>;
7
7
  relocationSnapshot(actorId: string, sealId: string): Promise<ZLinkActorSessionRelocationSnapshot | undefined>;
8
8
  retainRelocationOutbound(actorId: string, operation: ZLinkActorSessionRetainedOutbound, sealId?: string): Promise<ServiceSessionBindingAdmissionResult>;
9
- admitRelocationOutbound(claim: ServiceSessionBindingAdmissionClaim, operation: ZLinkActorSessionRetainedOutbound): Promise<ServiceSessionBindingAdmissionResult>;
10
9
  discardRelocationOutbound(actorId: string, sealId: string, error: unknown): Promise<void>;
11
- applyRelocation(actorId: string, sealId: string, applyFingerprint: string, action: 'commit' | 'abort', commitOwnerTransition: () => Promise<void>, acceptedProducerProof?: ZLinkActorSessionAcceptedProducerProof): Promise<void>;
10
+ applyRelocation(actorId: string, sealId: string, applyFingerprint: string, commitOwnerTransition: () => Promise<void>): Promise<void>;
12
11
  observeRelocationTerminal(actorId: string, sealId: string, applyFingerprint: string): Promise<void>;
13
12
  clearRelocation(actorId: string, error: unknown): Promise<void>;
14
13
  committedRoute(actorId: string): Promise<{
@@ -157,7 +157,7 @@ class ZLinkBoundSessionService {
157
157
  async submitNativeBoundSessionFrame(node, actorRef, frame, signal) {
158
158
  const backendActorRef = toBoundSessionSendActorRef(actorRef);
159
159
  (0, abort_1.throwIfAborted)(signal);
160
- return await node.sendActorBoundSession(backendActorRef, requireBoundSessionGeneration(actorRef), [frame], ZLINK_SEND_DONT_WAIT, boundSessionSendFence(node, backendActorRef, actorRef));
160
+ return await node.sendActorBoundSession(backendActorRef, requireBoundSessionGeneration(actorRef), [frame], ZLINK_SEND_DONT_WAIT);
161
161
  }
162
162
  requireTransport() {
163
163
  if (this.options.transport === undefined) {
@@ -184,20 +184,3 @@ function toBoundSessionSendActorRef(actor) {
184
184
  generation: actor.objectGeneration
185
185
  };
186
186
  }
187
- function boundSessionSendFence(node, backendActor, actor) {
188
- const internal = actor;
189
- const ownerNodeGeneration = internal.ownerNodeGeneration ?? node.actorNodeGeneration?.(backendActor);
190
- if (internal.ownershipGeneration === undefined ||
191
- internal.ownershipGeneration <= 0n ||
192
- internal.ownerLeaseGeneration === undefined ||
193
- internal.ownerLeaseGeneration <= 0n ||
194
- ownerNodeGeneration === undefined ||
195
- ownerNodeGeneration <= 0n) {
196
- return undefined;
197
- }
198
- return Object.freeze({
199
- targetNodeGeneration: ownerNodeGeneration,
200
- authorityOwnerGeneration: internal.ownershipGeneration,
201
- ownerLeaseGeneration: internal.ownerLeaseGeneration
202
- });
203
- }
@@ -1,3 +1,4 @@
1
+ import type { ZLinkListenerRecords } from '../foundation/listener-records';
1
2
  import type { ActorRef, ZLinkActor, ZLinkBoundSession, ZLinkSessionActor, ZLinkMessageSerializer, ZLinkStreamCompressionOptions } from '../../contracts';
2
3
  import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
3
4
  import type { ZLinkSubmitResult } from '../messaging/submission-result';
@@ -68,6 +69,7 @@ export interface ZLinkStreamSessionNodeRuntimeOptions extends Omit<ZLinkStreamSe
68
69
  readonly bindingRuntime?: ZLinkStreamBindingRuntime;
69
70
  }
70
71
  export interface ZLinkStreamRuntimeManagerOptions {
72
+ readonly listenerRecords?: ZLinkListenerRecords;
71
73
  readonly registration: ZLinkFrameworkRegistration;
72
74
  readonly backendAdapterFactory: ZLinkBackendAdapterFactory;
73
75
  readonly context: ZLinkBackendContext;
@@ -56,6 +56,12 @@ class ZLinkStreamRuntimeManager {
56
56
  const nativeMeshNode = actorDispatchEnabled ? this.options.nativeMeshNode : undefined;
57
57
  const meshCompletions = actorDispatchEnabled ? this.options.meshCompletions : undefined;
58
58
  const socket = streamAdapter.createStreamSocket(this.options.context);
59
+ const node = {
60
+ meshName: applicationMeshName,
61
+ socket,
62
+ nativeSessionServices: []
63
+ };
64
+ this.nodes.set(nodeName, node);
59
65
  // Core uses -1 as the explicit unlimited value. The Framework value 0
60
66
  // means that it adds no separate STREAM cap, so preserve that meaning
61
67
  // when applying the socket option.
@@ -71,7 +77,6 @@ class ZLinkStreamRuntimeManager {
71
77
  if (advertisedEndpoint === undefined) {
72
78
  throw new configuration_1.ZLinkConfigurationException(`STREAM node '${nodeName}' advertised host requires a TCP endpoint, received '${boundEndpoint}'.`);
73
79
  }
74
- const readablePoller = streamAdapter.createReadablePoller(socket);
75
80
  const nativeSessionRoutes = new Map();
76
81
  if (actorDispatchEnabled) {
77
82
  for (const [meshName, mesh] of this.options.registration.spotNodes) {
@@ -86,10 +91,11 @@ class ZLinkStreamRuntimeManager {
86
91
  if (typeof createService !== 'function' || completions === undefined)
87
92
  continue;
88
93
  const service = createService.call(meshNode, socket.nativeInstance);
94
+ node.nativeSessionServices.push(service);
89
95
  const bindingOwner = (0, actor_session_binding_runtime_owner_1.actorSessionBindingRuntimeOwner)(this.options.bindingRuntime);
90
96
  (0, service_session_binding_ingress_port_1.registerServiceSessionBindingIngressPort)(service, {
91
97
  actorSlot: (actorId, sessionRid) => bindingOwner.actorSlot(actorId, sessionRid),
92
- retainOutbound: (claim, delivery) => bindingOwner.admitRelocationOutbound(claim, delivery),
98
+ retainOutbound: (actorId, delivery) => bindingOwner.retainRelocationOutbound(actorId, delivery),
93
99
  clearOutbound: (actorId, error) => bindingOwner.clearRelocation(actorId, error)
94
100
  });
95
101
  nativeSessionRoutes.set(meshName, {
@@ -102,8 +108,12 @@ class ZLinkStreamRuntimeManager {
102
108
  ? undefined
103
109
  : nativeSessionRoutes.get(applicationMeshName)?.service;
104
110
  const monitor = monitoringAdapter.openSocketMonitor(socket);
111
+ node.monitor = monitor;
105
112
  const sessionType = streamNode.session;
106
113
  const sessionHandlerTypes = streamNode[Symbol.for('@zlink-systems/framework:session-handler-types')] ?? [];
114
+ // The runtime disposes the poller, so the poller is created where nothing
115
+ // can fail before the runtime owns it.
116
+ const readablePoller = streamAdapter.createReadablePoller(socket);
107
117
  const runtime = new stream_session_runtime_1.ZLinkStreamSessionNodeRuntime({
108
118
  nodeName,
109
119
  socket,
@@ -123,42 +133,33 @@ class ZLinkStreamRuntimeManager {
123
133
  replacementCallbackTimeoutMs: this.options.registration.sessionReplacementCallbackTimeoutMs,
124
134
  sessionFactory: (context) => (0, session_provider_1.createStreamSessionInstance)(sessionType, this.options.providerResolver, context, sessionHandlerTypes)
125
135
  });
136
+ node.runtime = runtime;
126
137
  runtime.start();
127
- this.nodes.set(nodeName, {
128
- meshName: applicationMeshName,
129
- advertisedEndpoint,
130
- runtime,
131
- socket,
132
- monitor,
133
- nativeSessionService,
134
- nativeSessionServices: [...nativeSessionRoutes.values()].map((route) => route.service)
135
- });
138
+ this.options.listenerRecords?.record('stream', nodeName, advertisedEndpoint);
136
139
  }
137
140
  }
138
141
  async dispose() {
139
142
  const nodes = [...this.nodes.values()];
140
143
  this.nodes.clear();
141
144
  for (const node of nodes.reverse()) {
142
- await node.runtime.dispose();
143
- const nativeServices = node.nativeSessionServices ??
144
- (node.nativeSessionService === undefined ? [] : [node.nativeSessionService]);
145
- for (const service of nativeServices) {
145
+ await node.runtime?.dispose();
146
+ for (const service of node.nativeSessionServices) {
146
147
  service.shutdown(1000);
147
148
  service.close();
148
149
  }
149
- await node.monitor.dispose();
150
+ await node.monitor?.dispose();
150
151
  await node.socket.dispose();
151
152
  }
152
153
  }
153
154
  async notifyServerDrain(meshName) {
154
155
  await Promise.all([...this.nodes.values()]
155
156
  .filter((node) => node.meshName === meshName)
156
- .map((node) => node.runtime.drainCloseSessions()));
157
+ .map((node) => node.runtime?.drainCloseSessions()));
157
158
  }
158
159
  async notifyUnscopedServerDrain() {
159
160
  await Promise.all([...this.nodes.values()]
160
161
  .filter((node) => node.meshName === undefined)
161
- .map((node) => node.runtime.drainCloseSessions()));
162
+ .map((node) => node.runtime?.drainCloseSessions()));
162
163
  }
163
164
  }
164
165
  exports.ZLinkStreamRuntimeManager = ZLinkStreamRuntimeManager;
@@ -224,7 +225,6 @@ class ZLinkStreamBindingRuntime {
224
225
  sealRelocation: (claim, expected, signal) => this.routes.sealRelocation(claim, expected, signal),
225
226
  relocationSnapshot: (actorId, sealId) => this.routes.relocationSnapshot(actorId, sealId),
226
227
  retainRelocationOutbound: (actorId, operation, sealId) => this.routes.retainRelocationOutbound(actorId, operation, sealId),
227
- admitRelocationOutbound: (claim, operation) => this.routes.admitRelocationOutbound(claim, operation),
228
228
  discardRelocationOutbound: (actorId, sealId, error) => this.routes.discardRelocationOutbound(actorId, sealId, error),
229
229
  applyRelocation: (...args) => this.routes.applyRelocation(...args),
230
230
  observeRelocationTerminal: (...args) => this.routes.observeRelocationTerminal(...args),
@@ -22,8 +22,8 @@ class ZLinkSessionSerialExecutor {
22
22
  }
23
23
  const boundWork = (0, application_job_queue_scope_1.bindApplicationJobPermit)(work);
24
24
  return (0, application_job_queue_scope_1.hasApplicationJobPermit)()
25
- ? this.scheduler.submitPreAdmitted(boundWork, { lane: 'lifecycle' })
26
- : this.scheduler.submit(boundWork, { lane: 'lifecycle' });
25
+ ? this.scheduler.submitPreAdmitted(boundWork, { lane: 'application' })
26
+ : this.scheduler.submit(boundWork, { lane: 'application' });
27
27
  }
28
28
  submit(work, lane, options = {}, onRejected) {
29
29
  if (this.closed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zlink-systems/framework",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "dependencies": {
20
20
  "@opentelemetry/api": "^1.9.1",
21
21
  "@opentelemetry/api-logs": "^0.221.0",
22
- "@zlink-systems/stream-wire": "0.24.0",
23
- "@zlink-systems/zlink": "1.6.0"
22
+ "@zlink-systems/stream-wire": "0.26.0",
23
+ "@zlink-systems/zlink": "1.10.0"
24
24
  }
25
25
  }
@@ -1,13 +1,6 @@
1
1
  import type { ZLinkFanoutPublishCall } from './Calls';
2
2
 
3
- export interface ZLinkFanoutListenerStatus {
4
- readonly channelName: string;
5
- readonly endpoint: string;
6
- readonly observedAt: Date;
7
- }
8
-
9
3
  export interface ZLinkFanoutClient {
10
4
  publish(channelName: string, event: unknown): ZLinkFanoutPublishCall;
11
5
  publish(channelName: string, topic: string, event: unknown): ZLinkFanoutPublishCall;
12
- getListenerStatus(channelName: string): ZLinkFanoutListenerStatus;
13
6
  }
@@ -142,12 +142,22 @@ export interface ZLinkHostCapacityStatus {
142
142
 
143
143
  export interface ZLinkFrameworkRuntime {
144
144
  readonly status: ZLinkFrameworkRuntimeStatus;
145
+ getListenerStatus(kind: ZLinkListenerKind, name: string): ZLinkListenerStatus;
145
146
  observe(signal?: AbortSignal): AsyncIterable<ZLinkObservedStatus<ZLinkFrameworkRuntimeStatus>>;
146
147
  resetCapacityMetrics(): void;
147
148
  relocate(options: ZLinkFrameworkRelocationOptions): Promise<ZLinkFrameworkRelocationResult>;
148
149
  shutdown(options?: ZLinkFrameworkLifecycleOptions): Promise<ZLinkFrameworkTerminationResult>;
149
150
  }
150
151
 
152
+ export type ZLinkListenerKind = 'routeMesh' | 'clientServer' | 'fanout' | 'stream';
153
+
154
+ export interface ZLinkListenerStatus {
155
+ readonly kind: ZLinkListenerKind;
156
+ readonly name: string;
157
+ readonly endpoint: string;
158
+ readonly observedAt: Date;
159
+ }
160
+
151
161
  export type ZLinkClientServerRole = 'client' | 'server' | 'clientAndServer';
152
162
 
153
163
  export interface ZLinkClientServerTargetStatus {
package/src/internal.ts CHANGED
@@ -3,6 +3,7 @@ export * from './index';
3
3
  export * from './runtime/diagnostics';
4
4
  export * from './runtime/host';
5
5
  export * from './runtime/admission';
6
+ export * from './runtime/activation-admission';
6
7
  export * from './runtime/streams';
7
8
  export * from './runtime/streams/protocol';
8
9
  export * from './runtime/actors';
@@ -0,0 +1,78 @@
1
+ import {
2
+ ZLinkFrameworkInternalErrorKind,
3
+ createInternalFrameworkException
4
+ } from './framework-errors-internal';
5
+
6
+ /** Current activation admission count and limit of one MeshNode. */
7
+ export interface ZLinkActivationConcurrency {
8
+ readonly active: number;
9
+ readonly limit: number;
10
+ }
11
+
12
+ interface ZLinkActivationRecord {
13
+ readonly meshName: string;
14
+ readonly limit: number;
15
+ active: number;
16
+ }
17
+
18
+ /**
19
+ * MeshNode §5.1 "Pending activation": the single per-MeshNode record of activation admissions.
20
+ *
21
+ * Actor creation, User Spot creation, Instance Spot cold activation and a relocation target's
22
+ * Restore each hold one admission from the moment the target MeshNode receives the operation
23
+ * until Ready or its target commit, or until it ends by rejection, failure or cleanup. Entry Spot
24
+ * and Actor Join never acquire one. The limit is enforced here, and the status current value and
25
+ * the placement `IsAvailable` headroom both read this record.
26
+ */
27
+ export class ZLinkActivationAdmission {
28
+ private readonly records = new Map<string, ZLinkActivationRecord>();
29
+
30
+ constructor(
31
+ private readonly limitOf: (meshName: string) => number,
32
+ private readonly changed?: (meshName: string) => void
33
+ ) {}
34
+
35
+ current(meshName: string): ZLinkActivationConcurrency {
36
+ const record = this.records.get(meshName);
37
+ return { active: record?.active ?? 0, limit: record?.limit ?? this.limitOf(meshName) };
38
+ }
39
+
40
+ hasHeadroom(meshName: string): boolean {
41
+ const { active, limit } = this.current(meshName);
42
+ return active < limit;
43
+ }
44
+
45
+ /**
46
+ * Holds one admission for `meshName`. A MeshNode without headroom does not accept a new
47
+ * admission (MeshNode §5.1): the operation fails as Unavailable at once. The returned release
48
+ * is idempotent.
49
+ */
50
+ async acquire(meshName: string, signal?: AbortSignal): Promise<() => void> {
51
+ signal?.throwIfAborted();
52
+ const record = this.record(meshName);
53
+ if (record.active >= record.limit) {
54
+ throw createInternalFrameworkException(
55
+ ZLinkFrameworkInternalErrorKind.PlacementCapacityExhausted,
56
+ `MeshNode '${meshName}' has no activation admission headroom.`
57
+ );
58
+ }
59
+ record.active += 1;
60
+ this.changed?.(meshName);
61
+ let released = false;
62
+ return () => {
63
+ if (released) return;
64
+ released = true;
65
+ record.active -= 1;
66
+ this.changed?.(meshName);
67
+ };
68
+ }
69
+
70
+ private record(meshName: string): ZLinkActivationRecord {
71
+ let record = this.records.get(meshName);
72
+ if (record === undefined) {
73
+ record = { meshName, limit: this.limitOf(meshName), active: 0 };
74
+ this.records.set(meshName, record);
75
+ }
76
+ return record;
77
+ }
78
+ }