@zlink-systems/framework 0.25.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,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RawServiceMeshRuntime = void 0;
4
- const node_crypto_1 = require("node:crypto");
5
4
  const runtime_values_1 = require("../backend/runtime-values");
6
5
  const application_ingress_record_owner_1 = require("../application-jobs/application-ingress-record-owner");
7
6
  const operation_registry_1 = require("./operation-registry");
@@ -11,9 +10,6 @@ const service_topology_registry_1 = require("./service-topology-registry");
11
10
  const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
12
11
  const service_wire_codec_1 = require("./service-wire-codec");
13
12
  const service_wire_constants_generated_1 = require("./service-wire-constants.generated");
14
- const MONITOR_DISCONNECTED = 0x0200;
15
- const MONITOR_CONNECTION_READY = 0x1000;
16
- const MONITOR_CONNECTION_READY_EDGE = 1;
17
13
  // Framework error code 13 (RequestTargetNotFound) is encoded as 14 on a
18
14
  // RequestResult.NotFound reply. Boundary transport results keep failureCode 0.
19
15
  const REQUEST_TARGET_NOT_FOUND_FAILURE_CODE = 14;
@@ -22,8 +18,6 @@ const livenessCodec = (0, service_wire_codec_1.createServiceWireCodec)({
22
18
  major: 1,
23
19
  commands: service_wire_m6a_codec_1.M6aServiceWireCommand
24
20
  });
25
- const MAX_PENDING_MONITOR_EVENTS = 8192;
26
- const MAX_UNRESOLVED_CONNECTION_CANDIDATES = 4096;
27
21
  /**
28
22
  * RouteMesh M6A runtime built only on the public raw binding package.
29
23
  * It owns protocol, admission, mailbox, request and liveness state.
@@ -36,17 +30,11 @@ class RawServiceMeshRuntime {
36
30
  expectedPeers = new Map();
37
31
  endpointOnlyPeers = new Set();
38
32
  peerConnectionIntentRemoved = new Set();
39
- connectionCandidates = new Map();
40
- unresolvedConnectionCandidates = [];
41
- connectionIds = new Map();
42
33
  /**
43
- * The monitor drain updates this fence before the event reaches the
44
- * normal runtime handler. `null` means that the last observed pair is no
45
- * longer usable by the application route.
34
+ * The last observed Core selected route of each RID (Core ROUTER §10.1):
35
+ * RID -> route generation. Only `observeSelectedRoutes` replaces it.
46
36
  */
47
- monitorConnectionStates = new Map();
48
- monitorEvents = [];
49
- monitorDrainBuffer = [];
37
+ selectedRoutes = new Map();
50
38
  bindingPort;
51
39
  applicationJobQueue;
52
40
  peerAdmissionSealed;
@@ -59,7 +47,6 @@ class RawServiceMeshRuntime {
59
47
  descriptor;
60
48
  host;
61
49
  router;
62
- monitor;
63
50
  nextCorrelation = 1n;
64
51
  serviceIngress;
65
52
  closed = false;
@@ -102,8 +89,6 @@ class RawServiceMeshRuntime {
102
89
  };
103
90
  this.topology.publishLocal(next);
104
91
  this.descriptor = next;
105
- this.monitor = router.monitor();
106
- this.pullMonitorEvents();
107
92
  this.host = host;
108
93
  this.router = router;
109
94
  }
@@ -237,24 +222,22 @@ class RawServiceMeshRuntime {
237
222
  ]);
238
223
  }
239
224
  isPeerRouteReady(nodeRoutingId, lifecycleGeneration) {
225
+ // Admission is bound to the observed selected route: the route observer
226
+ // removes the peer when that route ends or is replaced.
240
227
  const peer = this.topology.peer(nodeRoutingId);
241
- const monitorConnection = this.monitorConnectionStates.get(nodeRoutingId);
242
- const applicationRouteReady = monitorConnection === undefined || monitorConnection === peer?.connectionId;
243
- const ready = peer !== undefined &&
228
+ return (peer !== undefined &&
244
229
  (lifecycleGeneration === undefined ||
245
230
  peer.descriptor.lifecycleGeneration === lifecycleGeneration) &&
246
- applicationRouteReady &&
247
- this.liveness.isReady(nodeRoutingId, peer.connectionId);
248
- return ready;
231
+ this.liveness.isReady(nodeRoutingId, peer.connectionId));
249
232
  }
250
233
  async announceExpectedPeers() {
251
234
  let accepted = 0;
252
235
  for (const nodeRoutingId of this.expectedPeers.keys()) {
253
- // Admission is a one-time fence for the current physical connection.
236
+ // Admission is a one-time fence for the current selected route.
254
237
  // Re-sending Hello after the peer is admitted would re-run admission on
255
238
  // every poll and reset the liveness record before application traffic
256
- // can use the route. A disconnected peer is removed by monitor handling
257
- // and remains eligible for the next admission attempt.
239
+ // can use the route. A peer whose selected route ended is removed by the
240
+ // route observer and remains eligible for the next admission attempt.
258
241
  if (this.topology.peer(nodeRoutingId) !== undefined)
259
242
  continue;
260
243
  if (await this.announcePeer(nodeRoutingId))
@@ -410,7 +393,6 @@ class RawServiceMeshRuntime {
410
393
  ]);
411
394
  }
412
395
  async pumpOne(nowMs = performance.now(), observe) {
413
- this.pullMonitorEvents();
414
396
  return this.receiveOne(nowMs, observe);
415
397
  }
416
398
  setReadableHandler(handler) {
@@ -418,7 +400,7 @@ class RawServiceMeshRuntime {
418
400
  }
419
401
  /** Returns whether a receive budget ended before no-data; idle ticks only maintain peers. */
420
402
  async pumpBatch(receiveReady = true) {
421
- await this.drainMonitorEvents();
403
+ await this.observeSelectedRoutes();
422
404
  const startedAtMs = performance.now();
423
405
  let messages = 0;
424
406
  let bytes = 0;
@@ -483,22 +465,6 @@ class RawServiceMeshRuntime {
483
465
  applicationJobOwner.close();
484
466
  }
485
467
  }
486
- pullMonitorEvents() {
487
- this.monitor?.drain((event) => {
488
- this.observeMonitorEvent(event);
489
- if (this.monitorEvents.length < MAX_PENDING_MONITOR_EVENTS) {
490
- this.monitorEvents.push(event);
491
- return;
492
- }
493
- const existingIndex = this.monitorEvents.findIndex((candidate) => sameMonitorConnection(candidate, event));
494
- if (existingIndex >= 0)
495
- this.monitorEvents[existingIndex] = event;
496
- else {
497
- this.monitorEvents.shift();
498
- this.monitorEvents.push(event);
499
- }
500
- });
501
- }
502
468
  reportProtocolError(received) {
503
469
  const request = received.requestSeq !== undefined;
504
470
  const replied = request && this.replyGenericProtocolError(received);
@@ -564,8 +530,7 @@ class RawServiceMeshRuntime {
564
530
  await this.send(received.sourceRid, [(0, service_wire_m6a_codec_1.encodeReject)(3)]);
565
531
  return 'infrastructure';
566
532
  }
567
- const connection = this.currentConnectionCandidate(received.sourceRid, descriptor.advertisedEndpoint);
568
- const result = this.admitPeer(descriptor, connection, nowMs, expected);
533
+ const result = this.admitPeer(descriptor, routeConnectionId(received.routeGeneration), nowMs, expected);
569
534
  if (result !== 'admitted' && result !== 'notRequired') {
570
535
  const detail = result === 'invalidDescriptor'
571
536
  ? describeInvalidAdmissionDescriptor(descriptor, expected, this.topology.peer(descriptor.nodeRoutingId)?.descriptor)
@@ -594,10 +559,8 @@ class RawServiceMeshRuntime {
594
559
  if (received.parts.length !== 1)
595
560
  return 'protocolError';
596
561
  const reason = (0, service_wire_m6a_codec_1.decodeReject)(received.parts[0]);
597
- // A reject belongs to one physical candidate, but the wire record does
598
- // not carry that candidate's connection id. Keep an already admitted
599
- // peer: bilateral manual connect can reject the duplicate after the
600
- // other candidate has become Ready.
562
+ // Keep an already admitted peer: a reject of an earlier Hello on the
563
+ // same selected route does not end the admission that succeeded.
601
564
  if (reason === 4 && this.topology.peer(received.sourceRid) === undefined) {
602
565
  const expected = this.expectedPeers.get(received.sourceRid);
603
566
  const local = this.topology.localDescriptor();
@@ -752,121 +715,48 @@ class RawServiceMeshRuntime {
752
715
  const peer = this.topology.peer(nodeRoutingId);
753
716
  if (peer === undefined)
754
717
  continue;
755
- // Liveness timeout is a semantic peer removal. Route topology,
756
- // physical candidate state, and auto-connect intent must be retired by
757
- // the same owner so a failed endpoint cannot be selected again.
758
- this.removeConnectionCandidate(nodeRoutingId, peer.connectionId);
718
+ // Liveness timeout is a semantic peer removal of the admitted route.
759
719
  this.removePeer(peer);
760
720
  }
761
721
  return result;
762
722
  }
763
- async drainMonitorEvents(nowMs = performance.now()) {
764
- this.pullMonitorEvents();
765
- let handled = 0;
766
- // Detach the current batch so monitor pulls that run while an event is
767
- // being handled append to the next batch without copying or reindexing it.
768
- const events = this.monitorEvents;
769
- this.monitorEvents = this.monitorDrainBuffer;
770
- this.monitorDrainBuffer = events;
771
- this.monitorEvents.length = 0;
772
- for (const event of events) {
773
- handled++;
774
- const nodeRoutingId = event.routingId ?? this.expectedPeerRoutingId(event.remoteAddress);
775
- if (event.event === MONITOR_CONNECTION_READY && isConnectionReadyEdge(event)) {
776
- if (nodeRoutingId === undefined) {
777
- const candidate = this.createUnresolvedConnectionCandidate(event);
778
- const alreadyQueued = this.unresolvedConnectionCandidates.some((value) => value.connectionId === candidate.connectionId);
779
- if (!alreadyQueued &&
780
- this.unresolvedConnectionCandidates.length < MAX_UNRESOLVED_CONNECTION_CANDIDATES) {
781
- this.unresolvedConnectionCandidates.push(candidate);
782
- }
783
- continue;
784
- }
785
- if (!this.acceptsExpectedMonitorEndpoint(nodeRoutingId, event)) {
786
- continue;
787
- }
788
- const candidate = this.createConnectionCandidate(nodeRoutingId, event);
789
- const existingPeer = this.topology.peer(nodeRoutingId);
790
- if (existingPeer !== undefined && existingPeer.connectionId === candidate.connectionId) {
791
- this.monitorConnectionStates.set(nodeRoutingId, candidate.connectionId);
792
- continue;
793
- }
794
- if (existingPeer !== undefined) {
795
- if (this.liveness.isReady(nodeRoutingId, existingPeer.connectionId)) {
796
- const existingCandidate = this.connectionCandidates
797
- .get(nodeRoutingId)
798
- ?.get(existingPeer.connectionId);
799
- if (this.isDiscoveredLifecycleReplacement(existingPeer, nodeRoutingId)) {
800
- // A current discovery descriptor with the same RID but a
801
- // different lifecycle token denotes a replacement process.
802
- // Keep its physical pair provisional until its Hello passes
803
- // the descriptor fence; the old ready peer remains semantic
804
- // current until then.
805
- let provisionalCandidates = this.connectionCandidates.get(nodeRoutingId);
806
- if (provisionalCandidates === undefined) {
807
- provisionalCandidates = new Map();
808
- this.connectionCandidates.set(nodeRoutingId, provisionalCandidates);
809
- }
810
- provisionalCandidates.set(candidate.connectionId, candidate);
811
- await this.announcePeer(nodeRoutingId);
812
- continue;
813
- }
814
- if (existingCandidate?.connectionId === candidate.connectionId) {
815
- this.removeConnectionCandidate(nodeRoutingId, candidate.connectionId);
816
- this.monitorConnectionStates.set(nodeRoutingId, existingPeer.connectionId);
817
- continue;
818
- }
819
- // A non-replacement ready route is the current semantic owner.
820
- // A second physical candidate may be admitted only after the
821
- // current lease expires; this also prevents a late old-process
822
- // reconnect from replacing the ready replacement by arrival order.
823
- this.removeConnectionCandidate(nodeRoutingId, candidate.connectionId);
824
- continue;
825
- }
826
- // Keep the physical candidate until the wire descriptor decides
827
- // whether it is the current logical peer. READY events can race
828
- // with DISCONNECTED events during reconnect; rejecting the READY
829
- // here can terminate the replacement before admission evaluates its
830
- // lifecycle generation.
831
- let provisionalCandidates = this.connectionCandidates.get(nodeRoutingId);
832
- if (provisionalCandidates === undefined) {
833
- provisionalCandidates = new Map();
834
- this.connectionCandidates.set(nodeRoutingId, provisionalCandidates);
835
- }
836
- provisionalCandidates.set(candidate.connectionId, candidate);
837
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
838
- await this.announcePeer(nodeRoutingId);
839
- continue;
840
- }
841
- let candidates = this.connectionCandidates.get(nodeRoutingId);
842
- if (candidates === undefined) {
843
- candidates = new Map();
844
- this.connectionCandidates.set(nodeRoutingId, candidates);
845
- }
846
- candidates.set(candidate.connectionId, candidate);
847
- // Before wire admission the monitor candidate is the only physical
848
- // route evidence available to the admission message.
849
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
850
- await this.announcePeer(nodeRoutingId);
851
- }
852
- else if (event.event === MONITOR_DISCONNECTED && nodeRoutingId !== undefined) {
853
- const peer = this.topology.peer(nodeRoutingId);
854
- const disconnectedId = monitorConnectionId(event, nodeRoutingId);
855
- this.removeConnectionCandidate(nodeRoutingId, disconnectedId);
856
- if (peer !== undefined && peer.connectionId === disconnectedId) {
857
- this.removePeer(peer);
858
- }
859
- }
860
- else if (event.event === MONITOR_DISCONNECTED) {
861
- const disconnectedId = monitorConnectionId(event);
862
- const index = this.unresolvedConnectionCandidates.findIndex((candidate) => candidate.connectionId === disconnectedId);
863
- if (index >= 0)
864
- this.unresolvedConnectionCandidates.splice(index, 1);
723
+ /**
724
+ * The socket's one route observer (Core ROUTER §10.1). It replaces the
725
+ * observation with Core's selected-route snapshot, ends the admission of
726
+ * every RID whose observed route disappeared or was replaced, and starts the
727
+ * handshake of every newly selected route. Returns the number of changes.
728
+ */
729
+ async observeSelectedRoutes() {
730
+ const router = this.router;
731
+ if (router === undefined || this.closed)
732
+ return 0;
733
+ const observed = new Map();
734
+ for (const route of router.routesSnapshot()) {
735
+ observed.set(route.routingId, route.routeGeneration);
736
+ }
737
+ let changes = 0;
738
+ for (const [nodeRoutingId, generation] of this.selectedRoutes) {
739
+ if (observed.get(nodeRoutingId) === generation)
740
+ continue;
741
+ changes++;
742
+ const peer = this.topology.peer(nodeRoutingId);
743
+ if (peer !== undefined && peer.connectionId === routeConnectionId(generation)) {
744
+ this.removePeer(peer);
865
745
  }
866
746
  }
867
- this.monitorDrainBuffer.length = 0;
868
- void nowMs;
869
- return handled;
747
+ const selected = [];
748
+ for (const [nodeRoutingId, generation] of observed) {
749
+ if (this.selectedRoutes.get(nodeRoutingId) === generation)
750
+ continue;
751
+ if (!this.selectedRoutes.has(nodeRoutingId))
752
+ changes++;
753
+ selected.push(nodeRoutingId);
754
+ }
755
+ this.selectedRoutes = observed;
756
+ // A new selected route admits only through a new handshake.
757
+ for (const nodeRoutingId of selected)
758
+ await this.announcePeer(nodeRoutingId);
759
+ return changes;
870
760
  }
871
761
  close() {
872
762
  if (this.closed)
@@ -880,8 +770,6 @@ class RawServiceMeshRuntime {
880
770
  if (router !== undefined) {
881
771
  this.applicationJobQueue.unregisterReceiveFlowTarget?.(router);
882
772
  }
883
- this.monitor?.close();
884
- this.monitor = undefined;
885
773
  const host = this.host;
886
774
  this.router = undefined;
887
775
  this.host = undefined;
@@ -964,16 +852,13 @@ class RawServiceMeshRuntime {
964
852
  })().catch((error) => this.operations.fail(pending.id, error));
965
853
  return pending;
966
854
  }
967
- if (!this.isPeerRouteReady(selectedTargetNodeRoutingId)) {
968
- // Preserve Core's distinct target-not-found result for an RID the
969
- // topology has never observed. A known peer without a usable route is
970
- // a Framework NotConnected result; an unknown RID is completed locally
971
- // as RequestTargetNotFound without submitting an application frame.
972
- const knownTarget = this.topology.peer(selectedTargetNodeRoutingId) !== undefined ||
973
- this.topology.knownDescriptor(selectedTargetNodeRoutingId) !== undefined;
855
+ if (this.topology.peer(selectedTargetNodeRoutingId) === undefined) {
856
+ // Logical target selection is the requester's decision: an RID that is
857
+ // not an admitted peer is not a target. Whether an admitted target's
858
+ // route can carry the request is Core's REQUEST result.
974
859
  this.operations.complete(pending.id, {
975
- terminalResult: knownTarget ? runtime_values_1.RequestResult.NotConnected : runtime_values_1.RequestResult.NotFound,
976
- failureCode: knownTarget ? 0 : REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
860
+ terminalResult: runtime_values_1.RequestResult.NotFound,
861
+ failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
977
862
  });
978
863
  return pending;
979
864
  }
@@ -1031,7 +916,7 @@ class RawServiceMeshRuntime {
1031
916
  applicationPayload(payload) {
1032
917
  return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payload) : payload;
1033
918
  }
1034
- admitPeer(descriptor, connection, nowMs, expected) {
919
+ admitPeer(descriptor, connectionId, nowMs, expected) {
1035
920
  const previous = this.topology.peer(descriptor.nodeRoutingId);
1036
921
  if (previous !== undefined &&
1037
922
  previous.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
@@ -1040,190 +925,16 @@ class RawServiceMeshRuntime {
1040
925
  !(0, service_topology_registry_1.sameServiceNodeDescriptor)(previous.descriptor, descriptor)))) {
1041
926
  throw new service_wire_m6a_codec_1.ServiceWireProtocolError(`RouteMesh peer '${descriptor.nodeRoutingId}' descriptor revision is stale or conflicting.`);
1042
927
  }
1043
- const result = this.topology.admit(descriptor, connection.connectionId, expected, connection.discriminator);
928
+ const result = this.topology.admit(descriptor, connectionId, expected);
1044
929
  if (result === 'admitted') {
1045
- this.connectionIds.set(descriptor.nodeRoutingId, connection.connectionId);
1046
- this.liveness.admit(descriptor.nodeRoutingId, connection.connectionId, nowMs);
1047
- this.liveness.requestProbe(descriptor.nodeRoutingId, connection.connectionId, nowMs);
1048
- this.monitorConnectionStates.set(descriptor.nodeRoutingId, connection.connectionId);
1049
- if (previous !== undefined && previous.connectionId !== connection.connectionId) {
1050
- const previousCandidate = this.connectionCandidates
1051
- .get(descriptor.nodeRoutingId)
1052
- ?.get(previous.connectionId);
1053
- if (previousCandidate !== undefined) {
1054
- this.removeConnectionCandidate(descriptor.nodeRoutingId, previousCandidate.connectionId);
1055
- }
1056
- }
930
+ this.liveness.admit(descriptor.nodeRoutingId, connectionId, nowMs);
931
+ this.liveness.requestProbe(descriptor.nodeRoutingId, connectionId, nowMs);
1057
932
  }
1058
933
  else if (result === 'notRequired' && previous !== undefined) {
1059
934
  this.liveness.disconnect(descriptor.nodeRoutingId, previous.connectionId);
1060
935
  }
1061
- else if (previous !== undefined) {
1062
- this.connectionIds.set(descriptor.nodeRoutingId, previous.connectionId);
1063
- if (connection.connectionId !== previous.connectionId) {
1064
- this.removeConnectionCandidate(descriptor.nodeRoutingId, connection.connectionId);
1065
- }
1066
- }
1067
936
  return result;
1068
937
  }
1069
- currentConnectionCandidate(nodeRoutingId, advertisedEndpoint) {
1070
- let connectionId = this.connectionIds.get(nodeRoutingId);
1071
- let candidate = connectionId === undefined
1072
- ? undefined
1073
- : this.connectionCandidates.get(nodeRoutingId)?.get(connectionId);
1074
- if (candidate === undefined) {
1075
- candidate = this.promoteUnresolvedConnectionCandidate(nodeRoutingId, advertisedEndpoint);
1076
- connectionId = candidate?.connectionId;
1077
- }
1078
- if (candidate === undefined) {
1079
- const localRid = this.topology.localDescriptor().nodeRoutingId;
1080
- const endpointOnly = this.endpointOnlyPeers.has(advertisedEndpoint);
1081
- const direction = this.expectedPeers.has(nodeRoutingId)
1082
- ? 'outbound'
1083
- : endpointOnly
1084
- ? localRid.localeCompare(nodeRoutingId) <= 0
1085
- ? 'outbound'
1086
- : 'inbound'
1087
- : 'inbound';
1088
- const initiator = endpointOnly && localRid.localeCompare(nodeRoutingId) > 0
1089
- ? nodeRoutingId
1090
- : direction === 'outbound'
1091
- ? localRid
1092
- : nodeRoutingId;
1093
- connectionId = `unmonitored:${direction}:${(0, node_crypto_1.randomUUID)()}`;
1094
- candidate = {
1095
- connectionId,
1096
- direction,
1097
- discriminator: `initiator:${initiator}`,
1098
- localAddress: '',
1099
- remoteAddress: advertisedEndpoint
1100
- };
1101
- let candidates = this.connectionCandidates.get(nodeRoutingId);
1102
- if (candidates === undefined) {
1103
- candidates = new Map();
1104
- this.connectionCandidates.set(nodeRoutingId, candidates);
1105
- }
1106
- candidates.set(connectionId, candidate);
1107
- this.connectionIds.set(nodeRoutingId, connectionId);
1108
- }
1109
- return candidate;
1110
- }
1111
- createConnectionCandidate(nodeRoutingId, event) {
1112
- const local = this.topology.localDescriptor();
1113
- const expected = this.expectedPeers.get(nodeRoutingId);
1114
- const direction = event.localAddress === local.advertisedEndpoint
1115
- ? 'inbound'
1116
- : expected?.endpoint === event.remoteAddress
1117
- ? 'outbound'
1118
- : this.endpointOnlyPeers.has(event.remoteAddress)
1119
- ? 'outbound'
1120
- : 'unknown';
1121
- const initiator = direction === 'outbound'
1122
- ? local.nodeRoutingId
1123
- : direction === 'inbound'
1124
- ? nodeRoutingId
1125
- : undefined;
1126
- return {
1127
- connectionId: monitorConnectionId(event, nodeRoutingId),
1128
- direction,
1129
- discriminator: initiator === undefined
1130
- ? `unknown:${monitorConnectionId(event, nodeRoutingId)}`
1131
- : `initiator:${initiator}`,
1132
- localAddress: event.localAddress,
1133
- remoteAddress: event.remoteAddress
1134
- };
1135
- }
1136
- createUnresolvedConnectionCandidate(event) {
1137
- const connectionId = monitorConnectionId(event);
1138
- const local = this.topology.localDescriptor();
1139
- const direction = this.endpointOnlyPeers.has(event.remoteAddress) ||
1140
- this.expectedPeerRoutingId(event.remoteAddress) !== undefined
1141
- ? 'outbound'
1142
- : event.localAddress === local.advertisedEndpoint
1143
- ? 'inbound'
1144
- : 'unknown';
1145
- return {
1146
- connectionId,
1147
- direction,
1148
- discriminator: `unresolved:${connectionId}`,
1149
- localAddress: event.localAddress,
1150
- remoteAddress: event.remoteAddress
1151
- };
1152
- }
1153
- promoteUnresolvedConnectionCandidate(nodeRoutingId, advertisedEndpoint) {
1154
- const relevant = this.unresolvedConnectionCandidates
1155
- .map((candidate, index) => ({ candidate, index }))
1156
- .filter(({ candidate }) => candidate.remoteAddress === advertisedEndpoint || candidate.direction === 'inbound');
1157
- if (relevant.length === 0)
1158
- return undefined;
1159
- const directions = new Set(relevant.map((value) => value.candidate.direction));
1160
- const localRid = this.topology.localDescriptor().nodeRoutingId;
1161
- const preferredDirection = directions.has('inbound') && directions.has('outbound')
1162
- ? localRid.localeCompare(nodeRoutingId) <= 0
1163
- ? 'outbound'
1164
- : 'inbound'
1165
- : relevant.at(-1).candidate.direction;
1166
- const selected = [...relevant].reverse().find((value) => value.candidate.direction === preferredDirection) ??
1167
- relevant.at(-1);
1168
- this.unresolvedConnectionCandidates.splice(selected.index, 1);
1169
- const initiator = selected.candidate.direction === 'outbound'
1170
- ? localRid
1171
- : selected.candidate.direction === 'inbound'
1172
- ? nodeRoutingId
1173
- : undefined;
1174
- const candidate = {
1175
- ...selected.candidate,
1176
- discriminator: initiator === undefined
1177
- ? `unknown:${selected.candidate.connectionId}`
1178
- : `initiator:${initiator}`
1179
- };
1180
- let candidates = this.connectionCandidates.get(nodeRoutingId);
1181
- if (candidates === undefined) {
1182
- candidates = new Map();
1183
- this.connectionCandidates.set(nodeRoutingId, candidates);
1184
- }
1185
- candidates.set(candidate.connectionId, candidate);
1186
- this.connectionIds.set(nodeRoutingId, candidate.connectionId);
1187
- return candidate;
1188
- }
1189
- removeConnectionCandidate(nodeRoutingId, connectionId) {
1190
- const candidates = this.connectionCandidates.get(nodeRoutingId);
1191
- candidates?.delete(connectionId);
1192
- if (candidates?.size === 0)
1193
- this.connectionCandidates.delete(nodeRoutingId);
1194
- if (this.connectionIds.get(nodeRoutingId) === connectionId) {
1195
- this.connectionIds.delete(nodeRoutingId);
1196
- }
1197
- }
1198
- isDiscoveredLifecycleReplacement(admitted, nodeRoutingId) {
1199
- const expected = this.expectedPeers.get(nodeRoutingId);
1200
- // A same-RID replacement is permitted only from a complete discovery
1201
- // fence. Manual/endpoint-only intent has no authority to replace a ready
1202
- // peer, and lifecycle generations are opaque equality tokens.
1203
- return (expected?.endpoint !== undefined &&
1204
- expected.securityIdentity !== undefined &&
1205
- expected.lifecycleGeneration !== undefined &&
1206
- admitted.descriptor.lifecycleGeneration !== expected.lifecycleGeneration);
1207
- }
1208
- expectedPeerRoutingId(endpoint) {
1209
- for (const [nodeRoutingId, expected] of this.expectedPeers) {
1210
- if (expected.endpoint === endpoint)
1211
- return nodeRoutingId;
1212
- }
1213
- return undefined;
1214
- }
1215
- acceptsExpectedMonitorEndpoint(nodeRoutingId, event) {
1216
- // A monitor address is the physical socket address, which may be an
1217
- // ephemeral port. Once Core has resolved the peer routing ID, that ID is
1218
- // the semantic identity; comparing the physical address with the
1219
- // discovery advertised endpoint would reject valid READY events.
1220
- if (event.routingId === nodeRoutingId)
1221
- return true;
1222
- const expected = this.expectedPeers.get(nodeRoutingId);
1223
- return (expected === undefined ||
1224
- expected.endpoint === undefined ||
1225
- expected.endpoint === event.remoteAddress);
1226
- }
1227
938
  retireNotRequiredExpectedPeer(nodeRoutingId, advertisedEndpoint) {
1228
939
  const expected = this.expectedPeers.get(nodeRoutingId);
1229
940
  const endpoint = expected?.endpoint ??
@@ -1254,31 +965,10 @@ class RawServiceMeshRuntime {
1254
965
  this.endpointOnlyPeers.delete(endpoint);
1255
966
  }
1256
967
  removePeer(peer) {
1257
- if (this.monitorConnectionStates.get(peer.descriptor.nodeRoutingId) === peer.connectionId) {
1258
- this.monitorConnectionStates.set(peer.descriptor.nodeRoutingId, null);
1259
- }
1260
968
  this.topology.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
1261
969
  this.liveness.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
1262
970
  this.onPeerDisconnected?.(peer.descriptor.nodeRoutingId, peer.descriptor.advertisedEndpoint, peer.descriptor.lifecycleGeneration);
1263
971
  }
1264
- observeMonitorEvent(event) {
1265
- const nodeRoutingId = event.routingId ?? this.expectedPeerRoutingId(event.remoteAddress);
1266
- if (nodeRoutingId === undefined)
1267
- return;
1268
- const connectionId = monitorConnectionId(event, nodeRoutingId);
1269
- if (event.event === MONITOR_CONNECTION_READY && isConnectionReadyEdge(event)) {
1270
- if (!this.acceptsExpectedMonitorEndpoint(nodeRoutingId, event))
1271
- return;
1272
- // The event is queued for admission. Updating monitor state here would
1273
- // allow a late stale READY to overwrite the admitted replacement.
1274
- return;
1275
- }
1276
- if (event.event === MONITOR_DISCONNECTED &&
1277
- this.monitorConnectionStates.get(nodeRoutingId) === connectionId &&
1278
- this.topology.peer(nodeRoutingId)?.connectionId === connectionId) {
1279
- this.monitorConnectionStates.set(nodeRoutingId, null);
1280
- }
1281
- }
1282
972
  requireStarted() {
1283
973
  if (this.router === undefined)
1284
974
  throw new Error('Raw service runtime is not started.');
@@ -1331,28 +1021,12 @@ function isAlreadyDisconnectedError(error) {
1331
1021
  }
1332
1022
  return error.nativeErrno === 2;
1333
1023
  }
1334
- function monitorConnectionId(event, resolvedNodeRoutingId = event.routingId ?? '') {
1335
- if (event.connectionId !== undefined && event.connectionId !== 0n) {
1336
- return JSON.stringify([resolvedNodeRoutingId, 'connection', event.connectionId.toString()]);
1337
- }
1338
- return JSON.stringify([
1339
- // Monitor `value` is event-specific: CONNECTION_READY reports the
1340
- // socket's ready count and DISCONNECTED reports its reason. It is not a
1341
- // connection identity. The public monitor record has no native
1342
- // connection id, so use the stable routing and endpoint tuple instead.
1343
- resolvedNodeRoutingId,
1344
- event.localAddress,
1345
- event.remoteAddress
1346
- ]);
1347
- }
1348
- function sameMonitorConnection(left, right) {
1349
- return (monitorConnectionId(left) === monitorConnectionId(right) &&
1350
- left.remoteAddress === right.remoteAddress);
1351
- }
1352
- function isConnectionReadyEdge(event) {
1353
- return event.flags === undefined
1354
- ? event.value > 0n
1355
- : (event.flags & MONITOR_CONNECTION_READY_EDGE) !== 0;
1024
+ /**
1025
+ * An admission belongs to one Core selected route. Its connection identity is
1026
+ * that route's generation, an opaque token compared only for equality.
1027
+ */
1028
+ function routeConnectionId(routeGeneration) {
1029
+ return `route:${routeGeneration.toString()}`;
1356
1030
  }
1357
1031
  function describeInvalidAdmissionDescriptor(descriptor, expected, current) {
1358
1032
  try {
@@ -47,6 +47,7 @@ export declare function replaceServiceAuthorityRelocationState(payload: Uint8Arr
47
47
  export declare function encodeServiceUserSpotAuthorityPayload(value: ServiceUserSpotAuthorityPayload): Buffer;
48
48
  export declare function encodeServiceInstanceAuthorityPayload(value: ServiceInstanceAuthorityPayload): Buffer;
49
49
  export declare function decodeServiceReadySpotAuthority(payload: Uint8Array): ServiceReadySpotAuthority | undefined;
50
+ export declare function decodeServiceClosingSpotAuthority(payload: Uint8Array): ServiceReadySpotAuthority | undefined;
50
51
  export declare function decodeServiceInstanceAuthorityPayload(payload: Uint8Array): ServiceDecodedInstanceAuthority | undefined;
51
52
  export declare function rewriteServiceAuthorityOwner(payload: Uint8Array, owner: ZLinkLocationOwnerToken): Buffer | undefined;
52
53
  export declare function rewriteServiceAuthorityRoute(payload: Uint8Array, owner: ZLinkLocationOwnerToken, target: {
@@ -4,6 +4,7 @@ exports.replaceServiceAuthorityRelocationState = replaceServiceAuthorityRelocati
4
4
  exports.encodeServiceUserSpotAuthorityPayload = encodeServiceUserSpotAuthorityPayload;
5
5
  exports.encodeServiceInstanceAuthorityPayload = encodeServiceInstanceAuthorityPayload;
6
6
  exports.decodeServiceReadySpotAuthority = decodeServiceReadySpotAuthority;
7
+ exports.decodeServiceClosingSpotAuthority = decodeServiceClosingSpotAuthority;
7
8
  exports.decodeServiceInstanceAuthorityPayload = decodeServiceInstanceAuthorityPayload;
8
9
  exports.rewriteServiceAuthorityOwner = rewriteServiceAuthorityOwner;
9
10
  exports.rewriteServiceAuthorityRoute = rewriteServiceAuthorityRoute;
@@ -84,6 +85,13 @@ function decodeServiceReadySpotAuthority(payload) {
84
85
  const decoded = decodeSpotAuthority(payload);
85
86
  return decoded?.state === 'ready' && decoded.operationKind === 0 ? decoded : undefined;
86
87
  }
88
+ function decodeServiceClosingSpotAuthority(payload) {
89
+ const decoded = decodeSpotAuthority(payload);
90
+ return decoded?.operationKind === 3 &&
91
+ (decoded.kind === 'user_spot' || decoded.state === 'closing')
92
+ ? decoded
93
+ : undefined;
94
+ }
87
95
  function decodeServiceInstanceAuthorityPayload(payload) {
88
96
  const decoded = decodeSpotAuthority(payload);
89
97
  if (decoded?.kind !== 'instance_spot' ||