@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
@@ -3,6 +3,7 @@ import type { ServiceRelocationRestoreOwner, ServiceRelocationStaging } from './
3
3
  import type { ServiceRelocationEnvelope, ServiceRelocationMembership, ServiceRelocationParticipant, ServiceRelocationQueuedMessage, ServiceRelocationTimer } from './service-relocation-runtime';
4
4
  export interface ServiceRelocationSealedWork {
5
5
  readonly boundSessionState: Uint8Array;
6
+ readonly sessionBinding?: ServiceRelocationParticipant['sessionBinding'];
6
7
  readonly queuedMessages: readonly ServiceRelocationQueuedMessage[];
7
8
  readonly timers: readonly ServiceRelocationTimer[];
8
9
  }
@@ -102,6 +102,7 @@ class ServiceRelocationObjectCaptureOwner {
102
102
  authorityOwnerGeneration: unit.authorityOwnerGeneration,
103
103
  applicationState: Buffer.from(await unit.captureApplicationState(signal)),
104
104
  boundSessionState: Buffer.from(work.boundSessionState),
105
+ ...(work.sessionBinding === undefined ? {} : { sessionBinding: work.sessionBinding }),
105
106
  queuedMessages: work.queuedMessages.map((message) => ({
106
107
  sequence: message.sequence,
107
108
  payload: Buffer.from(message.payload)
@@ -1,4 +1,5 @@
1
1
  import type { ZLinkPlacementObjectKind } from '../locations/internal-location-contracts';
2
+ import { type RelocationEnvelopeV1 as GeneratedRelocationEnvelopeV1 } from '../protocol/service_wire_pilot_codec.generated';
2
3
  export interface ServiceRelocationParticipant {
3
4
  /** Wire-local participant ordinal; identity is projected from Location Store. */
4
5
  readonly participantId?: bigint;
@@ -17,6 +18,7 @@ export interface ServiceRelocationParticipant {
17
18
  readonly applicationStatePresent?: boolean;
18
19
  readonly applicationState: Uint8Array;
19
20
  readonly boundSessionState: Uint8Array;
21
+ readonly sessionBinding?: GeneratedRelocationEnvelopeV1['sessionBindings'][number];
20
22
  readonly queuedMessages: readonly ServiceRelocationQueuedMessage[];
21
23
  readonly timers: readonly ServiceRelocationTimer[];
22
24
  }
@@ -480,6 +480,9 @@ function encodeServiceRelocationEnvelope(envelope, applicationVersion) {
480
480
  lastCompletedScheduledIndex: nonNegativeBigInt(timer.lastScheduledIndex, 'timer scheduled index'),
481
481
  nextScheduledAtUnixMilliseconds: nonNegativeUnixMilliseconds(timer.dueAtUnixMs, 'timer due time')
482
482
  })),
483
+ sessionBindings: participants.flatMap((participant, index) => participant.sessionBinding === undefined
484
+ ? []
485
+ : [{ ...participant.sessionBinding, participantId: BigInt(index + 1) }]),
483
486
  pendingTimerTicks: pending.map((value) => ({
484
487
  participantId: value.participantId,
485
488
  order: value.order,
@@ -510,11 +513,20 @@ function decodeServiceRelocationEnvelope(payload, aggregateGeneration) {
510
513
  if (aggregateId === undefined)
511
514
  throw new TypeError('Invalid relocation identity.');
512
515
  positiveBigInt(aggregateGeneration, 'aggregate generation');
516
+ const bindings = new Map(generated.sessionBindings.map((binding) => [binding.participantId, binding]));
517
+ for (const id of bindings.keys()) {
518
+ if (id > BigInt(generated.applicationStates.length))
519
+ throw new TypeError('Session binding participant is outside its inventory.');
520
+ }
513
521
  const participants = generated.applicationStates.map((state, index) => {
514
522
  if (state.participantId !== BigInt(index + 1)) {
515
523
  throw new TypeError('Application state participant order is invalid.');
516
524
  }
517
- return wireParticipant(state.participantId, state.hasState, Buffer.from(state.payload));
525
+ const binding = bindings.get(state.participantId);
526
+ return {
527
+ ...wireParticipant(state.participantId, state.hasState, Buffer.from(state.payload)),
528
+ ...(binding === undefined ? {} : { sessionBinding: binding })
529
+ };
518
530
  });
519
531
  const byId = new Map(participants.map((value) => [value.participantId, value]));
520
532
  for (const work of generated.savedWork) {
@@ -343,6 +343,7 @@ export interface ServiceSpot {
343
343
  flags?: number;
344
344
  timeoutMs?: number;
345
345
  applicationMetadata?: Buffer;
346
+ onAdmitted?: () => void;
346
347
  }): MeshOperationId;
347
348
  sendToSpot(targetNodeRid: unknown, targetSpotId: unknown, targetSpotGeneration: bigint, parts: MessageLike | readonly MessageLike[], options?: {
348
349
  flags?: number;
@@ -354,6 +355,7 @@ export interface ServiceSpot {
354
355
  timeoutMs?: number;
355
356
  applicationMetadata?: Buffer;
356
357
  routeFence?: ServiceDirectSpotRouteFence;
358
+ onAdmitted?: () => void;
357
359
  entrySpot?: boolean;
358
360
  }): MeshOperationId;
359
361
  publish(channelName: string, topic: string, parts: MessageLike | readonly MessageLike[], options?: {
@@ -270,7 +270,7 @@ export declare class ServiceStatefulRuntime {
270
270
  clearSubscriptions(spotId: string): void;
271
271
  publishLogicalMulticast(channelName: string, topic: string, payload: ServiceApplicationPayload, sourceSpotId?: string): Promise<void>;
272
272
  sendToSpot(sourceSpotId: string, requested: ServiceDirectSpotRouteFence, payload: ServiceApplicationPayload): Promise<number>;
273
- requestToSpot(sourceSpotId: string, requested: ServiceDirectSpotRouteFence, payload: ServiceApplicationPayload, timeoutMs: number): ServiceStatefulPendingOperation;
273
+ requestToSpot(sourceSpotId: string, requested: ServiceDirectSpotRouteFence, payload: ServiceApplicationPayload, timeoutMs: number, onAdmitted?: () => void): ServiceStatefulPendingOperation;
274
274
  sendToActor(target: ServiceActorRef, _targetNodeGeneration: bigint, _authorityOwnerGeneration: bigint, payload: ServiceApplicationPayload, sourceActor?: ServiceActorRef, boundSession?: {
275
275
  readonly sessionRid: string;
276
276
  readonly bindingGeneration: bigint;
@@ -278,13 +278,13 @@ export declare class ServiceStatefulRuntime {
278
278
  requestToActor(target: ServiceActorRef, _targetNodeGeneration: bigint, _authorityOwnerGeneration: bigint, payload: ServiceApplicationPayload, timeoutMs: number, sourceActor?: ServiceActorRef, boundSession?: {
279
279
  readonly sessionRid: string;
280
280
  readonly bindingGeneration: bigint;
281
- }): ServiceStatefulPendingOperation;
281
+ }, onAdmitted?: () => void): ServiceStatefulPendingOperation;
282
282
  sendToInstanceSpot(route: ServiceInstanceRouteFence, payload: ServiceApplicationPayload, sourceSpotId?: string, metadataFrame?: Uint8Array, instanceIntent?: boolean): Promise<number>;
283
283
  sendToMissingInstanceSpot(target: ServiceInstanceColdActivationTarget, payload: ServiceApplicationPayload, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
284
284
  sendToMissingInstanceSpotFrame(target: ServiceInstanceColdActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): Promise<number>;
285
- requestToInstanceSpot(route: ServiceInstanceRouteFence, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array, instanceIntent?: boolean): ServiceStatefulPendingOperation;
285
+ requestToInstanceSpot(route: ServiceInstanceRouteFence, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array, instanceIntent?: boolean, onAdmitted?: () => void): ServiceStatefulPendingOperation;
286
286
  requestToMissingInstanceSpot(target: ServiceInstanceColdActivationTarget, payload: ServiceApplicationPayload, timeoutMs: number, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
287
- requestToMissingInstanceSpotFrame(target: ServiceInstanceColdActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array): ServiceStatefulPendingOperation;
287
+ requestToMissingInstanceSpotFrame(target: ServiceInstanceColdActivationTarget, payloadFrame: Buffer, deadlineUnixMs: bigint, sourceSpotId?: string, metadataFrame?: Uint8Array, onAdmitted?: () => void): ServiceStatefulPendingOperation;
288
288
  lookupRemoteActor(targetNodeRid: string, actorId: string, timeoutMs: number): ServiceStatefulPendingOperation;
289
289
  destroyActor(actor: ServiceActorRef, timeoutMs: number): ServiceStatefulPendingOperation;
290
290
  joinActor(actor: ServiceActorRef, targetNodeRid: string, targetSpot: ServiceSpotRef, targetSpotGeneration: bigint, payload: ServiceApplicationPayload | undefined, timeoutMs: number): ServiceStatefulPendingOperation;
@@ -7,7 +7,7 @@ const node_async_hooks_1 = require("node:async_hooks");
7
7
  const contracts_1 = require("../../contracts");
8
8
  const service_authority_payload_codec_1 = require("./service-authority-payload-codec");
9
9
  const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
10
- const abort_1 = require("../abort");
10
+ const durable_service_request_1 = require("./durable-service-request");
11
11
  const admission_1 = require("../admission");
12
12
  const runtime_values_1 = require("../backend/runtime-values");
13
13
  const message_flow_1 = require("../diagnostics/message-flow");
@@ -25,7 +25,6 @@ const service_stateful_wire_codec_1 = require("./service-stateful-wire-codec");
25
25
  const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
26
26
  const service_wire_constants_generated_1 = require("./service-wire-constants.generated");
27
27
  const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
28
- const STATEFUL_OPERATION_RETRY_TICK_MS = 20;
29
28
  const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
30
29
  const SPOT_MOVING = 34;
31
30
  const USER_SPOT_OPERATION_CAPACITY = 65_536;
@@ -92,7 +91,7 @@ class ServiceStatefulRuntime {
92
91
  this.operations = new service_stateful_registry_1.ServiceTerminalOperationRegistry(new operation_registry_1.OperationRegistry(undefined, onPendingOperationsChanged));
93
92
  this.registry = new service_stateful_registry_1.ServiceStatefulRegistry(nodeRid, nodeGeneration);
94
93
  this.registry.createEntrySpot(nodeRid);
95
- raw.setServiceIngress((record) => this.ingress(record));
94
+ raw.setServiceIngress((record) => this.ingress(record), acceptsStatefulIngress);
96
95
  }
97
96
  setMailboxDropHandler(handler) {
98
97
  this.mailboxDropHandler = handler;
@@ -679,7 +678,7 @@ class ServiceStatefulRuntime {
679
678
  });
680
679
  return this.submitOneWay(target.targetNodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)]);
681
680
  }
682
- requestToSpot(sourceSpotId, requested, payload, timeoutMs) {
681
+ requestToSpot(sourceSpotId, requested, payload, timeoutMs, onAdmitted) {
683
682
  const pending = this.operations.register(timeoutMs);
684
683
  const target = this.acceptSpotAuthority(requested);
685
684
  if (target === undefined) {
@@ -690,7 +689,7 @@ class ServiceStatefulRuntime {
690
689
  high: this.nodeGeneration,
691
690
  low: this.nextMessageFollowOperation++
692
691
  });
693
- this.submitRequest(pending, target.targetNodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'spotRequest');
692
+ this.submitRequest(pending, target.targetNodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'spotRequest', undefined, onAdmitted);
694
693
  return pending;
695
694
  }
696
695
  async sendToActor(target, _targetNodeGeneration, _authorityOwnerGeneration, payload, sourceActor, boundSession) {
@@ -705,7 +704,7 @@ class ServiceStatefulRuntime {
705
704
  }, { high: this.nodeGeneration, low: this.nextMessageFollowOperation++ });
706
705
  return this.submitOneWay(target.nodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)]);
707
706
  }
708
- requestToActor(target, _targetNodeGeneration, _authorityOwnerGeneration, payload, timeoutMs, sourceActor, boundSession) {
707
+ requestToActor(target, _targetNodeGeneration, _authorityOwnerGeneration, payload, timeoutMs, sourceActor, boundSession, onAdmitted) {
709
708
  const pending = this.operations.register(timeoutMs);
710
709
  const route = this.tryActorFence(target);
711
710
  if (route === undefined) {
@@ -718,7 +717,7 @@ class ServiceStatefulRuntime {
718
717
  ...boundSession,
719
718
  sequence: this.nextSessionSequence++
720
719
  }, { high: this.nodeGeneration, low: this.nextMessageFollowOperation++ });
721
- this.submitRequest(pending, target.nodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'actorRequest');
720
+ this.submitRequest(pending, target.nodeRid, [header, (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)], timeoutMs, 'actorRequest', undefined, onAdmitted);
722
721
  return pending;
723
722
  }
724
723
  async sendToInstanceSpot(route, payload, sourceSpotId, metadataFrame, instanceIntent = false) {
@@ -737,12 +736,12 @@ class ServiceStatefulRuntime {
737
736
  payloadFrame
738
737
  ], metadataFrame));
739
738
  }
740
- requestToInstanceSpot(route, payload, timeoutMs, sourceSpotId, metadataFrame, instanceIntent = false) {
739
+ requestToInstanceSpot(route, payload, timeoutMs, sourceSpotId, metadataFrame, instanceIntent = false, onAdmitted) {
741
740
  const pending = this.operations.register(timeoutMs);
742
741
  this.submitRequest(pending, route.targetNodeRid, instanceOperationParts([
743
742
  (0, service_stateful_wire_codec_1.encodeInstanceSpotHeader)(route, this.nodeGeneration, this.nodeRid, sourceSpotId, 'request', { high: operation_identity_1.ZLINK_NATIVE_CORRELATION_OPERATION_NAMESPACE, low: pending.id }, pending.id, metadataFrame !== undefined, instanceIntent),
744
743
  (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)
745
- ], metadataFrame), timeoutMs, 'instanceSpotRequest');
744
+ ], metadataFrame), timeoutMs, 'instanceSpotRequest', undefined, onAdmitted);
746
745
  return pending;
747
746
  }
748
747
  requestToMissingInstanceSpot(target, payload, timeoutMs, sourceSpotId, metadataFrame) {
@@ -750,13 +749,13 @@ class ServiceStatefulRuntime {
750
749
  const payloadFrame = (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload);
751
750
  return this.requestToMissingInstanceSpotFrame(target, payloadFrame, deadlineUnixMs, sourceSpotId, metadataFrame);
752
751
  }
753
- requestToMissingInstanceSpotFrame(target, payloadFrame, deadlineUnixMs, sourceSpotId, metadataFrame) {
752
+ requestToMissingInstanceSpotFrame(target, payloadFrame, deadlineUnixMs, sourceSpotId, metadataFrame, onAdmitted) {
754
753
  const timeoutMs = remainingDeadlineMs(deadlineUnixMs);
755
754
  const pending = this.operations.register(timeoutMs);
756
755
  this.submitRequest(pending, target.targetNodeRid, instanceOperationParts([
757
756
  (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(target, this.nodeGeneration, this.nodeRid, sourceSpotId, 'request', { high: operation_identity_1.ZLINK_NATIVE_CORRELATION_OPERATION_NAMESPACE, low: pending.id }, deadlineUnixMs, pending.id, metadataFrame !== undefined),
758
757
  payloadFrame
759
- ], metadataFrame), timeoutMs, 'instanceSpotRequest');
758
+ ], metadataFrame), timeoutMs, 'instanceSpotRequest', undefined, onAdmitted);
760
759
  return pending;
761
760
  }
762
761
  lookupRemoteActor(targetNodeRid, actorId, timeoutMs) {
@@ -1072,25 +1071,6 @@ class ServiceStatefulRuntime {
1072
1071
  this.spotMessageFollow.clear();
1073
1072
  }
1074
1073
  async ingress(record) {
1075
- // These frozen relocation controls overlap the legacy stateful command
1076
- // number range (30-34). They are bare infrastructure records and must
1077
- // fall through to RawServiceMeshRuntime's relocation ingress instead of
1078
- // being decoded as stateful actor/spot work.
1079
- if (record.command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationReady ||
1080
- record.command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationData ||
1081
- record.command === service_stateful_wire_codec_1.M6bServiceWireCommand.replyRelay ||
1082
- record.command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationCutover) {
1083
- return undefined;
1084
- }
1085
- if (record.command < service_stateful_wire_codec_1.M6bServiceWireCommand.spotSend ||
1086
- (record.command > service_stateful_wire_codec_1.M6bServiceWireCommand.instanceSpot &&
1087
- record.command !== service_stateful_wire_codec_1.M6bServiceWireCommand.userSpotCreate &&
1088
- record.command !== service_stateful_wire_codec_1.M6bServiceWireCommand.userSpotClose &&
1089
- record.command !== service_stateful_wire_codec_1.M6bServiceWireCommand.actorCreate &&
1090
- record.command !== service_stateful_wire_codec_1.M6bServiceWireCommand.messageFollow &&
1091
- record.command !== service_stateful_wire_codec_1.M6bServiceWireCommand.boundSessionReplaced)) {
1092
- return undefined;
1093
- }
1094
1074
  let decoded;
1095
1075
  let canonicalActorJoin;
1096
1076
  try {
@@ -1329,12 +1309,14 @@ class ServiceStatefulRuntime {
1329
1309
  if (record.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
1330
1310
  const ownedIngress = shareIngressOwnership(ingress);
1331
1311
  void this.continueMissingInstanceActivation(ownedIngress, record, payloadFrame, localReply, metadataFrame).finally(() => ownedIngress.applicationJobOwner?.close());
1312
+ ingress.onAdmitted?.();
1332
1313
  return 'infrastructure';
1333
1314
  }
1334
1315
  if (record.activation === 'ready' &&
1335
1316
  this.needsInstanceApplicationMaterialization(ingress, record)) {
1336
1317
  const ownedIngress = shareIngressOwnership(ingress);
1337
1318
  void this.continueReadyInstanceMaterialization(ownedIngress, record, payloadFrame, metadataFrame).finally(() => ownedIngress.applicationJobOwner?.close());
1319
+ ingress.onAdmitted?.();
1338
1320
  return 'infrastructure';
1339
1321
  }
1340
1322
  const spot = this.requireInstanceActivation(ingress, record);
@@ -2380,8 +2362,10 @@ class ServiceStatefulRuntime {
2380
2362
  stateful,
2381
2363
  applicationJob
2382
2364
  });
2383
- if (accepted)
2365
+ if (accepted) {
2366
+ ingress.onAdmitted?.();
2384
2367
  return domain;
2368
+ }
2385
2369
  applicationJob.close();
2386
2370
  if (stateful.reply !== undefined) {
2387
2371
  const result = failure((0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Mailbox owner '${owner}' no longer accepts work on this runtime.`));
@@ -2682,57 +2666,11 @@ class ServiceStatefulRuntime {
2682
2666
  }
2683
2667
  }
2684
2668
  async requestDurableOperation(targetNodeRid, parts, correlation, operationKind, deadlineMs) {
2685
- const stop = new AbortController();
2686
- const unobserve = this.raw.observePeerConnectionIntentRemoved((nodeRoutingId) => {
2687
- if (nodeRoutingId === targetNodeRid) {
2688
- stop.abort((0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RouteNotConnected, `${operationKind} target node lifecycle ended.`, true));
2689
- }
2690
- });
2691
- let wasAdmitted = false;
2692
- try {
2693
- // Only durable lifecycle operations replay these frozen frames. Received
2694
- // envelopes leave this loop before decoding, including failure terminals.
2695
- for (;;) {
2696
- this.requireOpen();
2697
- stop.signal.throwIfAborted();
2698
- const remainingMs = Math.ceil(deadlineMs - performance.now());
2699
- if (remainingMs <= 0)
2700
- throw durableOperationExhausted(operationKind, wasAdmitted);
2701
- try {
2702
- return await (0, abort_1.awaitWithAbort)(targetNodeRid === this.nodeRid
2703
- ? this.requestLocalInfrastructure(parts[0], correlation, remainingMs)
2704
- : this.raw.requestService(targetNodeRid, parts, remainingMs), stop.signal);
2705
- }
2706
- catch (error) {
2707
- stop.signal.throwIfAborted();
2708
- if (!durableRequestCanReplay(error))
2709
- throw error;
2710
- if (durableRequestWasAdmitted(error))
2711
- wasAdmitted = true;
2712
- const retryDelayMs = Math.min(STATEFUL_OPERATION_RETRY_TICK_MS, deadlineMs - performance.now());
2713
- if (retryDelayMs <= 0) {
2714
- throw durableOperationExhausted(operationKind, wasAdmitted, error);
2715
- }
2716
- let timer;
2717
- try {
2718
- await (0, abort_1.awaitWithAbort)(new Promise((resolve) => {
2719
- timer = setTimeout(resolve, retryDelayMs);
2720
- }), stop.signal);
2721
- }
2722
- finally {
2723
- if (timer !== undefined)
2724
- clearTimeout(timer);
2725
- }
2726
- }
2727
- }
2728
- }
2729
- catch (error) {
2730
- stop.signal.throwIfAborted();
2731
- throw error;
2732
- }
2733
- finally {
2734
- unobserve();
2669
+ this.requireOpen();
2670
+ if (targetNodeRid === this.nodeRid) {
2671
+ return this.requestLocalInfrastructure(parts[0], correlation, Math.ceil(deadlineMs - performance.now()));
2735
2672
  }
2673
+ return (0, durable_service_request_1.requestDurableService)(this.raw, targetNodeRid, parts, deadlineMs, operationKind);
2736
2674
  }
2737
2675
  requestLocalInfrastructure(header, requestSequence, timeoutMs) {
2738
2676
  return new Promise((resolve, reject) => {
@@ -2838,7 +2776,7 @@ class ServiceStatefulRuntime {
2838
2776
  ? runtime_values_1.SubmitResult.Ok
2839
2777
  : runtime_values_1.SubmitResult.NotConnected;
2840
2778
  }
2841
- submitRequest(pending, targetNodeRid, parts, timeoutMs, operationKind, actor) {
2779
+ submitRequest(pending, targetNodeRid, parts, timeoutMs, operationKind, actor, onAdmitted) {
2842
2780
  if (targetNodeRid === this.nodeRid) {
2843
2781
  const deadlineUnixMs = BigInt(Date.now() + Math.max(0, timeoutMs));
2844
2782
  const capacityStop = new AbortController();
@@ -2853,6 +2791,7 @@ class ServiceStatefulRuntime {
2853
2791
  flags: parts[0][service_wire_binary_primitives_1.SERVICE_WIRE_FLAGS_OFFSET],
2854
2792
  sourceRoutingId: this.nodeRid,
2855
2793
  requestSequence: pending.id,
2794
+ onAdmitted,
2856
2795
  parts,
2857
2796
  applicationJobOwner
2858
2797
  };
@@ -2867,7 +2806,7 @@ class ServiceStatefulRuntime {
2867
2806
  })().catch((error) => this.operations.reply(pending.id, failure(error)));
2868
2807
  return;
2869
2808
  }
2870
- void this.raw.requestService(targetNodeRid, parts, timeoutMs).then((reply) => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply), (error) => this.operations.fail(pending.id, error));
2809
+ void this.raw.requestService(targetNodeRid, parts, timeoutMs, onAdmitted).then((reply) => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply), (error) => this.operations.fail(pending.id, error));
2871
2810
  }
2872
2811
  submitActorJoin(pending, actor, targetNodeRid, target, payload, deadlineMs, canonical) {
2873
2812
  const actorRoute = canonical === undefined
@@ -3598,26 +3537,6 @@ function operationDeadline(deadlineUnixMs) {
3598
3537
  function operationReplayExpired(replayExpiresAtMs) {
3599
3538
  return performance.now() > replayExpiresAtMs;
3600
3539
  }
3601
- function durableRequestWasAdmitted(error) {
3602
- // Request NotConnected includes Core handover of an admitted request. The
3603
- // submit/request phase, rather than the result number, owns this distinction.
3604
- return (0, runtime_values_1.isZLinkBackendResultError)(error) && error.operation === 'request';
3605
- }
3606
- function durableOperationExhausted(operationKind, wasAdmitted, cause) {
3607
- return (0, framework_errors_internal_1.createInternalFrameworkException)(wasAdmitted
3608
- ? framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.DeadlineExceeded
3609
- : framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RouteNotConnected, `${operationKind} exhausted its end-to-end deadline.`, true, cause);
3610
- }
3611
- function durableRequestCanReplay(error) {
3612
- if (!(0, runtime_values_1.isZLinkBackendResultError)(error))
3613
- return false;
3614
- return error.operation === 'request'
3615
- ? error.result === runtime_values_1.RequestResult.NotConnected || error.result === runtime_values_1.RequestResult.TimedOut
3616
- : error.result === runtime_values_1.SubmitResult.NotConnected ||
3617
- error.result === runtime_values_1.SubmitResult.NotFound ||
3618
- error.result === runtime_values_1.SubmitResult.NotAdmitted ||
3619
- error.result === runtime_values_1.SubmitResult.Backpressured;
3620
- }
3621
3540
  function remainingDeadlineMs(deadlineUnixMs) {
3622
3541
  const remainingMs = deadlineUnixMs - BigInt(Date.now());
3623
3542
  if (remainingMs <= 0n) {
@@ -3944,3 +3863,25 @@ function instanceOperationParts(headerAndPayload, metadataFrame) {
3944
3863
  function statefulMailboxData(record) {
3945
3864
  return record.stateful;
3946
3865
  }
3866
+ function acceptsStatefulIngress(command) {
3867
+ // These frozen relocation controls overlap the legacy stateful command
3868
+ // number range (30-34). They are bare infrastructure records and must
3869
+ // fall through to RawServiceMeshRuntime's relocation ingress instead of
3870
+ // being decoded as stateful actor/spot work.
3871
+ if (command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationReady ||
3872
+ command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationData ||
3873
+ command === service_stateful_wire_codec_1.M6bServiceWireCommand.replyRelay ||
3874
+ command === service_stateful_wire_codec_1.M6bServiceWireCommand.relocationCutover) {
3875
+ return false;
3876
+ }
3877
+ if (command < service_stateful_wire_codec_1.M6bServiceWireCommand.spotSend ||
3878
+ (command > service_stateful_wire_codec_1.M6bServiceWireCommand.instanceSpot &&
3879
+ command !== service_stateful_wire_codec_1.M6bServiceWireCommand.userSpotCreate &&
3880
+ command !== service_stateful_wire_codec_1.M6bServiceWireCommand.userSpotClose &&
3881
+ command !== service_stateful_wire_codec_1.M6bServiceWireCommand.actorCreate &&
3882
+ command !== service_stateful_wire_codec_1.M6bServiceWireCommand.messageFollow &&
3883
+ command !== service_stateful_wire_codec_1.M6bServiceWireCommand.boundSessionReplaced)) {
3884
+ return false;
3885
+ }
3886
+ return true;
3887
+ }
@@ -126,11 +126,6 @@ export interface ServiceMaintenanceRelocationPrepare extends ServiceWireRelocati
126
126
  /** CRC-32C (Castagnoli) over the fully assembled payload bytes. */
127
127
  readonly payloadChecksumCrc32c: number;
128
128
  readonly applicationVersion: bigint;
129
- /** NODE-INTERNAL; serialized as extra frames after canonical command 40. */
130
- readonly nodeInternalBoundSessions?: readonly {
131
- readonly participantKey: string;
132
- readonly payload: Uint8Array;
133
- }[];
134
129
  }
135
130
  export interface ServiceMaintenanceRelocationReady extends ServiceWireRelocationBase {
136
131
  readonly kind: 'ready';
@@ -24,8 +24,13 @@ export interface ServiceNodeDescriptor {
24
24
  readonly activeCapacityUsed: number;
25
25
  readonly pendingCapacityUsed: number;
26
26
  }
27
- export interface AdmittedServicePeer {
27
+ interface ServiceRouteAdmission {
28
28
  readonly descriptor: ServiceNodeDescriptor;
29
+ readonly descriptorBytes?: Uint8Array;
30
+ readonly connectionId?: string | bigint;
31
+ readonly remoteAdmitReceived?: boolean;
32
+ }
33
+ export interface AdmittedServicePeer extends ServiceRouteAdmission {
29
34
  readonly connectionId: string | bigint;
30
35
  readonly liveness?: ServiceLivenessConnection;
31
36
  }
@@ -46,15 +51,19 @@ export declare class ServiceTopologyRegistry {
46
51
  constructor(local: ServiceNodeDescriptor);
47
52
  localDescriptor(): ServiceNodeDescriptor;
48
53
  publishLocal(descriptor: ServiceNodeDescriptor): void;
49
- admit(descriptor: ServiceNodeDescriptor, connectionId: string | bigint, expected?: ServicePeerAdmissionExpectation, admitLiveness?: () => ServiceLivenessConnection): PeerAdmissionResult;
54
+ admit(descriptor: ServiceNodeDescriptor, connectionId: string | bigint, expected?: ServicePeerAdmissionExpectation, admitLiveness?: () => ServiceLivenessConnection, command?: number, descriptorBytes?: Uint8Array): PeerAdmissionResult;
50
55
  disconnect(nodeRoutingId: string, connectionId: string | bigint): boolean;
51
56
  peer(nodeRoutingId: string): AdmittedServicePeer | undefined;
57
+ routeAdmission(nodeRoutingId: string): ServiceRouteAdmission | undefined;
58
+ admittedPeer(nodeRoutingId: string): AdmittedServicePeer | undefined;
52
59
  peers(): readonly AdmittedServicePeer[];
53
60
  private peersCore;
54
61
  notRequiredPeers(): readonly ServiceNodeDescriptor[];
55
62
  replaceDiscoveredNotRequired(descriptors: readonly ServiceNodeDescriptor[]): void;
56
63
  forgetNotRequired(nodeRoutingId: string): void;
57
64
  knownDescriptor(nodeRoutingId: string): ServiceNodeDescriptor | undefined;
65
+ isObjectClientNodeDirectTarget(nodeRoutingId: string): boolean;
66
+ private knownPeerDescriptor;
58
67
  hasKnownChannelTarget(channelName: string): boolean;
59
68
  private remember;
60
69
  selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
@@ -72,3 +81,4 @@ export declare class ServiceTopologyRegistry {
72
81
  }
73
82
  export declare function validateDescriptor(descriptor: ServiceNodeDescriptor): void;
74
83
  export declare function sameServiceNodeDescriptor(left: ServiceNodeDescriptor, right: ServiceNodeDescriptor): boolean;
84
+ export {};
@@ -35,13 +35,13 @@ class ServiceTopologyRegistry {
35
35
  }
36
36
  this.local = cloneDescriptor(descriptor);
37
37
  for (const [nodeRoutingId, remote] of this.notRequiredByRid) {
38
- if (!(0, route_mesh_connection_policy_1.descriptorConnectionNotRequired)(this.local, remote)) {
38
+ if (!(0, route_mesh_connection_policy_1.descriptorConnectionNotRequired)(this.local, remote.descriptor)) {
39
39
  this.notRequiredByRid.delete(nodeRoutingId);
40
40
  }
41
41
  }
42
42
  this.rebuildSelections();
43
43
  }
44
- admit(descriptor, connectionId, expected, admitLiveness) {
44
+ admit(descriptor, connectionId, expected, admitLiveness, command = service_wire_constants_generated_1.ServiceWireCommand.hello, descriptorBytes) {
45
45
  try {
46
46
  validateDescriptor(descriptor);
47
47
  if (typeof connectionId === 'string')
@@ -63,32 +63,46 @@ class ServiceTopologyRegistry {
63
63
  descriptor.lifecycleGeneration !== expected.lifecycleGeneration)) {
64
64
  return 'invalidDescriptor';
65
65
  }
66
+ const current = this.peersByRid.get(descriptor.nodeRoutingId);
67
+ const previous = current ?? this.notRequiredByRid.get(descriptor.nodeRoutingId);
68
+ const receivedAdmit = command === service_wire_constants_generated_1.ServiceWireCommand.admit ||
69
+ (previous?.connectionId === connectionId && previous.remoteAdmitReceived === true);
70
+ if (previous !== undefined &&
71
+ previous.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration) {
72
+ const existing = previous.descriptor;
73
+ if (descriptor.descriptorRevision < existing.descriptorRevision ||
74
+ (descriptor.descriptorRevision === existing.descriptorRevision &&
75
+ !(previous.descriptorBytes !== undefined && descriptorBytes !== undefined
76
+ ? Buffer.compare(previous.descriptorBytes, descriptorBytes) === 0
77
+ : sameServiceNodeDescriptor(existing, descriptor))) ||
78
+ (descriptor.descriptorRevision > existing.descriptorRevision &&
79
+ command !== service_wire_constants_generated_1.ServiceWireCommand.update))
80
+ return 'staleDescriptor';
81
+ if (descriptor.descriptorRevision > existing.descriptorRevision &&
82
+ !sameImmutableDescriptor(existing, descriptor))
83
+ return 'invalidDescriptor';
84
+ }
85
+ else if (command === service_wire_constants_generated_1.ServiceWireCommand.update) {
86
+ return 'staleDescriptor';
87
+ }
88
+ const admission = {
89
+ descriptor: cloneDescriptor(descriptor),
90
+ ...(descriptorBytes === undefined ? {} : { descriptorBytes: Buffer.from(descriptorBytes) }),
91
+ connectionId,
92
+ remoteAdmitReceived: receivedAdmit
93
+ };
66
94
  if ((0, route_mesh_connection_policy_1.descriptorConnectionNotRequired)(this.local, descriptor)) {
67
95
  this.peersByRid.delete(descriptor.nodeRoutingId);
68
96
  this.remember(descriptor);
69
- this.notRequiredByRid.set(descriptor.nodeRoutingId, cloneDescriptor(descriptor));
97
+ this.notRequiredByRid.set(descriptor.nodeRoutingId, admission);
70
98
  this.rebuildSelections();
71
99
  return 'notRequired';
72
100
  }
73
101
  this.notRequiredByRid.delete(descriptor.nodeRoutingId);
74
- const current = this.peersByRid.get(descriptor.nodeRoutingId);
75
- if (current !== undefined &&
76
- current.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
77
- descriptor.descriptorRevision > current.descriptor.descriptorRevision &&
78
- !sameImmutableDescriptor(current.descriptor, descriptor)) {
79
- return 'invalidDescriptor';
80
- }
81
- if (current !== undefined &&
82
- current.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
83
- (current.descriptor.descriptorRevision > descriptor.descriptorRevision ||
84
- (current.descriptor.descriptorRevision === descriptor.descriptorRevision &&
85
- !sameServiceNodeDescriptor(current.descriptor, descriptor)))) {
86
- return 'staleDescriptor';
87
- }
88
102
  // Core selects the one route per RID (Core ROUTER §10.1); an admission on
89
103
  // that route replaces the admission of a route it replaced.
90
104
  this.peersByRid.set(descriptor.nodeRoutingId, {
91
- descriptor: cloneDescriptor(descriptor),
105
+ ...admission,
92
106
  connectionId,
93
107
  liveness: admitLiveness?.() ?? current?.liveness
94
108
  });
@@ -105,9 +119,15 @@ class ServiceTopologyRegistry {
105
119
  return true;
106
120
  }
107
121
  peer(nodeRoutingId) {
108
- const peer = this.peersByRid.get(nodeRoutingId);
122
+ const peer = this.admittedPeer(nodeRoutingId);
109
123
  return peer === undefined ? undefined : clonePeer(peer);
110
124
  }
125
+ routeAdmission(nodeRoutingId) {
126
+ return this.peersByRid.get(nodeRoutingId) ?? this.notRequiredByRid.get(nodeRoutingId);
127
+ }
128
+ admittedPeer(nodeRoutingId) {
129
+ return this.peersByRid.get(nodeRoutingId);
130
+ }
111
131
  peers() {
112
132
  return this.peersCore();
113
133
  }
@@ -118,8 +138,8 @@ class ServiceTopologyRegistry {
118
138
  }
119
139
  notRequiredPeers() {
120
140
  return [...this.notRequiredByRid.values()]
121
- .sort((left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId))
122
- .map(cloneDescriptor);
141
+ .sort((left, right) => compareOrdinal(left.descriptor.nodeRoutingId, right.descriptor.nodeRoutingId))
142
+ .map((peer) => cloneDescriptor(peer.descriptor));
123
143
  }
124
144
  replaceDiscoveredNotRequired(descriptors) {
125
145
  const next = new Map();
@@ -128,13 +148,16 @@ class ServiceTopologyRegistry {
128
148
  if (descriptor.meshName === this.local.meshName &&
129
149
  descriptor.nodeRoutingId !== this.local.nodeRoutingId &&
130
150
  (0, route_mesh_connection_policy_1.descriptorConnectionNotRequired)(this.local, descriptor)) {
131
- next.set(descriptor.nodeRoutingId, cloneDescriptor(descriptor));
151
+ next.set(descriptor.nodeRoutingId, {
152
+ ...this.notRequiredByRid.get(descriptor.nodeRoutingId),
153
+ descriptor: cloneDescriptor(descriptor)
154
+ });
132
155
  }
133
156
  }
134
157
  this.notRequiredByRid.clear();
135
- for (const [nodeRoutingId, descriptor] of next) {
136
- this.notRequiredByRid.set(nodeRoutingId, descriptor);
137
- this.remember(descriptor);
158
+ for (const [nodeRoutingId, admission] of next) {
159
+ this.notRequiredByRid.set(nodeRoutingId, admission);
160
+ this.remember(admission.descriptor);
138
161
  }
139
162
  this.rebuildSelections();
140
163
  }
@@ -143,10 +166,21 @@ class ServiceTopologyRegistry {
143
166
  this.rebuildSelections();
144
167
  }
145
168
  knownDescriptor(nodeRoutingId) {
146
- const admitted = this.peersByRid.get(nodeRoutingId)?.descriptor;
147
- const descriptor = admitted ?? this.notRequiredByRid.get(nodeRoutingId) ?? this.knownByRid.get(nodeRoutingId);
169
+ const descriptor = this.knownPeerDescriptor(nodeRoutingId);
148
170
  return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
149
171
  }
172
+ isObjectClientNodeDirectTarget(nodeRoutingId) {
173
+ const descriptor = nodeRoutingId === this.local.nodeRoutingId
174
+ ? this.local
175
+ : this.knownPeerDescriptor(nodeRoutingId);
176
+ return descriptor?.objectRole === 'client';
177
+ }
178
+ knownPeerDescriptor(nodeRoutingId) {
179
+ const admitted = this.peersByRid.get(nodeRoutingId)?.descriptor;
180
+ return (admitted ??
181
+ this.notRequiredByRid.get(nodeRoutingId)?.descriptor ??
182
+ this.knownByRid.get(nodeRoutingId));
183
+ }
150
184
  hasKnownChannelTarget(channelName) {
151
185
  requireText(channelName, 'channelName');
152
186
  return [...this.knownByRid.values()].some((descriptor) => (descriptor.state === 'serving' ||
@@ -66,11 +66,11 @@ export declare class ServiceWireProtocolError extends Error {
66
66
  }
67
67
  export declare function encodeNodeSendHeader(): Buffer;
68
68
  export declare function encodeNodeRequestHeader(correlation: bigint): Buffer;
69
- export declare function decodeNodeRequestHeader(frame: Uint8Array): bigint;
69
+ export declare function decodeNodeRequestHeader(frame: Uint8Array, header?: ServiceWireHeader): bigint;
70
70
  export declare function encodeChannelSendHeader(channelName: string): Buffer;
71
- export declare function decodeChannelSendHeader(frame: Uint8Array): string;
71
+ export declare function decodeChannelSendHeader(frame: Uint8Array, header?: ServiceWireHeader): string;
72
72
  export declare function encodeChannelRequestHeader(correlation: bigint, channelName: string): Buffer;
73
- export declare function decodeChannelRequestHeader(frame: Uint8Array): {
73
+ export declare function decodeChannelRequestHeader(frame: Uint8Array, header?: ServiceWireHeader): {
74
74
  readonly correlation: bigint;
75
75
  readonly channelName: string;
76
76
  };