@zlink-systems/framework 0.27.0 → 0.29.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 (191) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActorClient.d.ts +1 -1
  3. package/dist/contracts/Channels/Calls.d.ts +1 -1
  4. package/dist/contracts/Configuration/Builders.d.ts +2 -1
  5. package/dist/contracts/Configuration/Configs.d.ts +0 -1
  6. package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
  7. package/dist/contracts/Configuration/InternalDefaults.js +4 -1
  8. package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
  9. package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
  10. package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
  11. package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -5
  12. package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
  13. package/dist/contracts/Spots/Contracts.d.ts +1 -1
  14. package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
  15. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
  16. package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
  17. package/dist/runtime/actors/actor-client.js +7 -10
  18. package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
  19. package/dist/runtime/actors/actor-mailbox.js +2 -2
  20. package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
  21. package/dist/runtime/actors/actor-request-deadline.js +4 -29
  22. package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
  23. package/dist/runtime/backend/contracts/index.d.ts +1 -4
  24. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
  25. package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
  26. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  27. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
  28. package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
  29. package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
  30. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  31. package/dist/runtime/channels/channel-clients.js +8 -10
  32. package/dist/runtime/channels/channel-envelope.d.ts +1 -1
  33. package/dist/runtime/channels/channel-envelope.js +4 -10
  34. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  35. package/dist/runtime/channels/channel-outbound-operations.js +20 -36
  36. package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
  37. package/dist/runtime/channels/channel-runtime-manager.js +8 -8
  38. package/dist/runtime/channels/channel-socket-options.js +0 -10
  39. package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
  40. package/dist/runtime/channels/channel-socket-registry.js +3 -14
  41. package/dist/runtime/channels/channel-transports.d.ts +10 -14
  42. package/dist/runtime/channels/channel-transports.js +11 -14
  43. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  44. package/dist/runtime/channels/local-spot-route-transport.js +1 -3
  45. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
  46. package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
  47. package/dist/runtime/channels/source-spot-router.d.ts +1 -1
  48. package/dist/runtime/channels/source-spot-router.js +2 -4
  49. package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
  50. package/dist/runtime/channels/spot-node-route-transport.js +2 -3
  51. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
  52. package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
  53. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
  54. package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
  55. package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
  56. package/dist/runtime/execution/serial-execution-queue.js +116 -28
  57. package/dist/runtime/foundation/operation-registry.d.ts +3 -2
  58. package/dist/runtime/foundation/operation-registry.js +5 -1
  59. package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
  60. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
  61. package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
  62. package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
  63. package/dist/runtime/foundation/service-stateful-registry.js +5 -2
  64. package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
  65. package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
  66. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
  67. package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
  68. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
  69. package/dist/runtime/foundation/service-topology-registry.js +16 -17
  70. package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
  71. package/dist/runtime/host/actor-packet-relay.js +8 -10
  72. package/dist/runtime/host/index.js +1 -15
  73. package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
  74. package/dist/runtime/host/instance-activation-authority.js +46 -97
  75. package/dist/runtime/host/remote-bound-session-relay.js +8 -8
  76. package/dist/runtime/host/spot-address-transport.d.ts +1 -9
  77. package/dist/runtime/host/spot-address-transport.js +47 -87
  78. package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
  79. package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
  80. package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
  81. package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
  82. package/dist/runtime/locations/in-memory-authority-store.js +22 -8
  83. package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
  84. package/dist/runtime/locations/in-memory-location-store.js +3 -0
  85. package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
  86. package/dist/runtime/locations/location-store-repository.d.ts +1 -0
  87. package/dist/runtime/locations/location-store-repository.js +98 -53
  88. package/dist/runtime/messaging/request-deadline.d.ts +3 -0
  89. package/dist/runtime/messaging/request-deadline.js +39 -0
  90. package/dist/runtime/messaging/submission-result.d.ts +2 -1
  91. package/dist/runtime/messaging/submission-result.js +11 -4
  92. package/dist/runtime/spots/spot-outbound.d.ts +3 -5
  93. package/dist/runtime/spots/spot-outbound.js +12 -15
  94. package/dist/runtime/spots/spot-serial-executor.js +4 -1
  95. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  96. package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
  97. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
  98. package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
  99. package/dist/runtime/streams/bound-session-service.js +1 -1
  100. package/dist/runtime/streams/index.d.ts +2 -3
  101. package/dist/runtime/streams/index.js +6 -5
  102. package/dist/runtime/streams/managed-stream.d.ts +2 -2
  103. package/dist/runtime/streams/managed-stream.js +3 -5
  104. package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
  105. package/dist/runtime/streams/protocol.js +0 -9
  106. package/dist/runtime/streams/session-calls.d.ts +5 -7
  107. package/dist/runtime/streams/session-calls.js +6 -23
  108. package/dist/runtime/streams/session-context.d.ts +4 -8
  109. package/dist/runtime/streams/session-context.js +3 -32
  110. package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
  111. package/dist/runtime/streams/session-serial-executor.js +0 -3
  112. package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
  113. package/dist/runtime/streams/stream-message-utils.js +0 -20
  114. package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
  115. package/dist/runtime/streams/stream-session-runtime.js +40 -85
  116. package/package.json +3 -3
  117. package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
  118. package/src/contracts/Channels/Calls.ts +1 -1
  119. package/src/contracts/Configuration/Builders.ts +2 -1
  120. package/src/contracts/Configuration/Configs.ts +0 -1
  121. package/src/contracts/Configuration/InternalDefaults.ts +4 -0
  122. package/src/contracts/Configuration/RegistrationBuilders.ts +13 -3
  123. package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
  124. package/src/contracts/Configuration/RegistrationTypes.ts +3 -5
  125. package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
  126. package/src/contracts/Spots/Contracts.ts +1 -1
  127. package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
  128. package/src/contracts/Streams/IZLinkSession.ts +2 -3
  129. package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
  130. package/src/runtime/actors/actor-client.ts +9 -15
  131. package/src/runtime/actors/actor-mailbox.ts +7 -2
  132. package/src/runtime/actors/actor-request-deadline.ts +4 -49
  133. package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
  134. package/src/runtime/backend/contracts/index.ts +1 -8
  135. package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
  136. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
  137. package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
  138. package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
  139. package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
  140. package/src/runtime/channels/channel-clients.ts +9 -19
  141. package/src/runtime/channels/channel-envelope.ts +16 -28
  142. package/src/runtime/channels/channel-outbound-operations.ts +36 -38
  143. package/src/runtime/channels/channel-runtime-manager.ts +6 -28
  144. package/src/runtime/channels/channel-socket-options.ts +0 -13
  145. package/src/runtime/channels/channel-socket-registry.ts +3 -17
  146. package/src/runtime/channels/channel-transports.ts +7 -33
  147. package/src/runtime/channels/local-spot-route-transport.ts +1 -5
  148. package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
  149. package/src/runtime/channels/source-spot-router.ts +2 -9
  150. package/src/runtime/channels/spot-node-route-transport.ts +2 -7
  151. package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
  152. package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
  153. package/src/runtime/execution/serial-execution-queue.ts +117 -29
  154. package/src/runtime/foundation/operation-registry.ts +16 -9
  155. package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
  156. package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
  157. package/src/runtime/foundation/service-stateful-registry.ts +10 -5
  158. package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
  159. package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
  160. package/src/runtime/foundation/service-topology-registry.ts +30 -20
  161. package/src/runtime/host/actor-packet-relay.ts +8 -15
  162. package/src/runtime/host/index.ts +2 -26
  163. package/src/runtime/host/instance-activation-authority.ts +58 -125
  164. package/src/runtime/host/remote-bound-session-relay.ts +8 -16
  165. package/src/runtime/host/spot-address-transport.ts +107 -166
  166. package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
  167. package/src/runtime/locations/domain-store-contract.ts +5 -0
  168. package/src/runtime/locations/in-memory-authority-store.ts +42 -8
  169. package/src/runtime/locations/in-memory-location-store.ts +8 -0
  170. package/src/runtime/locations/internal-store-contracts.ts +1 -0
  171. package/src/runtime/locations/location-store-repository.ts +130 -68
  172. package/src/runtime/messaging/request-deadline.ts +58 -0
  173. package/src/runtime/messaging/submission-result.ts +18 -4
  174. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  175. package/src/runtime/spots/spot-outbound.ts +14 -24
  176. package/src/runtime/spots/spot-serial-executor.ts +4 -1
  177. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
  178. package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
  179. package/src/runtime/streams/bound-session-service.ts +1 -1
  180. package/src/runtime/streams/index.ts +5 -5
  181. package/src/runtime/streams/managed-stream.ts +3 -13
  182. package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
  183. package/src/runtime/streams/protocol.ts +0 -13
  184. package/src/runtime/streams/session-calls.ts +8 -34
  185. package/src/runtime/streams/session-context.ts +6 -47
  186. package/src/runtime/streams/session-serial-executor.ts +0 -8
  187. package/src/runtime/streams/stream-message-utils.ts +1 -27
  188. package/src/runtime/streams/stream-session-runtime.ts +54 -97
  189. package/dist/runtime/streams/session-requests.d.ts +0 -25
  190. package/dist/runtime/streams/session-requests.js +0 -101
  191. package/src/runtime/streams/session-requests.ts +0 -110
@@ -430,8 +430,8 @@ class ServiceTerminalOperationRegistry {
430
430
  constructor(operations = new operation_registry_1.OperationRegistry()) {
431
431
  this.operations = operations;
432
432
  }
433
- register(timeoutMs, timeoutOwner = 'registry') {
434
- return this.operations.register(timeoutMs, timeoutOwner);
433
+ register(timeoutMs, timeoutOwner = 'registry', context) {
434
+ return this.operations.register(timeoutMs, timeoutOwner, context);
435
435
  }
436
436
  reply(id, value) {
437
437
  return this.operations.complete(id, value);
@@ -445,6 +445,9 @@ class ServiceTerminalOperationRegistry {
445
445
  isPending(id) {
446
446
  return this.operations.isPending(id);
447
447
  }
448
+ context(id) {
449
+ return this.operations.context(id);
450
+ }
448
451
  get size() {
449
452
  return this.operations.size;
450
453
  }
@@ -125,9 +125,6 @@ export type ServiceInstanceAuthorityRead = {
125
125
  export type ServiceInstanceAuthorityReserve = {
126
126
  readonly kind: 'reserved';
127
127
  readonly reservation: ServiceInstanceActivationReservation;
128
- } | {
129
- readonly kind: 'ready';
130
- readonly route: ServiceInstanceRouteFence;
131
128
  };
132
129
  /**
133
130
  * Synchronous authority port used by the raw ingress turn. An asynchronous
@@ -161,10 +158,6 @@ export interface ServiceAsyncInstanceActivationAuthority {
161
158
  complete(target: ServiceInstanceActivationTarget, route: ServiceInstanceRouteFence): Promise<ServiceInstanceRouteFence>;
162
159
  abort(target: ServiceInstanceActivationTarget, reservation: ServiceInstanceActivationReservation): Promise<void>;
163
160
  }
164
- export declare class ServiceInstanceActivationRedirectError extends Error {
165
- readonly route: ServiceInstanceRouteFence;
166
- constructor(route: ServiceInstanceRouteFence);
167
- }
168
161
  export interface ServiceUserSpotOperationResult {
169
162
  readonly terminalResult: number;
170
163
  readonly failureCode: number;
@@ -326,7 +319,7 @@ export declare class ServiceStatefulRuntime {
326
319
  private beginInstanceAuthorityOperation;
327
320
  private completeInstanceAuthorityOperation;
328
321
  private completeInstanceApplicationWaitersIfQuiescent;
329
- private relayMissingInstanceActivation;
322
+ private finishMissingInstanceActivationFailure;
330
323
  private requireInstanceActivation;
331
324
  private requireReadyInstanceApplicationRoute;
332
325
  private validateInstanceIngress;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ServiceStatefulRuntime = exports.ServiceInstanceActivationRedirectError = void 0;
3
+ exports.ServiceStatefulRuntime = void 0;
4
4
  exports.statefulMailboxData = statefulMailboxData;
5
5
  const stream_wire_1 = require("@zlink-systems/stream-wire");
6
6
  const node_async_hooks_1 = require("node:async_hooks");
@@ -31,15 +31,6 @@ const SPOT_MOVING = 34;
31
31
  const USER_SPOT_OPERATION_CAPACITY = 65_536;
32
32
  const USER_SPOT_OPERATION_REPLAY_RETENTION_MS = 5 * 60_000;
33
33
  const ACTOR_ROUTE_NOT_FOUND = 1;
34
- class ServiceInstanceActivationRedirectError extends Error {
35
- route;
36
- constructor(route) {
37
- super(`Instance Spot '${route.targetSpotId}' is owned by '${route.targetNodeRid}'.`);
38
- this.route = route;
39
- this.name = 'ServiceInstanceActivationRedirectError';
40
- }
41
- }
42
- exports.ServiceInstanceActivationRedirectError = ServiceInstanceActivationRedirectError;
43
34
  /**
44
35
  * Owns M6B routing, identity fences and terminal operations. Application turns
45
36
  * remain in the common mailbox, where a claimed Spot or Actor owner is serial.
@@ -410,7 +401,7 @@ class ServiceStatefulRuntime {
410
401
  const committed = await authority.commit(target, reservation, spot);
411
402
  if (committed.kind !== 'committed') {
412
403
  this.registry.closeSpot(spot.ref);
413
- throw new ServiceInstanceActivationRedirectError(committed.route);
404
+ throw activationRefused();
414
405
  }
415
406
  return this.finishRecoveredInstanceActivation(envelope, committed.route, expectedCurrentRoute);
416
407
  }
@@ -1116,6 +1107,10 @@ class ServiceStatefulRuntime {
1116
1107
  const metadata = (record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
1117
1108
  decoded = (0, service_stateful_wire_codec_1.decodeStatefulHeader)(record.parts[0], record.parts[metadata ? 2 : 1], metadata ? record.parts[1] : undefined);
1118
1109
  }
1110
+ if (decoded.kind === 'instanceSpot' &&
1111
+ (decoded.operationKind === 'request') !== (record.requestSequence !== undefined)) {
1112
+ return 'protocolError';
1113
+ }
1119
1114
  const hasPayload = [
1120
1115
  'spotSend',
1121
1116
  'spotRequest',
@@ -1164,6 +1159,12 @@ class ServiceStatefulRuntime {
1164
1159
  return await this.handleIngress(record, decoded, payloadFrame, metadataFrame, canonicalActorJoin, canonicalPayload);
1165
1160
  }
1166
1161
  catch (error) {
1162
+ if (decoded.kind === 'instanceSpot' &&
1163
+ decoded.activation === 'missing' &&
1164
+ (error instanceof contracts_1.ZLinkFrameworkException || error instanceof service_stateful_registry_1.ServiceStaleGenerationError)) {
1165
+ this.finishMissingInstanceActivationFailure(record, decoded, error);
1166
+ return 'infrastructure';
1167
+ }
1167
1168
  const correlation = statefulCorrelation(decoded);
1168
1169
  if (correlation !== undefined) {
1169
1170
  const result = failure(error);
@@ -1254,7 +1255,7 @@ class ServiceStatefulRuntime {
1254
1255
  sourceSpotId: record.sourceSpotId,
1255
1256
  targetSpot: record.target.spot,
1256
1257
  ...(record.kind === 'spotRequest'
1257
- ? { reply: this.replyPort(ingress, record.correlation, 'spotRequest') }
1258
+ ? { reply: this.replyPort(ingress, record.correlation) }
1258
1259
  : {})
1259
1260
  });
1260
1261
  }
@@ -1290,7 +1291,7 @@ class ServiceStatefulRuntime {
1290
1291
  originalReplyRouteId: ingress.requestSequence ?? 0n
1291
1292
  },
1292
1293
  ...(record.kind === 'actorRequest'
1293
- ? { reply: this.replyPort(ingress, record.correlation, 'actorRequest') }
1294
+ ? { reply: this.replyPort(ingress, record.correlation) }
1294
1295
  : {})
1295
1296
  });
1296
1297
  }
@@ -1324,10 +1325,10 @@ class ServiceStatefulRuntime {
1324
1325
  return this.handleUserSpotOperation(ingress, record);
1325
1326
  }
1326
1327
  }
1327
- enqueueInstanceSpot(ingress, record, payloadFrame, metadataFrame) {
1328
+ enqueueInstanceSpot(ingress, record, payloadFrame, metadataFrame, localReply) {
1328
1329
  if (record.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
1329
1330
  const ownedIngress = shareIngressOwnership(ingress);
1330
- void this.continueMissingInstanceActivation(ownedIngress, record, payloadFrame, undefined, metadataFrame).finally(() => ownedIngress.applicationJobOwner?.close());
1331
+ void this.continueMissingInstanceActivation(ownedIngress, record, payloadFrame, localReply, metadataFrame).finally(() => ownedIngress.applicationJobOwner?.close());
1331
1332
  return 'infrastructure';
1332
1333
  }
1333
1334
  if (record.activation === 'ready' &&
@@ -1344,7 +1345,7 @@ class ServiceStatefulRuntime {
1344
1345
  objectGeneration: spot.ref.generation
1345
1346
  }
1346
1347
  : this.instanceApplicationTarget(record);
1347
- return this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, spot, undefined, this.instanceApplicationTerminalCompletion(applicationTarget), metadataFrame);
1348
+ return this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, spot, localReply, this.instanceApplicationTerminalCompletion(applicationTarget), metadataFrame);
1348
1349
  }
1349
1350
  needsInstanceApplicationMaterialization(ingress, record) {
1350
1351
  const lifecycle = this.instanceApplicationLifecycle;
@@ -1376,7 +1377,10 @@ class ServiceStatefulRuntime {
1376
1377
  return 'application';
1377
1378
  }
1378
1379
  const directReply = record.operationKind === 'request'
1379
- ? (localReply ?? this.replyPort(ingress, record.replyRouteId, 'instanceSpotRequest'))
1380
+ ? (localReply ??
1381
+ (ingress.requestSequence === undefined
1382
+ ? undefined
1383
+ : this.replyPort(ingress, record.replyRouteId)))
1380
1384
  : undefined;
1381
1385
  const applicationTarget = applicationTargetOverride ??
1382
1386
  (record.activation === 'missing'
@@ -1410,7 +1414,7 @@ class ServiceStatefulRuntime {
1410
1414
  ...(metadataFrame === undefined ? {} : { applicationMetadata: metadataFrame }),
1411
1415
  ...(terminalCompletion === undefined ? {} : { onTerminalCompletion: terminalCompletion }),
1412
1416
  ...(onHandlerTurnStarted === undefined ? {} : { onHandlerTurnStarted }),
1413
- ...(record.operationKind === 'request'
1417
+ ...(record.operationKind === 'request' && directReply !== undefined
1414
1418
  ? {
1415
1419
  reply: onTerminalCompletion === undefined
1416
1420
  ? directReply
@@ -1442,27 +1446,7 @@ class ServiceStatefulRuntime {
1442
1446
  }
1443
1447
  }
1444
1448
  catch (error) {
1445
- let terminalError = error;
1446
- if (error instanceof ServiceInstanceActivationRedirectError && !this.closed) {
1447
- try {
1448
- await this.relayMissingInstanceActivation(record, payloadFrame, error.route, ingress, localReply, metadataFrame);
1449
- return;
1450
- }
1451
- catch (relayError) {
1452
- terminalError = relayError;
1453
- }
1454
- }
1455
- if (record.operationKind !== 'request')
1456
- return;
1457
- const result = failure(terminalError);
1458
- if (localReply !== undefined) {
1459
- localReply(result.terminalResult, result.failureCode);
1460
- }
1461
- else {
1462
- if (record.replyRouteId === undefined)
1463
- return;
1464
- this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
1465
- }
1449
+ this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
1466
1450
  }
1467
1451
  }
1468
1452
  async continueReadyInstanceMaterialization(ingress, record, payloadFrame, metadataFrame) {
@@ -1492,7 +1476,9 @@ class ServiceStatefulRuntime {
1492
1476
  }
1493
1477
  }
1494
1478
  catch (error) {
1495
- if (record.operationKind !== 'request' || record.replyRouteId === undefined)
1479
+ if (record.operationKind !== 'request' ||
1480
+ record.replyRouteId === undefined ||
1481
+ ingress.requestSequence === undefined)
1496
1482
  return;
1497
1483
  const result = failure(error);
1498
1484
  this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
@@ -1545,8 +1531,11 @@ class ServiceStatefulRuntime {
1545
1531
  return { onTerminalCompletion, onHandlerTurnStarted };
1546
1532
  }
1547
1533
  instanceApplicationTerminalCompletion(target) {
1534
+ const lifecycle = this.instanceApplicationLifecycle;
1535
+ if (lifecycle === undefined)
1536
+ return undefined;
1548
1537
  return async () => {
1549
- const authorityReleased = (await this.instanceApplicationLifecycle?.completeTerminal(target)) ?? false;
1538
+ const authorityReleased = await lifecycle.completeTerminal(target);
1550
1539
  if (!authorityReleased)
1551
1540
  return;
1552
1541
  const current = this.instanceIntents.get(target.targetSpotId);
@@ -1601,39 +1590,33 @@ class ServiceStatefulRuntime {
1601
1590
  for (const waiter of waiters)
1602
1591
  waiter();
1603
1592
  }
1604
- async relayMissingInstanceActivation(record, payloadFrame, route, ingress, localReply, metadataFrame) {
1605
- const remainingMs = Number(record.deadlineUnixMs - BigInt(Date.now()));
1606
- if (remainingMs <= 0) {
1607
- throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', route.targetSpotId);
1608
- }
1609
- const redirectedTarget = {
1610
- ...record.target,
1611
- targetNodeRid: route.targetNodeRid,
1612
- targetNodeGeneration: route.targetNodeGeneration,
1613
- targetSpotId: route.targetSpotId
1614
- };
1615
- if (record.operationKind === 'send') {
1616
- const submitted = await this.submitOneWay(route.targetNodeRid, instanceOperationParts([
1617
- (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(redirectedTarget, this.nodeGeneration, this.nodeRid, record.sourceSpotId, 'send', record.operation, record.deadlineUnixMs, undefined, metadataFrame !== undefined),
1618
- payloadFrame
1619
- ], metadataFrame));
1620
- if (submitted !== runtime_values_1.SubmitResult.Ok) {
1621
- throw new Error(`Instance activation redirect was not admitted: ${submitted}.`);
1593
+ finishMissingInstanceActivationFailure(ingress, record, error, localReply) {
1594
+ if (record.operationKind === 'request') {
1595
+ if (record.deadlineUnixMs <= BigInt(Date.now()))
1596
+ return;
1597
+ const result = failure(error);
1598
+ if (localReply !== undefined) {
1599
+ localReply(result.terminalResult, result.failureCode);
1600
+ }
1601
+ else if (ingress.requestSequence !== undefined && record.replyRouteId !== undefined) {
1602
+ this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
1622
1603
  }
1623
1604
  return;
1624
1605
  }
1625
- const pending = this.operations.register(remainingMs);
1626
- this.submitRequest(pending, route.targetNodeRid, instanceOperationParts([
1627
- (0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(redirectedTarget, this.nodeGeneration, this.nodeRid, record.sourceSpotId, 'request', record.operation, record.deadlineUnixMs, pending.id, metadataFrame !== undefined),
1628
- payloadFrame
1629
- ], metadataFrame), remainingMs, 'instanceSpotRequest');
1630
- const result = await pending.promise;
1631
- if (localReply !== undefined) {
1632
- localReply(result.terminalResult, result.failureCode, result.payload);
1633
- return;
1634
- }
1635
- if (record.replyRouteId !== undefined) {
1636
- this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode, result.payload);
1606
+ const reporter = this.dispatchErrors;
1607
+ if (reporter?.captureEnabled() === true) {
1608
+ reporter.report({
1609
+ surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.InstanceSpot,
1610
+ messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
1611
+ reason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget,
1612
+ action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.Drop,
1613
+ meshName: this.dispatchErrorMeshName,
1614
+ sourceRid: record.sourceNodeRid,
1615
+ spotId: record.target.targetSpotId,
1616
+ instanceSpotType: record.target.stableType,
1617
+ activationState: 'activating',
1618
+ error
1619
+ });
1637
1620
  }
1638
1621
  }
1639
1622
  requireInstanceActivation(ingress, record) {
@@ -1731,12 +1714,9 @@ class ServiceStatefulRuntime {
1731
1714
  return local;
1732
1715
  }
1733
1716
  if (current.kind === 'ready') {
1734
- throw new ServiceInstanceActivationRedirectError(current.route);
1717
+ throw activationRefused();
1735
1718
  }
1736
1719
  const reserved = authority.reserve(target, record.operation, record.deadlineUnixMs);
1737
- if (reserved.kind === 'ready') {
1738
- throw new ServiceInstanceActivationRedirectError(reserved.route);
1739
- }
1740
1720
  let activation;
1741
1721
  try {
1742
1722
  activation = this.activateInstanceSpot(target.targetSpotId, target.stableType, reserved.reservation.attempt, reserved.reservation.authorityOwnerGeneration);
@@ -1749,7 +1729,7 @@ class ServiceStatefulRuntime {
1749
1729
  if (committed.kind === 'lost') {
1750
1730
  if (activation.created)
1751
1731
  this.registry.closeSpot(activation.spot.ref);
1752
- throw new ServiceInstanceActivationRedirectError(committed.route);
1732
+ throw activationRefused();
1753
1733
  }
1754
1734
  if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
1755
1735
  if (activation.created)
@@ -1803,12 +1783,12 @@ class ServiceStatefulRuntime {
1803
1783
  }
1804
1784
  };
1805
1785
  }
1786
+ if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
1787
+ throw activationRefused();
1788
+ }
1806
1789
  if (current.kind === 'ready' &&
1807
1790
  current.authority !== undefined &&
1808
1791
  (0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(current.authority.payload) !== undefined) {
1809
- if (current.route.targetNodeRid !== this.nodeRid) {
1810
- throw new ServiceInstanceActivationRedirectError(current.route);
1811
- }
1812
1792
  if (current.route.targetNodeGeneration !== this.nodeGeneration ||
1813
1793
  (local !== undefined &&
1814
1794
  !routeMatchesLocal(current.route, local, this.nodeRid, this.nodeGeneration)) ||
@@ -1821,17 +1801,11 @@ class ServiceStatefulRuntime {
1821
1801
  };
1822
1802
  }
1823
1803
  if (!this.acceptsLocalMissingInstancePlacement()) {
1824
- if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
1825
- throw new ServiceInstanceActivationRedirectError(current.route);
1826
- }
1827
1804
  // A draining or retiring node must not create a successor generation
1828
1805
  // from a stale client route while relocation is publishing its target.
1829
1806
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1830
1807
  }
1831
1808
  if (current.kind === 'ready') {
1832
- if (current.route.targetNodeRid !== this.nodeRid) {
1833
- throw new ServiceInstanceActivationRedirectError(current.route);
1834
- }
1835
1809
  if (current.route.targetNodeGeneration !== this.nodeGeneration) {
1836
1810
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1837
1811
  }
@@ -1867,16 +1841,9 @@ class ServiceStatefulRuntime {
1867
1841
  await lifecycle.discard(target);
1868
1842
  materialized = lifecycle.isMaterialized(target);
1869
1843
  }
1870
- // The authority can still be in a Creating reservation after the local
1871
- // registry exposes the activation. Allow the authority reserve operation
1872
- // to join that attempt instead of treating the local projection as stale.
1873
- // A newer reservation can also become visible after an explicit close
1874
- // releases authority but before local disposal removes the old
1875
- // projection. The close gate serializes that replacement; rejecting it
1876
- // here turns a valid next-generation Instance intent into a stale-route
1877
- // failure.
1878
- // If disposal did not remove an orphaned materialization, keep fencing
1879
- // it instead of admitting work into two application generations.
1844
+ // The local projection does not decide reservation ownership. Preserve
1845
+ // the Creating fence for Reserve, which refuses a competing attempt.
1846
+ // Keep fencing an orphan that disposal did not remove.
1880
1847
  if ((!joinsCreating && current.kind !== 'missing') ||
1881
1848
  (!joinsCreating && materialized !== false && !materializing)) {
1882
1849
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
@@ -1901,9 +1868,6 @@ class ServiceStatefulRuntime {
1901
1868
  finally {
1902
1869
  deadline.close();
1903
1870
  }
1904
- if (reserved.kind === 'ready') {
1905
- throw new ServiceInstanceActivationRedirectError(reserved.route);
1906
- }
1907
1871
  if (this.closed || record.deadlineUnixMs < BigInt(Date.now())) {
1908
1872
  await authority.abort(target, reserved.reservation);
1909
1873
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
@@ -1932,7 +1896,7 @@ class ServiceStatefulRuntime {
1932
1896
  if (activation.created)
1933
1897
  this.registry.closeSpot(activation.spot.ref);
1934
1898
  await this.instanceApplicationLifecycle?.discard(target);
1935
- throw new ServiceInstanceActivationRedirectError(committed.route);
1899
+ throw activationRefused();
1936
1900
  }
1937
1901
  if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
1938
1902
  if (activation.created)
@@ -1960,7 +1924,7 @@ class ServiceStatefulRuntime {
1960
1924
  const confirmed = await authority.read(target);
1961
1925
  if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
1962
1926
  if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
1963
- throw new ServiceInstanceActivationRedirectError(confirmed.route);
1927
+ throw activationRefused();
1964
1928
  }
1965
1929
  throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
1966
1930
  }
@@ -2056,7 +2020,7 @@ class ServiceStatefulRuntime {
2056
2020
  kindData: control,
2057
2021
  ...(canonicalApplicationPayload === undefined ? {} : { canonicalApplicationPayload }),
2058
2022
  reply: (terminalResult, failureCode, replyPayload, tail) => {
2059
- return this.replyPort(ingress, record.correlation, 'actorJoin')(terminalResult, failureCode, replyPayload, tail);
2023
+ return this.replyPort(ingress, record.correlation)(terminalResult, failureCode, replyPayload, tail);
2060
2024
  }
2061
2025
  });
2062
2026
  }
@@ -2538,20 +2502,20 @@ class ServiceStatefulRuntime {
2538
2502
  }
2539
2503
  return accepted;
2540
2504
  }
2541
- replyPort(ingress, correlation, operationKind) {
2505
+ replyPort(ingress, correlation) {
2542
2506
  if (correlation === undefined)
2543
2507
  throw new service_wire_m6a_codec_1.ServiceWireProtocolError('Request correlation is missing.');
2544
2508
  return (terminalResult, failureCode, payload, tail) => {
2545
2509
  this.replyWire(ingress, correlation, terminalResult, failureCode, payload, tail);
2546
- void operationKind;
2547
2510
  return true;
2548
2511
  };
2549
2512
  }
2550
2513
  replyWire(ingress, correlation, terminalResult, failureCode, payload, tail) {
2551
- this.raw.replyService(ingress, [
2514
+ const parts = [
2552
2515
  (0, service_stateful_wire_codec_1.encodeStatefulReply)(correlation, terminalResult, failureCode, tail),
2553
2516
  ...(payload === undefined ? [] : [(0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)])
2554
- ]);
2517
+ ];
2518
+ this.raw.replyService(ingress, parts);
2555
2519
  }
2556
2520
  handleUserSpotOperation(ingress, record) {
2557
2521
  if (ingress.requestSequence === undefined ||
@@ -3094,24 +3058,11 @@ class ServiceStatefulRuntime {
3094
3058
  return;
3095
3059
  }
3096
3060
  if (decoded.kind === 'instanceSpot') {
3097
- if (decoded.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
3098
- void this.continueMissingInstanceActivation(ingress, decoded, payloadFrame, localReply);
3099
- return;
3100
- }
3101
- try {
3102
- const activation = this.requireInstanceActivation(ingress, decoded);
3103
- this.enqueueApplicationFrame(ingress, `spot:${activation.ref.spotId}`, payloadFrame, {
3104
- receiveKind: service_runtime_contracts_1.ReceiveKind.InstanceSpotActivation,
3105
- operationKind: service_runtime_contracts_1.OperationKind.InstanceSpotRequest,
3106
- correlation: pending.id,
3107
- ...(decoded.sourceSpotId === undefined ? {} : { sourceSpotId: decoded.sourceSpotId }),
3108
- targetSpot: activation.ref,
3109
- reply: localReply
3110
- });
3111
- }
3112
- catch (error) {
3113
- this.operations.reply(pending.id, failure(error));
3114
- }
3061
+ const hasMetadata = (ingress.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
3062
+ const metadataFrame = hasMetadata
3063
+ ? (0, service_metadata_codec_1.validateServiceMetadataFrame)(ingress.parts[1])
3064
+ : undefined;
3065
+ this.enqueueInstanceSpot(ingress, decoded, ingress.parts[hasMetadata ? 2 : 1], metadataFrame, localReply);
3115
3066
  return;
3116
3067
  }
3117
3068
  this.operations.reply(pending.id, {
@@ -3292,9 +3243,7 @@ class ServiceStatefulRuntime {
3292
3243
  ...(wire.correlation === undefined ? {} : { correlation: wire.correlation }),
3293
3244
  sourceSpotId: wire.sourceSpotId,
3294
3245
  targetSpot: wire.target.spot,
3295
- ...(wire.kind === 'spotRequest'
3296
- ? { reply: this.replyPort(ingress, wire.correlation, 'spotRequest') }
3297
- : {})
3246
+ ...(wire.kind === 'spotRequest' ? { reply: this.replyPort(ingress, wire.correlation) } : {})
3298
3247
  });
3299
3248
  }
3300
3249
  async drainSpotMessageFollow(state) {
@@ -3671,6 +3620,9 @@ function actorLocation(actor) {
3671
3620
  membershipEpoch: actor.membershipEpoch
3672
3621
  };
3673
3622
  }
3623
+ function activationRefused() {
3624
+ return new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.Unavailable, 'Instance cold activation authority was refused.');
3625
+ }
3674
3626
  function failure(error) {
3675
3627
  if (error instanceof service_stateful_registry_1.ServiceStaleGenerationError) {
3676
3628
  return (0, framework_errors_internal_1.internalFrameworkWireReply)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorLocationStale);
@@ -105,8 +105,6 @@ export type ServiceWireRelocationObject = {
105
105
  readonly spotId: string;
106
106
  readonly objectGeneration: bigint;
107
107
  };
108
- /** Shared CRC-32C wire bounds for the direct relocation payload transfer. */
109
- export declare const RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = 274877906944n;
110
108
  export declare const RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = 4096;
111
109
  export { RELOCATION_STATE_CHUNK_DATA_MAX_BYTES } from '../../contracts/Configuration/InternalDefaults';
112
110
  interface ServiceWireRelocationBase {
@@ -576,15 +574,6 @@ export declare function decodeMaintenanceReplyRelayAck(frame: Uint8Array): Servi
576
574
  export declare function decodeStatefulReply(frame: Uint8Array, expectedCorrelation: bigint, operationKind: 'spotRequest' | 'actorRequest' | 'actorLookup' | 'actorDestroy' | 'actorJoin' | 'streamBind' | 'streamUnbind' | 'instanceSpotRequest' | 'userSpotCreate' | 'userSpotClose' | 'actorCreate', hasPayload?: boolean): ServiceStatefulReply;
577
575
  export declare function sessionBindingFromWire(actor: ServiceActorRef, sessionRid: string, sessionOwnerNodeRid: string, generation: bigint, membershipEpoch: bigint, sessionOwnerNodeGeneration?: bigint, sessionOwnerId?: string, sessionOwnerLeaseGeneration?: bigint): ServiceSessionBinding;
578
576
  export declare function decodeServiceWireFrozenRecord(bytes: Uint8Array): ServiceWireFrozenRecord;
579
- /**
580
- * Consumes exactly one canonical frozen record at `offset`. Relocation
581
- * saved-work is a vector of back-to-back records, so its boundary is the
582
- * record grammar itself rather than an enclosing length field.
583
- */
584
- export declare function decodeServiceWireFrozenRecordPrefix(bytes: Uint8Array, offset?: number): {
585
- readonly record: ServiceWireFrozenRecord;
586
- readonly length: number;
587
- };
588
577
  export declare function encodeServiceWireFrozenRecord(record: ServiceWireFrozenRecord): Buffer;
589
578
  export declare function encodeServiceWireFrozenActorApplicationRecord(input: {
590
579
  readonly source: ServiceWireRequestSourceFence;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.M6bServiceWireFlag = exports.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES = exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = exports.RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = exports.M6bServiceWireCommand = void 0;
3
+ exports.M6bServiceWireFlag = exports.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES = exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = exports.M6bServiceWireCommand = void 0;
4
4
  exports.serviceSessionRelocationIdentityKey = serviceSessionRelocationIdentityKey;
5
5
  exports.encodeSpotHeader = encodeSpotHeader;
6
6
  exports.encodeActorHeader = encodeActorHeader;
@@ -33,7 +33,6 @@ exports.decodeMaintenanceReplyRelayAck = decodeMaintenanceReplyRelayAck;
33
33
  exports.decodeStatefulReply = decodeStatefulReply;
34
34
  exports.sessionBindingFromWire = sessionBindingFromWire;
35
35
  exports.decodeServiceWireFrozenRecord = decodeServiceWireFrozenRecord;
36
- exports.decodeServiceWireFrozenRecordPrefix = decodeServiceWireFrozenRecordPrefix;
37
36
  exports.encodeServiceWireFrozenRecord = encodeServiceWireFrozenRecord;
38
37
  exports.encodeServiceWireFrozenActorApplicationRecord = encodeServiceWireFrozenActorApplicationRecord;
39
38
  const stream_wire_1 = require("@zlink-systems/stream-wire");
@@ -55,8 +54,6 @@ const MAGIC_0 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[0];
55
54
  const MAGIC_1 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[1];
56
55
  const MAJOR = service_wire_constants_generated_1.SERVICE_WIRE_MAJOR;
57
56
  exports.M6bServiceWireCommand = service_wire_constants_generated_1.ServiceWireCommand;
58
- /** Shared CRC-32C wire bounds for the direct relocation payload transfer. */
59
- exports.RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = 274877906944n;
60
57
  exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = 4096;
61
58
  var InternalDefaults_2 = require("../../contracts/Configuration/InternalDefaults");
62
59
  Object.defineProperty(exports, "RELOCATION_STATE_CHUNK_DATA_MAX_BYTES", { enumerable: true, get: function () { return InternalDefaults_2.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES; } });
@@ -1623,25 +1620,6 @@ function decodeServiceWireFrozenRecord(bytes) {
1623
1620
  reader.end('frozen record');
1624
1621
  return { ...record, canonicalBytes: Buffer.from(bytes) };
1625
1622
  }
1626
- /**
1627
- * Consumes exactly one canonical frozen record at `offset`. Relocation
1628
- * saved-work is a vector of back-to-back records, so its boundary is the
1629
- * record grammar itself rather than an enclosing length field.
1630
- */
1631
- function decodeServiceWireFrozenRecordPrefix(bytes, offset = 0) {
1632
- if (!Number.isSafeInteger(offset) || offset < 0 || offset >= bytes.byteLength) {
1633
- fail('Frozen record offset is outside the encoded bytes.');
1634
- }
1635
- const reader = new FrozenReader(bytes.subarray(offset));
1636
- const record = decodeServiceWireFrozenRecordFrom(reader);
1637
- return {
1638
- record: {
1639
- ...record,
1640
- canonicalBytes: Buffer.from(bytes.subarray(offset, offset + reader.position))
1641
- },
1642
- length: reader.position
1643
- };
1644
- }
1645
1623
  function decodeServiceWireFrozenRecordFrom(reader) {
1646
1624
  const recordKind = reader.u8('recordKind');
1647
1625
  if (recordKind < 1 || recordKind > 14)
@@ -58,7 +58,12 @@ export declare class ServiceTopologyRegistry {
58
58
  selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
59
59
  selectPlacement(isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
60
60
  selectObjectPlacement(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceNodeDescriptor | undefined;
61
+ selectObjectPlacementWithStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): {
62
+ readonly status: ServiceObjectPlacementStatus;
63
+ readonly descriptor?: ServiceNodeDescriptor;
64
+ };
61
65
  objectPlacementStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceObjectPlacementStatus;
66
+ private objectPlacementCandidates;
62
67
  instanceSpotPlacementTypes(): readonly string[];
63
68
  private selectWeightedCycle;
64
69
  private rebuildSelections;
@@ -181,21 +181,22 @@ class ServiceTopologyRegistry {
181
181
  .map(clonePeer), (peer) => peer.descriptor.placementWeight, (peer) => peer.descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.descriptor.nodeRoutingId, right.descriptor.nodeRoutingId), isReady);
182
182
  }
183
183
  selectObjectPlacement(stableType, isReady = () => true) {
184
+ return this.selectObjectPlacementWithStatus(stableType, isReady).descriptor;
185
+ }
186
+ selectObjectPlacementWithStatus(stableType, isReady = () => true) {
184
187
  requireText(stableType, 'stableType');
185
- return this.selectWeightedCycle(`object:${stableType}`, () => {
186
- const capability = `object-type:${stableType}`;
187
- return [this.local, ...this.peersByRid.values()]
188
- .map((value) => ('descriptor' in value ? value.descriptor : value))
189
- .filter((descriptor) => descriptor.state === 'serving' &&
190
- descriptor.objectRole === 'server' &&
191
- descriptor.placementWeight > 0 &&
192
- descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
193
- descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit &&
194
- descriptor.protocolCapabilities.includes(capability))
195
- .map(cloneDescriptor);
196
- }, (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
188
+ const candidates = this.objectPlacementCandidates(stableType, isReady);
189
+ if (candidates.status !== 'available')
190
+ return { status: candidates.status };
191
+ const descriptor = this.selectWeightedCycle(`object:${stableType}`, () => this.objectPlacementCandidates(stableType).eligible.map(cloneDescriptor), (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
192
+ return descriptor === undefined
193
+ ? { status: 'unavailable' }
194
+ : { status: 'available', descriptor };
197
195
  }
198
196
  objectPlacementStatus(stableType, isReady = () => true) {
197
+ return this.objectPlacementCandidates(stableType, isReady).status;
198
+ }
199
+ objectPlacementCandidates(stableType, isReady = () => true) {
199
200
  requireText(stableType, 'stableType');
200
201
  const capability = `object-type:${stableType}`;
201
202
  const supported = [
@@ -205,15 +206,13 @@ class ServiceTopologyRegistry {
205
206
  descriptor.objectRole === 'server' &&
206
207
  descriptor.protocolCapabilities.includes(capability));
207
208
  if (supported.length === 0)
208
- return 'unsupported';
209
+ return { status: 'unsupported', eligible: [] };
209
210
  const withCapacity = supported.filter((descriptor) => descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
210
211
  descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit);
211
212
  if (withCapacity.length === 0)
212
- return 'capacity';
213
+ return { status: 'capacity', eligible: [] };
213
214
  const weighted = withCapacity.filter((descriptor) => descriptor.placementWeight > 0);
214
- if (weighted.length === 0)
215
- return 'unsupported';
216
- return weighted.some(isReady) ? 'available' : 'unavailable';
215
+ return { status: weighted.some(isReady) ? 'available' : 'unavailable', eligible: weighted };
217
216
  }
218
217
  instanceSpotPlacementTypes() {
219
218
  const prefix = 'instance-spot-type:';
@@ -43,7 +43,7 @@ export declare class ZLinkActorPacketRelay {
43
43
  readonly deferredResponse?: boolean;
44
44
  readonly actorPacketTarget?: unknown;
45
45
  }>;
46
- relayActorPacket(actor: ZLinkSessionActor, frameHeader: ZLinkStreamFrameHeader, payload: Message, signal?: AbortSignal): Promise<boolean>;
46
+ relayActorPacket(actor: ZLinkSessionActor, frameHeader: ZLinkStreamFrameHeader, payload: Message): Promise<boolean>;
47
47
  confirmRemoteSessionBinding(actor: ActorRef, sessionNodeRid: RoutingId, sessionRid: RoutingId, signal?: AbortSignal, options?: ZLinkSessionBindingConfirmationOptions): Promise<void>;
48
48
  /**
49
49
  * Session–Actor binding §5·§6 (.NET ZLinkSessionActorCoordinator rule): a
@@ -51,7 +51,7 @@ export declare class ZLinkActorPacketRelay {
51
51
  * same install a remote command 38 uses, so relocation seals and carries it.
52
52
  */
53
53
  private installSameNodeSessionBinding;
54
- relayRemoteActorPacket(actor: ZLinkSessionActor, frameHeader: ZLinkStreamFrameHeader, payload: Message, signal?: AbortSignal): Promise<boolean>;
54
+ relayRemoteActorPacket(actor: ZLinkSessionActor, frameHeader: ZLinkStreamFrameHeader, payload: Message): Promise<boolean>;
55
55
  notifyRemoteActorDisconnected(actorId: string, remoteTarget: ZLinkRemoteBoundSessionTarget | ZLinkRemoteActorPacketTarget, signal?: AbortSignal, bindingActorRef?: ActorRef): Promise<void>;
56
56
  private requireCurrentRemoteBinding;
57
57
  private requestRemoteTarget;