@zlink-systems/framework 0.29.0 → 0.30.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 (168) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +1 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +0 -5
  4. package/dist/contracts/Configuration/RegistrationBuilders.js +6 -4
  5. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -6
  6. package/dist/contracts/Configuration/RegistrationValidators.js +4 -4
  7. package/dist/contracts/Configuration/index.d.ts +1 -1
  8. package/dist/internal.d.ts +1 -0
  9. package/dist/internal.js +3 -1
  10. package/dist/runtime/actors/actor-handoff.d.ts +3 -2
  11. package/dist/runtime/actors/actor-handoff.js +9 -4
  12. package/dist/runtime/actors/actor-runtime-contracts.d.ts +1 -0
  13. package/dist/runtime/actors/index.d.ts +2 -0
  14. package/dist/runtime/actors/index.js +21 -6
  15. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +3 -1
  16. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  17. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  18. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  19. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  20. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  21. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  22. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  23. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  25. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  26. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  27. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  28. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  29. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  30. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  31. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  32. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  33. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  34. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  35. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  36. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  37. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  38. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  39. package/dist/runtime/diagnostics/message-flow.js +3 -3
  40. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  41. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  42. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  43. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  44. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  45. package/dist/runtime/foundation/service-mailbox.js +2 -1
  46. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  47. package/dist/runtime/foundation/service-stateful-runtime.js +60 -41
  48. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  49. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  50. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  51. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  52. package/dist/runtime/handlers/handler-instance-scope.js +1 -1
  53. package/dist/runtime/host/actor-packet-relay.js +7 -7
  54. package/dist/runtime/host/actor-placement-coordinator.d.ts +2 -1
  55. package/dist/runtime/host/actor-placement-coordinator.js +2 -1
  56. package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -1
  57. package/dist/runtime/host/actor-runtime-options-factory.js +2 -1
  58. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  59. package/dist/runtime/host/application-job-queue.js +66 -10
  60. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  61. package/dist/runtime/host/index.d.ts +1 -1
  62. package/dist/runtime/host/index.js +18 -13
  63. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  64. package/dist/runtime/host/instance-activation-authority.js +89 -25
  65. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  66. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  67. package/dist/runtime/host/spot-address-transport.js +25 -12
  68. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  69. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  70. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  71. package/dist/runtime/locations/domain-store-contract.d.ts +3 -2
  72. package/dist/runtime/locations/in-memory-authority-store.d.ts +5 -2
  73. package/dist/runtime/locations/in-memory-authority-store.js +107 -14
  74. package/dist/runtime/locations/in-memory-location-store.d.ts +3 -3
  75. package/dist/runtime/locations/in-memory-location-store.js +31 -13
  76. package/dist/runtime/locations/internal-location-contracts.d.ts +8 -0
  77. package/dist/runtime/locations/location-store-repository.d.ts +4 -2
  78. package/dist/runtime/locations/location-store-repository.js +209 -33
  79. package/dist/runtime/locations/resolvers.d.ts +2 -1
  80. package/dist/runtime/locations/resolvers.js +10 -1
  81. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  82. package/dist/runtime/messaging/submission-result.js +2 -2
  83. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  84. package/dist/runtime/spots/index.d.ts +4 -3
  85. package/dist/runtime/spots/index.js +14 -14
  86. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  87. package/dist/runtime/spots/spot-activation.js +4 -4
  88. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  89. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  90. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  91. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  92. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  93. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  94. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -0
  95. package/dist/runtime/spots/spot-entry-activation.js +6 -1
  96. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +3 -1
  97. package/dist/runtime/spots/spot-node-runtime-manager.js +10 -11
  98. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  99. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  100. package/dist/runtime/spots/spot-serial-executor.d.ts +1 -0
  101. package/dist/runtime/spots/spot-serial-executor.js +10 -1
  102. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  103. package/package.json +3 -3
  104. package/src/contracts/Configuration/Builders.ts +1 -2
  105. package/src/contracts/Configuration/Configs.ts +0 -6
  106. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  107. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  108. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  109. package/src/contracts/Configuration/index.ts +1 -6
  110. package/src/internal.ts +1 -0
  111. package/src/runtime/actors/actor-handoff.ts +13 -4
  112. package/src/runtime/actors/actor-runtime-contracts.ts +4 -0
  113. package/src/runtime/actors/index.ts +77 -42
  114. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  115. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  116. package/src/runtime/application-jobs/contracts.ts +14 -1
  117. package/src/runtime/backend/contracts/index.ts +10 -18
  118. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  119. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  120. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  121. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  122. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  123. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  124. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  125. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  126. package/src/runtime/backend/raw-binding-port.ts +12 -2
  127. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  128. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  129. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  130. package/src/runtime/diagnostics/message-flow.ts +1 -1
  131. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  132. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  133. package/src/runtime/foundation/service-mailbox.ts +7 -1
  134. package/src/runtime/foundation/service-stateful-runtime.ts +109 -77
  135. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  136. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  137. package/src/runtime/handlers/handler-instance-scope.ts +2 -1
  138. package/src/runtime/host/actor-packet-relay.ts +7 -0
  139. package/src/runtime/host/actor-placement-coordinator.ts +8 -2
  140. package/src/runtime/host/actor-runtime-options-factory.ts +7 -0
  141. package/src/runtime/host/application-job-queue.ts +77 -10
  142. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  143. package/src/runtime/host/index.ts +30 -7
  144. package/src/runtime/host/instance-activation-authority.ts +132 -27
  145. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  146. package/src/runtime/host/spot-address-transport.ts +45 -22
  147. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  148. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  149. package/src/runtime/locations/domain-store-contract.ts +6 -2
  150. package/src/runtime/locations/in-memory-authority-store.ts +153 -12
  151. package/src/runtime/locations/in-memory-location-store.ts +39 -13
  152. package/src/runtime/locations/internal-location-contracts.ts +10 -0
  153. package/src/runtime/locations/location-store-repository.ts +295 -36
  154. package/src/runtime/locations/resolvers.ts +25 -2
  155. package/src/runtime/messaging/submission-result.ts +2 -2
  156. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  157. package/src/runtime/spots/index.ts +16 -0
  158. package/src/runtime/spots/spot-activation.ts +5 -0
  159. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  160. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  161. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  162. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  163. package/src/runtime/spots/spot-entry-activation.ts +7 -0
  164. package/src/runtime/spots/spot-node-runtime-manager.ts +14 -12
  165. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  166. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  167. package/src/runtime/spots/spot-serial-executor.ts +10 -4
  168. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -1,3 +1,5 @@
1
+ import type { ApplicationJobOrigin } from '../application-jobs/contracts';
2
+ import { currentApplicationJobOrigin } from '../application-jobs/application-job-queue-scope';
1
3
  import { listAllMeshNodeDescriptors } from '../locations/location-store-pages';
2
4
  import { ZlinkStreamContentType } from '@zlink-systems/stream-wire';
3
5
  import { randomBytes, randomUUID } from 'node:crypto';
@@ -349,7 +351,12 @@ export class ZLinkFrameworkRuntimeHost
349
351
  } catch {
350
352
  return false;
351
353
  }
352
- }
354
+ },
355
+ (error) =>
356
+ (this.errorSink ?? this.preStartErrorSink).reportRuntimeTaskException(
357
+ 'logger-provider',
358
+ error
359
+ )
353
360
  );
354
361
  this.capacityStatus = new HostCapacityStatusProjection(
355
362
  options.registration.coreHwm,
@@ -721,13 +728,13 @@ export class ZLinkFrameworkRuntimeHost
721
728
  sessionRelocationWire: () => this.serviceRelocation,
722
729
  clearRemoteActorPacketTarget: (actorId) =>
723
730
  this.boundSessionRelay.clearRemoteActorPacketTarget(actorId),
724
- prepareApplicationJob: async (preparationSignal) => {
731
+ prepareApplicationJob: async (preparationSignal, origin) => {
725
732
  const runtimeSignal = this.executionState?.abortController.signal;
726
733
  const signal =
727
734
  runtimeSignal === undefined
728
735
  ? preparationSignal
729
736
  : AbortSignal.any([runtimeSignal, preparationSignal]);
730
- const permit = await this.applicationJobQueue.acquire(signal);
737
+ const permit = await this.applicationJobQueue.acquire(signal, origin);
731
738
  let state: 'ready' | 'running' | 'closed' = 'ready';
732
739
  let closedReason: unknown;
733
740
  let abort: (() => void) | undefined;
@@ -1722,6 +1729,10 @@ export class ZLinkFrameworkRuntimeHost
1722
1729
  activationNode.registerAsyncInstanceActivationAuthority?.(
1723
1730
  new ZLinkInstanceActivationAuthority({
1724
1731
  store: locationStore,
1732
+ relocationPolicy: (stableType) =>
1733
+ this.options.registration.spotNodes.get(meshName)?.instanceSpotFactoryRegistrations?.[
1734
+ stableType
1735
+ ]?.relocation.kind,
1725
1736
  relocationStore: this.options.registration.locations.relocationStoreInstance,
1726
1737
  meshName,
1727
1738
  owner: () => this.locationOwner.currentRuntime?.currentOwnerToken,
@@ -2016,6 +2027,7 @@ export class ZLinkFrameworkRuntimeHost
2016
2027
  | 'actorRefResolver'
2017
2028
  | 'actorCreatedNotifier'
2018
2029
  | 'actorDestroyedCleanup'
2030
+ | 'terminateActorActivation'
2019
2031
  | 'locationLifecycle'
2020
2032
  | 'boundSessionFactory'
2021
2033
  | 'shutdownSignal'
@@ -2139,6 +2151,9 @@ export class ZLinkFrameworkRuntimeHost
2139
2151
  }
2140
2152
  this.actorPlacement ??= new ZLinkActorPlacementCoordinator({
2141
2153
  store: locationStore,
2154
+ actorRelocationPolicy: (meshName, stableType) =>
2155
+ this.options.registration.spotNodes.get(meshName)?.actorFactoryRegistrations?.[stableType]
2156
+ ?.relocation.kind,
2142
2157
  remoteCreate: (meshName, targetNodeRid, request, timeoutMs) => {
2143
2158
  const node = this.spotNodeRuntime?.meshNode(meshName);
2144
2159
  if (node === undefined) {
@@ -2194,14 +2209,12 @@ export class ZLinkFrameworkRuntimeHost
2194
2209
  const capability = descriptor.objectCapabilities.find(
2195
2210
  (candidate) => candidate.objectKind === 'actor' && candidate.stableType === stableType
2196
2211
  );
2197
- const actors = descriptor.populationCapacity.actors;
2198
2212
  return (
2199
2213
  descriptor.state === ZLinkFrameworkRuntimeState.Serving &&
2200
2214
  descriptor.objectRole === 'server' &&
2201
2215
  descriptor.placementWeight > 0 &&
2202
2216
  descriptor.entrySpotId !== undefined &&
2203
2217
  excludedNodeRids?.has(String(descriptor.rid)) !== true &&
2204
- (actors.limit === 0 || actors.active + actors.reserved < actors.limit) &&
2205
2218
  capability !== undefined
2206
2219
  );
2207
2220
  }
@@ -2233,6 +2246,9 @@ export class ZLinkFrameworkRuntimeHost
2233
2246
  });
2234
2247
  const coordinator = (this.userSpotCoordinator = new ZLinkUserSpotCreationCoordinator({
2235
2248
  store: locationStore,
2249
+ relocationPolicy: (meshName, stableType) =>
2250
+ this.options.registration.spotNodes.get(meshName)?.spotFactoryRegistrations?.[stableType]
2251
+ ?.relocation.kind,
2236
2252
  publishReadyRoute: (meshName, route) => {
2237
2253
  this.cachedLocationSpotRouteResolver?.invalidate?.(route.spot.spotId);
2238
2254
  this.spotNodeRuntime?.meshNode(meshName)?.rememberSpotRoute?.(route);
@@ -2592,6 +2608,8 @@ export class ZLinkFrameworkRuntimeHost
2592
2608
 
2593
2609
  private actorRuntimeOptionsFactory(): ZLinkActorRuntimeOptionsFactory {
2594
2610
  return new ZLinkActorRuntimeOptionsFactory({
2611
+ terminateActorActivation: (actor, terminal) =>
2612
+ this.spotNodeRuntime?.terminateActorActivation(actor, terminal) ?? terminal(),
2595
2613
  registration: this.options.registration,
2596
2614
  messageFlow: () => this.createDispatchErrorReporter(this.runtimeOrPreStartErrorSink).flow,
2597
2615
  routeTransport: this.routeTransport,
@@ -2753,6 +2771,7 @@ export class ZLinkFrameworkRuntimeHost
2753
2771
  },
2754
2772
  meshRecordDispatcher: (meshName: string, owner: ReadyRecord, record: ReceiveRecord) =>
2755
2773
  this.dispatchMeshRecord(
2774
+ currentApplicationJobOrigin,
2756
2775
  meshName,
2757
2776
  owner,
2758
2777
  record,
@@ -2763,6 +2782,7 @@ export class ZLinkFrameworkRuntimeHost
2763
2782
  }
2764
2783
 
2765
2784
  private async dispatchMeshRecord(
2785
+ applicationJobOrigin: () => ApplicationJobOrigin,
2766
2786
  meshName: string,
2767
2787
  owner: ReadyRecord,
2768
2788
  record: ReceiveRecord,
@@ -2964,7 +2984,7 @@ export class ZLinkFrameworkRuntimeHost
2964
2984
  );
2965
2985
  }
2966
2986
  return this.admission.run(meshName, 'RouteMesh Actor dispatch', () =>
2967
- this.spotManager!.dispatchMeshActor(meshName, owner, record)
2987
+ this.spotManager!.dispatchMeshActor(applicationJobOrigin(), meshName, owner, record)
2968
2988
  );
2969
2989
  case ReceiveKind.SendReady:
2970
2990
  return Promise.resolve();
@@ -3008,7 +3028,10 @@ export class ZLinkFrameworkRuntimeHost
3008
3028
  try {
3009
3029
  state.taskRunner.runDetached('mesh-node-local-route-dispatch', async () => {
3010
3030
  try {
3011
- const permit = await this.applicationJobQueue.acquire(state.abortController.signal);
3031
+ const permit = await this.applicationJobQueue.acquire(
3032
+ state.abortController.signal,
3033
+ 'local'
3034
+ );
3012
3035
  permit.markApplicationQueued();
3013
3036
  await runWithApplicationJobPermit(permit, () =>
3014
3037
  channelRuntime.dispatchLocalMeshRoute(meshName, sourceNodeRid, ownedParts)
@@ -41,6 +41,7 @@ import { putNewRelocationBlob, relocationBlobReference } from '../locations/relo
41
41
 
42
42
  import {
43
43
  encodeInstanceActivationRecoveryEnvelope,
44
+ decodeInstanceActivationRecoveryEnvelope,
44
45
  type ServiceInstanceActivationRecoveryEnvelope
45
46
  } from '../foundation/service-instance-activation-recovery-codec';
46
47
 
@@ -57,6 +58,9 @@ interface PendingReservation {
57
58
  }
58
59
 
59
60
  export interface ZLinkInstanceActivationAuthorityOptions {
61
+ readonly relocationPolicy?: (
62
+ stableType: string
63
+ ) => import('../locations/internal-location-contracts').ZLinkObjectReserveRequest['actorRelocationPolicy'];
60
64
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
61
65
  readonly relocationStore?: ZLinkRelocationStore;
62
66
  readonly meshName: string;
@@ -73,9 +77,101 @@ export class ZLinkInstanceActivationAuthority implements ServiceAsyncInstanceAct
73
77
 
74
78
  constructor(private readonly options: ZLinkInstanceActivationAuthorityOptions) {}
75
79
 
76
- async read(target: ServiceInstanceActivationTarget): Promise<ServiceInstanceAuthorityRead> {
77
- const current = await this.options.store.readAuthority(authorityKey(target.targetSpotId));
78
- return current.kind === 'snapshot' ? readyRead(current, target) : { kind: 'missing' };
80
+ async read(
81
+ target: ServiceInstanceActivationTarget,
82
+ instanceIntent?: true,
83
+ activation?: ServiceInstanceActivationRecoveryEnvelope,
84
+ acceptsLocal?: (route: ServiceInstanceRouteFence) => boolean
85
+ ): Promise<ServiceInstanceAuthorityRead> {
86
+ const key = authorityKey(target.targetSpotId);
87
+ const current = await this.options.store.readAuthority(key);
88
+ if (current.kind !== 'snapshot') return { kind: 'missing' };
89
+ const ready = decodeServiceInstanceAuthorityPayload(current.payload);
90
+ const resolved = readyRead(current, target, ready);
91
+ if (
92
+ ready?.state === 'ready' &&
93
+ resolved.kind === 'ready' &&
94
+ acceptsLocal?.(resolved.route) === true
95
+ ) {
96
+ return resolved;
97
+ }
98
+ if (
99
+ activation !== undefined &&
100
+ current.allocation.objectKind === 'instance_spot' &&
101
+ routingIdsEqual(current.allocation.descriptor.rid, target.targetNodeRid) &&
102
+ current.allocation.descriptorLifecycleGeneration === target.targetNodeGeneration
103
+ ) {
104
+ const reference =
105
+ current.pendingCreation?.requestContentReference ?? ready?.activationRecovery?.reference;
106
+ if (reference !== undefined) {
107
+ const root = await this.options.relocationStore?.read(relocationBlobReference(reference));
108
+ if (root?.kind !== 'found') {
109
+ throw new ZLinkFrameworkException(
110
+ ZLinkFrameworkErrorKind.DataLost,
111
+ 'Stored Instance activation is missing.'
112
+ );
113
+ }
114
+ let stored: ServiceInstanceActivationRecoveryEnvelope;
115
+ try {
116
+ stored = decodeInstanceActivationRecoveryEnvelope(root.bytes);
117
+ } catch (error) {
118
+ if (!(error instanceof TypeError || error instanceof RangeError)) throw error;
119
+ throw new ZLinkFrameworkException(
120
+ ZLinkFrameworkErrorKind.ProtocolError,
121
+ 'Stored Instance activation is invalid.'
122
+ );
123
+ }
124
+ if (
125
+ stored.operation.high === activation.operation.high &&
126
+ stored.operation.low === activation.operation.low &&
127
+ (stored.targetMeshName !== activation.targetMeshName ||
128
+ stored.target.stableType !== target.stableType ||
129
+ stored.target.descriptorVersion !== target.descriptorVersion ||
130
+ stored.deadlineUnixMs !== activation.deadlineUnixMs ||
131
+ (stored.metadataFrame === undefined) !== (activation.metadataFrame === undefined) ||
132
+ (stored.metadataFrame !== undefined &&
133
+ activation.metadataFrame !== undefined &&
134
+ Buffer.compare(stored.metadataFrame, activation.metadataFrame) !== 0))
135
+ ) {
136
+ throw new ZLinkFrameworkException(
137
+ ZLinkFrameworkErrorKind.ProtocolError,
138
+ 'Instance route does not match the stored activation.'
139
+ );
140
+ }
141
+ }
142
+ }
143
+
144
+ if (
145
+ instanceIntent === true &&
146
+ current.allocation.state === 'active' &&
147
+ ready?.state === 'ready'
148
+ ) {
149
+ if (
150
+ current.allocation.objectKind !== 'instance_spot' ||
151
+ current.allocation.stableType !== target.stableType
152
+ ) {
153
+ throw createInternalFrameworkException(
154
+ ZLinkFrameworkInternalErrorKind.SpotTypeMismatch,
155
+ 'Instance activation type does not match the current authority.'
156
+ );
157
+ }
158
+ const released = await this.options.store.releaseEndedReservation?.(
159
+ key,
160
+ current.storeVersion.value,
161
+ undefined,
162
+ {
163
+ key: { kind: 'instance_spot', globalId: target.targetSpotId },
164
+ intent: { stableType: target.stableType },
165
+ actorRelocationPolicy: this.options.relocationPolicy?.(target.stableType)
166
+ }
167
+ );
168
+ if (released === true) return { kind: 'missing' };
169
+ const refreshed = await this.options.store.readAuthority(key);
170
+ return refreshed.kind === 'snapshot'
171
+ ? readyRead(refreshed, target, decodeServiceInstanceAuthorityPayload(refreshed.payload))
172
+ : { kind: 'missing' };
173
+ }
174
+ return resolved;
79
175
  }
80
176
 
81
177
  async reserve(
@@ -117,6 +213,7 @@ export class ZLinkInstanceActivationAuthority implements ServiceAsyncInstanceAct
117
213
  reserved = await this.options.store.reserve(
118
214
  {
119
215
  key: { kind: 'instance_spot', globalId: target.targetSpotId },
216
+ actorRelocationPolicy: this.options.relocationPolicy?.(target.stableType),
120
217
  intent: {
121
218
  stableType: target.stableType,
122
219
  requestContentReference: stored.reference.value,
@@ -310,7 +407,11 @@ export class ZLinkInstanceActivationAuthority implements ServiceAsyncInstanceAct
310
407
  }
311
408
  const current = await this.options.store.readAuthority(authorityKey(target.targetSpotId));
312
409
  if (current.kind === 'snapshot') {
313
- const ready = readyRead(current, target);
410
+ const ready = readyRead(
411
+ current,
412
+ target,
413
+ decodeServiceInstanceAuthorityPayload(current.payload)
414
+ );
314
415
  if (ready.kind === 'ready') {
315
416
  this.pending.delete(reservation.token);
316
417
  return { kind: 'lost', route: ready.route };
@@ -344,7 +445,7 @@ export class ZLinkInstanceActivationAuthority implements ServiceAsyncInstanceAct
344
445
  throw new Error('Instance activation authority changed before inbox release.');
345
446
  }
346
447
  const recovery = decoded.activationRecovery;
347
- if (recovery === undefined) return routeFromSnapshot(current);
448
+ if (recovery === undefined) return routeFromSnapshot(current, decoded.spotId);
348
449
  let completed = current;
349
450
  if (recovery.replayCursor < recovery.inboxSequence) {
350
451
  const cursorStored = await this.options.store.compareExchangeAuthority(
@@ -459,22 +560,19 @@ export class ZLinkInstanceActivationAuthority implements ServiceAsyncInstanceAct
459
560
 
460
561
  function readyRead(
461
562
  snapshot: ZLinkAuthoritySnapshot,
462
- target: ServiceInstanceActivationTarget
563
+ target: ServiceInstanceActivationTarget,
564
+ decoded: ReturnType<typeof decodeServiceInstanceAuthorityPayload>
463
565
  ): ServiceInstanceAuthorityRead {
464
- const decoded =
465
- decodeServiceReadySpotAuthority(snapshot.payload) ??
466
- decodeServiceClosingSpotAuthority(snapshot.payload);
467
- const creating = decodeServiceInstanceAuthorityPayload(snapshot.payload);
468
566
  if (
469
- creating?.state === 'coldActivating' &&
567
+ decoded?.state === 'coldActivating' &&
470
568
  snapshot.allocation.objectKind === 'instance_spot' &&
471
- creating.stableType === target.stableType &&
472
- creating.spotId === target.targetSpotId &&
473
- creating.ownerId === snapshot.ownerId &&
474
- creating.ownerLeaseGeneration === snapshot.ownerLeaseGeneration &&
475
- creating.ownerMeshName === snapshot.allocation.descriptor.meshName &&
476
- creating.ownerNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration &&
477
- routingIdsEqual(creating.ownerNodeRid, snapshot.allocation.descriptor.rid) &&
569
+ decoded.stableType === target.stableType &&
570
+ decoded.spotId === target.targetSpotId &&
571
+ decoded.ownerId === snapshot.ownerId &&
572
+ decoded.ownerLeaseGeneration === snapshot.ownerLeaseGeneration &&
573
+ decoded.ownerMeshName === snapshot.allocation.descriptor.meshName &&
574
+ decoded.ownerNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration &&
575
+ routingIdsEqual(decoded.ownerNodeRid, snapshot.allocation.descriptor.rid) &&
478
576
  routingIdsEqual(target.targetNodeRid, snapshot.allocation.descriptor.rid) &&
479
577
  target.targetNodeGeneration === snapshot.allocation.descriptorLifecycleGeneration
480
578
  ) {
@@ -486,7 +584,8 @@ function readyRead(
486
584
  };
487
585
  }
488
586
  if (
489
- decoded?.kind !== 'instance_spot' ||
587
+ decoded === undefined ||
588
+ decoded.state === 'coldActivating' ||
490
589
  decoded.stableType !== target.stableType ||
491
590
  decoded.spotId !== target.targetSpotId ||
492
591
  snapshot.allocation.objectKind !== 'instance_spot' ||
@@ -501,7 +600,7 @@ function readyRead(
501
600
  ) {
502
601
  return { kind: 'missing' };
503
602
  }
504
- return { kind: 'ready', route: routeFromSnapshot(snapshot), authority: snapshot };
603
+ return { kind: 'ready', route: routeFromSnapshot(snapshot, decoded.spotId), authority: snapshot };
505
604
  }
506
605
 
507
606
  function requireCommitIdentity(
@@ -529,17 +628,23 @@ function requireCommitIdentity(
529
628
  }
530
629
  }
531
630
 
532
- function routeFromSnapshot(snapshot: ZLinkAuthoritySnapshot): ServiceInstanceRouteFence {
533
- const decoded =
534
- decodeServiceReadySpotAuthority(snapshot.payload) ??
535
- decodeServiceClosingSpotAuthority(snapshot.payload);
536
- if (decoded?.kind !== 'instance_spot') {
537
- throw new Error('Instance authority is not Ready.');
631
+ function routeFromSnapshot(
632
+ snapshot: ZLinkAuthoritySnapshot,
633
+ spotId?: string
634
+ ): ServiceInstanceRouteFence {
635
+ if (spotId === undefined) {
636
+ const decoded =
637
+ decodeServiceReadySpotAuthority(snapshot.payload) ??
638
+ decodeServiceClosingSpotAuthority(snapshot.payload);
639
+ if (decoded?.kind !== 'instance_spot') {
640
+ throw new Error('Instance authority is not Ready.');
641
+ }
642
+ spotId = decoded.spotId;
538
643
  }
539
644
  return {
540
645
  targetNodeRid: String(snapshot.allocation.descriptor.rid),
541
646
  targetNodeGeneration: snapshot.allocation.descriptorLifecycleGeneration,
542
- targetSpotId: decoded.spotId,
647
+ targetSpotId: spotId,
543
648
  objectGeneration: snapshot.objectGeneration,
544
649
  ownerId: snapshot.ownerId,
545
650
  authorityOwnerGeneration: snapshot.authorityOwnerGeneration,
@@ -4812,6 +4812,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4812
4812
  throw new Error('Relocated Entry Spot Actor dispatch is unavailable.');
4813
4813
  }
4814
4814
  return runtime.dispatchEntryActorPacket(
4815
+ 'remote',
4815
4816
  hidden.actor!.context.actorId,
4816
4817
  parts,
4817
4818
  returnResponse,
@@ -4820,6 +4821,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
4820
4821
  );
4821
4822
  }
4822
4823
  return this.requireSpotManager().dispatchRoutedActorPacket(
4824
+ 'remote',
4823
4825
  state.spotId,
4824
4826
  hidden.actor!.context.actorId,
4825
4827
  parts,
@@ -1,3 +1,4 @@
1
+ import type { ServiceInstanceColdActivationTarget } from '../foundation/service-stateful-wire-codec';
1
2
  import { ZLinkFrameworkException, ZLinkSpotKind, type RoutingId } from '../../contracts';
2
3
  import {
3
4
  ZLinkRuntimeDispatchErrorAction as ZLinkDispatchErrorAction,
@@ -44,7 +45,11 @@ import type {
44
45
  ZLinkSpotAddressTransport,
45
46
  ZLinkSpotRoutedTransport
46
47
  } from '../spots/spot-outbound';
47
- import type { ZLinkSpotRouteResolver, ZLinkSpotRouteTarget } from '../spots/spot-routing-internal';
48
+ import type {
49
+ ZLinkSpotRouteResolver,
50
+ ZLinkSpotRouteTarget,
51
+ ZLinkInstanceReactivationRoute
52
+ } from '../spots/spot-routing-internal';
48
53
 
49
54
  export interface ZLinkHostSpotAddressTransportOptions {
50
55
  readonly resolver: () => ZLinkSpotRouteResolver | undefined;
@@ -65,13 +70,7 @@ export function hasObjectClientCapability(role: 'none' | 'client' | 'server' | u
65
70
  type MissingTarget = {
66
71
  readonly meshName: string;
67
72
  readonly node: ZLinkBackendMeshNode;
68
- readonly target: {
69
- readonly targetNodeRid: string;
70
- readonly targetNodeGeneration: bigint;
71
- readonly targetSpotId: string;
72
- readonly stableType: string;
73
- readonly descriptorVersion: string;
74
- };
73
+ readonly target: ServiceInstanceColdActivationTarget;
75
74
  };
76
75
 
77
76
  type MissingTargetSelection =
@@ -106,7 +105,10 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
106
105
  const activationDeadlineUnixMs = BigInt(
107
106
  Math.round(Date.now() + this.options.defaultRequestTimeoutMs)
108
107
  );
109
- const existing = await this.resolveExisting(spotId);
108
+ const resolution = await this.resolveExisting(spotId, undefined, call);
109
+ const existing = resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
110
+ if (resolution !== undefined && 'kind' in resolution)
111
+ call = this.reactivationCall(resolution, call);
110
112
  if (existing !== undefined) {
111
113
  this.validateExisting(existing, call);
112
114
  try {
@@ -166,7 +168,7 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
166
168
  if (!call.instanceSpot) {
167
169
  return { status: ZLinkSubmitStatus.TargetNotFound };
168
170
  }
169
- const selected = this.selectMissingTarget(spotId, call);
171
+ const selected = this.selectMissingTarget(spotId, call, resolution);
170
172
  if (selected.kind === 'unsupported') {
171
173
  this.traceInstanceAddress(
172
174
  ZLinkMessageFlowOutcome.Dropped,
@@ -252,10 +254,13 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
252
254
  const timeoutMs = call.timeoutMs ?? this.options.defaultRequestTimeoutMs;
253
255
  const deadline = createSpotAddressDeadline(timeoutMs, call.signal);
254
256
  try {
255
- const existing = await this.resolveExisting(spotId, deadline.signal);
257
+ const resolution = await this.resolveExisting(spotId, deadline.signal, call);
258
+ const existing = resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
259
+ if (resolution !== undefined && 'kind' in resolution)
260
+ call = this.reactivationCall(resolution, call);
256
261
  deadline.requireRemaining();
257
262
  if (existing === undefined) {
258
- return await this.requestToMissingInstance(spotId, request, call, deadline);
263
+ return await this.requestToMissingInstance(spotId, request, call, deadline, resolution);
259
264
  }
260
265
  this.validateExisting(existing, call);
261
266
  try {
@@ -351,7 +356,8 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
351
356
  spotId: RoutingId,
352
357
  request: unknown,
353
358
  call: ZLinkSpotAddressCallOptions,
354
- deadline: ZLinkSpotAddressDeadline
359
+ deadline: ZLinkSpotAddressDeadline,
360
+ current?: ZLinkSpotRouteTarget | ZLinkInstanceReactivationRoute
355
361
  ): Promise<TReply> {
356
362
  if (!call.instanceSpot) {
357
363
  throw createInternalFrameworkException(
@@ -359,7 +365,7 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
359
365
  `Spot '${String(spotId)}' has no Ready authority.`
360
366
  );
361
367
  }
362
- const selected = this.selectMissingTarget(spotId, call);
368
+ const selected = this.selectMissingTarget(spotId, call, current);
363
369
  if (selected.kind === 'capacity') {
364
370
  throw missingInstancePlacementCapacity(spotId, call.instanceSpotType);
365
371
  }
@@ -507,14 +513,20 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
507
513
 
508
514
  private async resolveExisting(
509
515
  spotId: RoutingId,
510
- signal?: AbortSignal
511
- ): Promise<import('../spots/spot-routing-internal').ZLinkSpotRouteTarget | undefined> {
516
+ signal?: AbortSignal,
517
+ call?: ZLinkSpotAddressCallOptions
518
+ ): Promise<ZLinkSpotRouteTarget | ZLinkInstanceReactivationRoute | undefined> {
512
519
  const resolver = this.options.resolver();
513
520
  if (resolver === undefined) {
514
521
  throw new Error('Global Spot address resolution requires a Location Store.');
515
522
  }
516
523
  try {
517
- return await awaitWithAbort(resolver.resolve(spotId, signal), signal);
524
+ return await awaitWithAbort(
525
+ call?.instanceSpot === true
526
+ ? resolver.resolve(spotId, signal, true)
527
+ : resolver.resolve(spotId, signal),
528
+ signal
529
+ );
518
530
  } catch (error) {
519
531
  if (
520
532
  error instanceof ZLinkFrameworkException &&
@@ -526,9 +538,18 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
526
538
  }
527
539
  }
528
540
 
541
+ private reactivationCall(
542
+ route: ZLinkInstanceReactivationRoute,
543
+ call: ZLinkSpotAddressCallOptions
544
+ ): ZLinkSpotAddressCallOptions {
545
+ this.validateExisting(route, call);
546
+ return { ...call, initialMeshName: route.meshName, instanceSpotType: route.stableType };
547
+ }
548
+
529
549
  private selectMissingTarget(
530
550
  spotId: RoutingId,
531
- call: ZLinkSpotAddressCallOptions
551
+ call: ZLinkSpotAddressCallOptions,
552
+ current?: ZLinkSpotRouteTarget | ZLinkInstanceReactivationRoute
532
553
  ): MissingTargetSelection {
533
554
  const configuredMeshes = this.options.meshNames();
534
555
  if (
@@ -574,14 +595,15 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
574
595
  const stableType =
575
596
  call.instanceSpotType ?? (distinctTypes.length === 1 ? distinctTypes[0] : undefined);
576
597
  if (stableType === undefined) {
577
- if (distinctTypes.length === 0) return { kind: 'unsupported' };
598
+ if (distinctTypes.length === 0)
599
+ return { kind: current === undefined ? 'unsupported' : 'unavailable' };
578
600
  throw createInternalFrameworkException(
579
601
  ZLinkFrameworkInternalErrorKind.InvalidOperation,
580
602
  'Instance Spot type is required when multiple types are registered.'
581
603
  );
582
604
  }
583
605
  if (canInspectPlacementTypes && !distinctTypes.includes(stableType)) {
584
- return { kind: 'unsupported' };
606
+ return { kind: current === undefined ? 'unsupported' : 'unavailable' };
585
607
  }
586
608
  let unavailable = false;
587
609
  let capacity = false;
@@ -600,6 +622,7 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
600
622
  node,
601
623
  target: {
602
624
  ...placement.target,
625
+ targetMeshName: meshName,
603
626
  targetSpotId: String(spotId),
604
627
  stableType
605
628
  }
@@ -611,12 +634,12 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
611
634
  }
612
635
  if (unavailable) return { kind: 'unavailable' };
613
636
  if (capacity) return { kind: 'capacity' };
614
- if (unsupported) return { kind: 'unsupported' };
637
+ if (unsupported) return { kind: current === undefined ? 'unsupported' : 'unavailable' };
615
638
  return { kind: 'unavailable' };
616
639
  }
617
640
 
618
641
  private validateExisting(
619
- target: import('../spots/spot-routing-internal').ZLinkSpotRouteTarget,
642
+ target: Pick<ZLinkSpotRouteTarget, 'spotId' | 'spotKind' | 'stableType'>,
620
643
  call: ZLinkSpotAddressCallOptions
621
644
  ): void {
622
645
  if (!call.instanceSpot) return;
@@ -102,6 +102,7 @@ export class ZLinkSpotRuntimeOptionsFactory {
102
102
  Promise.resolve()
103
103
  },
104
104
  dispatchEntryActorPacket: (
105
+ origin,
105
106
  actorId,
106
107
  parts,
107
108
  returnResponse,
@@ -117,6 +118,7 @@ export class ZLinkSpotRuntimeOptionsFactory {
117
118
  );
118
119
  }
119
120
  return runtime.dispatchEntryActorPacket(
121
+ origin,
120
122
  actorId,
121
123
  parts,
122
124
  returnResponse,
@@ -38,6 +38,10 @@ const DEFAULT_USER_SPOT_CLEANUP_TIMEOUT_MS = 1_000;
38
38
  const DEFAULT_USER_SPOT_POLL_INTERVAL_MS = 10;
39
39
 
40
40
  export interface ZLinkUserSpotCreationCoordinatorOptions {
41
+ readonly relocationPolicy?: (
42
+ meshName: string,
43
+ stableType: string
44
+ ) => import('../locations/internal-location-contracts').ZLinkObjectReserveRequest['actorRelocationPolicy'];
41
45
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
42
46
  readonly target: (
43
47
  request: Pick<ZLinkUserSpotCreationRequest, 'meshName' | 'stableType'>,
@@ -169,6 +173,10 @@ export class ZLinkUserSpotCreationCoordinator {
169
173
  reserved = await this.options.store.reserve(
170
174
  {
171
175
  key: { kind: 'user_spot', globalId: String(request.spotId) },
176
+ actorRelocationPolicy: this.options.relocationPolicy?.(
177
+ target.meshName,
178
+ request.stableType
179
+ ),
172
180
  intent: {
173
181
  stableType: request.stableType,
174
182
  requestContentReference: contentReference,
@@ -37,9 +37,12 @@ import type {
37
37
  ZLinkObjectCommitResult,
38
38
  ZLinkObjectCreationCompleteRequest,
39
39
  ZLinkObjectCreationCompleteResult,
40
- ZLinkObjectReserveRequest,
41
40
  ZLinkObjectReserveResult
42
41
  } from '../../contracts/Locations/Authority';
42
+ import type {
43
+ ZLinkObjectReserveRequest,
44
+ ZLinkEndedOwnerReleaseIntent
45
+ } from './internal-location-contracts';
43
46
 
44
47
  /**
45
48
  * Framework-private domain repository. Provider implementations never
@@ -143,7 +146,8 @@ export interface ZLinkDomainLocationStore {
143
146
  releaseEndedReservation?(
144
147
  key: ZLinkAuthorityKey,
145
148
  expectedStoreVersion: string,
146
- signal?: AbortSignal
149
+ signal?: AbortSignal,
150
+ request?: ZLinkEndedOwnerReleaseIntent
147
151
  ): Promise<boolean>;
148
152
  abort(
149
153
  request: ZLinkObjectAbortRequest,