@zlink-systems/framework 0.32.0 → 0.32.2

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 (37) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/runtime/actors/actor-context.js +1 -1
  3. package/dist/runtime/actors/actor-creation.js +1 -1
  4. package/dist/runtime/actors/actor-runtime-state.d.ts +2 -1
  5. package/dist/runtime/actors/actor-runtime-state.js +24 -8
  6. package/dist/runtime/actors/index.d.ts +3 -3
  7. package/dist/runtime/actors/index.js +19 -32
  8. package/dist/runtime/host/entry-actor-runtime.d.ts +3 -9
  9. package/dist/runtime/host/entry-actor-runtime.js +2 -10
  10. package/dist/runtime/host/spot-runtime-options-factory.js +2 -1
  11. package/dist/runtime/locations/actor-location-claims.js +9 -10
  12. package/dist/runtime/spots/index.d.ts +4 -4
  13. package/dist/runtime/spots/index.js +24 -0
  14. package/dist/runtime/spots/spot-activation.d.ts +2 -0
  15. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -1
  16. package/dist/runtime/spots/spot-actor-admission-coordinator.js +2 -1
  17. package/dist/runtime/spots/spot-actor-ingress-admission.d.ts +11 -7
  18. package/dist/runtime/spots/spot-actor-ingress-admission.js +14 -6
  19. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +3 -3
  20. package/dist/runtime/spots/spot-actor-packet-dispatch.js +2 -2
  21. package/dist/runtime/spots/spot-entry-activation.js +1 -1
  22. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -4
  23. package/package.json +2 -2
  24. package/src/runtime/actors/actor-context.ts +1 -1
  25. package/src/runtime/actors/actor-creation.ts +1 -1
  26. package/src/runtime/actors/actor-runtime-state.ts +27 -9
  27. package/src/runtime/actors/index.ts +30 -35
  28. package/src/runtime/host/entry-actor-runtime.ts +4 -34
  29. package/src/runtime/host/spot-runtime-options-factory.ts +2 -1
  30. package/src/runtime/locations/actor-location-claims.ts +14 -14
  31. package/src/runtime/spots/index.ts +34 -4
  32. package/src/runtime/spots/spot-activation.ts +2 -0
  33. package/src/runtime/spots/spot-actor-admission-coordinator.ts +3 -1
  34. package/src/runtime/spots/spot-actor-ingress-admission.ts +29 -11
  35. package/src/runtime/spots/spot-actor-packet-dispatch.ts +5 -5
  36. package/src/runtime/spots/spot-entry-activation.ts +1 -11
  37. package/src/runtime/spots/spot-runtime-ports.ts +2 -10
@@ -59,7 +59,7 @@ class DefaultZLinkActorContext {
59
59
  return this.state.boundSession;
60
60
  }
61
61
  get objectGeneration() {
62
- return this.state.nativeActorRef?.generation ?? 1n;
62
+ return this.state.currentActorRef().generation;
63
63
  }
64
64
  get spotId() {
65
65
  return this.state.spotId;
@@ -116,7 +116,7 @@ class ZLinkActorCreationCoordinator {
116
116
  }
117
117
  }
118
118
  else if (nodeRid !== undefined && updateLocation) {
119
- await this.options.locationLifecycle?.setActorRef(actorType, actorId, { actorId, objectGeneration: 1n, meshName, nodeRid }, 0n);
119
+ await this.options.locationLifecycle?.setActorRef(actorType, actorId, (0, actor_runtime_state_1.toFrameworkActorRef)(state.currentActorRef(), meshName), 0n);
120
120
  }
121
121
  return { status: 'created', actor, reply: response.reply };
122
122
  }
@@ -76,6 +76,7 @@ export declare class ZLinkActorRuntimeState {
76
76
  private spotGenerationValue;
77
77
  private spotMembershipEpochValue;
78
78
  private nativeActorRefValue;
79
+ private nativeActorDestroyed;
79
80
  private entryNodeRidValue;
80
81
  boundSessionValue: ZLinkRemoteBoundSessionTarget | undefined;
81
82
  private remoteActorPacketTargetValue;
@@ -96,6 +97,7 @@ export declare class ZLinkActorRuntimeState {
96
97
  get spotMembershipEpoch(): bigint;
97
98
  get spotGeneration(): bigint | undefined;
98
99
  get nativeActorRef(): ZLinkBackendActorRef | undefined;
100
+ currentActorRef(): ZLinkBackendActorRef;
99
101
  get boundSessionBindingGeneration(): bigint;
100
102
  get entryNodeRid(): RoutingId | undefined;
101
103
  /** The one current Session binding of this Actor (Session–Actor binding §6). */
@@ -124,7 +126,6 @@ export declare class ZLinkActorRuntimeState {
124
126
  setOwnerLeaseGeneration(generation: bigint): void;
125
127
  getOrStartDestroy(entryNodeRid: RoutingId, destroy: (actorRef: ZLinkBackendActorRef | undefined) => Promise<void>): Promise<void>;
126
128
  markNativeActorDestroyed(actorRef: ZLinkBackendActorRef): void;
127
- clearFailedDestroy(task: Promise<void>): void;
128
129
  ensureContext(createContext: () => ZLinkActorContext): ZLinkActorContext;
129
130
  getOrStartCreation(actorType: string, failIfExists: boolean, createActor: () => Promise<ZLinkActorCreationAttemptResult>): ZLinkActorCreationOperation;
130
131
  clearFailedCreation(task: Promise<ZLinkActorCreationAttemptResult>): boolean;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZLinkActorRuntimeState = void 0;
4
4
  exports.toFrameworkRoutingId = toFrameworkRoutingId;
5
5
  exports.toFrameworkActorRef = toFrameworkActorRef;
6
+ const configuration_1 = require("../configuration");
6
7
  const state_lane_1 = require("../execution/state-lane");
7
8
  const framework_errors_internal_1 = require("../framework-errors-internal");
8
9
  const routing_id_1 = require("../routing-id");
@@ -57,7 +58,9 @@ class ZLinkActorRuntimeState {
57
58
  spotIdValue;
58
59
  spotGenerationValue;
59
60
  spotMembershipEpochValue = 0n;
61
+ // Preserve the exact incarnation until teardown finalization, even after native removal.
60
62
  nativeActorRefValue;
63
+ nativeActorDestroyed = false;
61
64
  entryNodeRidValue;
62
65
  boundSessionValue;
63
66
  remoteActorPacketTargetValue;
@@ -103,6 +106,12 @@ class ZLinkActorRuntimeState {
103
106
  get nativeActorRef() {
104
107
  return this.nativeActorRefValue;
105
108
  }
109
+ currentActorRef() {
110
+ if (this.nativeActorRefValue === undefined) {
111
+ throw new configuration_1.ZLinkConfigurationException(`Actor '${this.actorId}' lifecycle identity is not initialized.`);
112
+ }
113
+ return this.nativeActorRefValue;
114
+ }
106
115
  get boundSessionBindingGeneration() {
107
116
  return this.boundSessionValue?.bindingGeneration ?? 0n;
108
117
  }
@@ -194,20 +203,23 @@ class ZLinkActorRuntimeState {
194
203
  !(0, routing_id_1.routingIdsEqual)(toFrameworkRoutingId(actorRef.nodeRid), entryNodeRid)) {
195
204
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorRouteNotFound, `Actor '${this.actorId}' is not owned by this Entry Spot.`);
196
205
  }
197
- const task = Promise.resolve().then(() => destroy(actorRef));
206
+ const task = Promise.resolve()
207
+ .then(() => destroy(this.nativeActorDestroyed ? undefined : actorRef))
208
+ .catch((error) => {
209
+ // A caller may start another attempt only after this attempt has failed.
210
+ if (this.destroyTask === task) {
211
+ this.destroyTask = undefined;
212
+ this.movingValue = false;
213
+ }
214
+ throw error;
215
+ });
198
216
  this.destroyTask = task;
199
217
  return task;
200
218
  }
201
219
  markNativeActorDestroyed(actorRef) {
202
220
  if (this.nativeActorRefValue === actorRef) {
203
- this.nativeActorRefValue = undefined;
204
- }
205
- }
206
- clearFailedDestroy(task) {
207
- if (this.destroyTask === task) {
208
- this.destroyTask = undefined;
221
+ this.nativeActorDestroyed = true;
209
222
  }
210
- this.movingValue = false;
211
223
  }
212
224
  ensureContext(createContext) {
213
225
  this.context ??= createContext();
@@ -243,6 +255,7 @@ class ZLinkActorRuntimeState {
243
255
  this.createRequestPayloadValue = undefined;
244
256
  this.configured = false;
245
257
  this.nativeActorRefValue = undefined;
258
+ this.nativeActorDestroyed = false;
246
259
  this.entryNodeRidValue = undefined;
247
260
  return true;
248
261
  }
@@ -278,6 +291,7 @@ class ZLinkActorRuntimeState {
278
291
  }
279
292
  setNativeActorRef(actorRef) {
280
293
  this.nativeActorRefValue = actorRef;
294
+ this.nativeActorDestroyed = false;
281
295
  this.entryNodeRidValue ??= toFrameworkRoutingId(actorRef.nodeRid);
282
296
  }
283
297
  /** @internal Installs one authority-confirmed binding inside its Actor owner turn. */
@@ -344,6 +358,7 @@ class ZLinkActorRuntimeState {
344
358
  this.spotGenerationValue = undefined;
345
359
  this.spotMembershipEpochValue = 0n;
346
360
  this.nativeActorRefValue = undefined;
361
+ this.nativeActorDestroyed = false;
347
362
  this.entryNodeRidValue = undefined;
348
363
  this.boundSessionValue = undefined;
349
364
  this.remoteActorPacketTargetValue = undefined;
@@ -366,6 +381,7 @@ class ZLinkActorRuntimeState {
366
381
  this.spotValue = undefined;
367
382
  this.spotIdValue = undefined;
368
383
  this.nativeActorRefValue = undefined;
384
+ this.nativeActorDestroyed = false;
369
385
  // The arriving relocation installs the binding it carries.
370
386
  this.boundSessionValue = undefined;
371
387
  this.remoteActorPacketTargetValue = undefined;
@@ -64,7 +64,7 @@ export declare class DefaultZLinkActorManager implements ZLinkActorManager {
64
64
  private awaitActorDestroy;
65
65
  getOrCreateActor(actorId: string, actorType: string, signal?: AbortSignal): Promise<ZLinkActor>;
66
66
  getOrCreateWithNativeRef(actorId: string, actorType: string, actorRef: ZLinkBackendActorRef, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActor>;
67
- getOrCreateActorResult(actorId: string, actorType: string, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActorLocalCreateResult>;
67
+ getOrCreateActorResult(actorId: string, actorType: string, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActorLocalCreateResult<ActorRef | undefined>>;
68
68
  createReservedActorResult(actorId: string, actorType: string, createRequest: unknown, signal?: AbortSignal, nativeActorRef?: ZLinkBackendActorRef): Promise<ZLinkActorLocalCreateResult>;
69
69
  adoptCreatedAuthority(actorId: string, authorityOwnerGeneration: bigint, ownerLeaseGeneration: bigint): void;
70
70
  getState(actorId: string): ZLinkActorRuntimeState | undefined;
@@ -77,10 +77,10 @@ export declare class DefaultZLinkActorManager implements ZLinkActorManager {
77
77
  private getOrCreateState;
78
78
  private actorRefForState;
79
79
  }
80
- export type ZLinkActorLocalCreateResult = {
80
+ export type ZLinkActorLocalCreateResult<TActorRef extends ActorRef | undefined = ActorRef> = {
81
81
  readonly status: 'existing' | 'created';
82
82
  readonly actor: ZLinkActor;
83
- readonly actorRef: ActorRef;
83
+ readonly actorRef: TActorRef;
84
84
  readonly reply?: unknown;
85
85
  } | {
86
86
  readonly status: 'rejected';
@@ -17,7 +17,6 @@ const actor_creation_1 = require("./actor-creation");
17
17
  const actor_runtime_state_1 = require("./actor-runtime-state");
18
18
  const transferred_actor_rollback_1 = require("./transferred-actor-rollback");
19
19
  const ACTOR_CREATION_RETRY_INTERVAL_MS = 5;
20
- const LOCAL_DIAGNOSTIC_NODE_RID = 'local';
21
20
  var actor_client_1 = require("./actor-client");
22
21
  Object.defineProperty(exports, "DefaultZLinkActorClient", { enumerable: true, get: function () { return actor_client_1.DefaultZLinkActorClient; } });
23
22
  var actor_context_1 = require("./actor-context");
@@ -100,7 +99,7 @@ class DefaultZLinkActorManager {
100
99
  if (this.relocationStaged.has(actorId))
101
100
  return undefined;
102
101
  const state = this.states.get(actorId);
103
- if (state?.actor !== undefined) {
102
+ if (state?.actor !== undefined && state.nativeActorRef !== undefined) {
104
103
  return this.actorRefForState(state);
105
104
  }
106
105
  return await this.options.actorRefResolver?.resolveActorRef(actorId, signal);
@@ -111,7 +110,9 @@ class DefaultZLinkActorManager {
111
110
  async findSpot(actorId, signal) {
112
111
  (0, abort_1.throwIfAborted)(signal);
113
112
  const state = this.states.get(actorId);
114
- if (state?.actor === undefined || state.spotId === undefined)
113
+ if (state?.actor === undefined ||
114
+ state.spotId === undefined ||
115
+ state.nativeActorRef === undefined)
115
116
  return undefined;
116
117
  const spotId = state.spotId;
117
118
  const actorRef = this.actorRefForState(state);
@@ -162,13 +163,7 @@ class DefaultZLinkActorManager {
162
163
  }
163
164
  await this.finalizeDestroyedActor(actor.actorId, state);
164
165
  }));
165
- try {
166
- await this.awaitActorDestroy(state, destroyTask);
167
- }
168
- catch (error) {
169
- state.clearFailedDestroy(destroyTask);
170
- throw error;
171
- }
166
+ await this.awaitActorDestroy(state, destroyTask);
172
167
  return true;
173
168
  }
174
169
  async materializeTransferredActor(actorId, actorType, adapterKey, transferState, signal) {
@@ -235,9 +230,10 @@ class DefaultZLinkActorManager {
235
230
  if (result.status === 'failed') {
236
231
  throw actorApplicationFailure(actorId, result.error);
237
232
  }
233
+ const actorRef = result.actorRef ?? this.actorRefForState(this.states.get(actorId));
238
234
  return result.reply === undefined
239
- ? { status: result.status, actor: result.actorRef }
240
- : { status: result.status, actor: result.actorRef, reply: result.reply };
235
+ ? { status: result.status, actor: actorRef }
236
+ : { status: result.status, actor: actorRef, reply: result.reply };
241
237
  }
242
238
  ensureActorTypeBelongsToMesh(meshName, actorType) {
243
239
  const registeredMesh = this.options.actorMeshNameProvider?.(actorType);
@@ -442,7 +438,13 @@ class DefaultZLinkActorManager {
442
438
  }
443
439
  async createReservedActorResult(actorId, actorType, createRequest, signal, nativeActorRef) {
444
440
  this.rememberActorMeshFromType(actorId, actorType);
445
- return await this.createOrGet(actorId, actorType, false, createRequest, signal, false, Registration_1.DEFAULT_REQUEST_TIMEOUT_MS, nativeActorRef);
441
+ const result = await this.createOrGet(actorId, actorType, false, createRequest, signal, false, Registration_1.DEFAULT_REQUEST_TIMEOUT_MS, nativeActorRef);
442
+ if (result.status === 'failed' || result.status === 'rejected')
443
+ return result;
444
+ return {
445
+ ...result,
446
+ actorRef: this.actorRefForState(this.states.get(actorId))
447
+ };
446
448
  }
447
449
  adoptCreatedAuthority(actorId, authorityOwnerGeneration, ownerLeaseGeneration) {
448
450
  const state = this.states.get(actorId);
@@ -473,7 +475,7 @@ class DefaultZLinkActorManager {
473
475
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorRouteNotFound, `Actor '${actor.context.actorId}' must leave its current SPOT before destroy.`);
474
476
  }
475
477
  const destroyTask = state.getOrStartDestroy(entryNodeRid, (actorRef) => this.terminateActor(state, async () => {
476
- const destroyedActorRef = actorRef === undefined ? undefined : (0, actor_runtime_state_1.toFrameworkActorRef)(actorRef, state.meshName ?? '');
478
+ const destroyedActorRef = state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
477
479
  if (actorRef !== undefined) {
478
480
  await node.destroyActor(actorRef, 0, destroySignal);
479
481
  state.markNativeActorDestroyed(actorRef);
@@ -483,13 +485,7 @@ class DefaultZLinkActorManager {
483
485
  }
484
486
  await this.finalizeDestroyedActor(actor.context.actorId, state);
485
487
  }));
486
- try {
487
- await this.awaitActorDestroy(state, destroyTask);
488
- }
489
- catch (error) {
490
- state.clearFailedDestroy(destroyTask);
491
- throw error;
492
- }
488
+ await this.awaitActorDestroy(state, destroyTask);
493
489
  }
494
490
  async createOrGet(actorId, actorType, failIfExists, request, signal, claimLocation = true, timeoutMs = Registration_1.DEFAULT_REQUEST_TIMEOUT_MS, nativeActorRef) {
495
491
  const deadline = performance.now() + timeoutMs;
@@ -533,7 +529,7 @@ class DefaultZLinkActorManager {
533
529
  try {
534
530
  const result = await operation.task;
535
531
  if (result.status === 'created') {
536
- const actorRef = this.actorRefForState(state);
532
+ const actorRef = state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
537
533
  return {
538
534
  status: operation.created ? 'created' : 'existing',
539
535
  actor: result.actor,
@@ -602,16 +598,7 @@ class DefaultZLinkActorManager {
602
598
  return state;
603
599
  }
604
600
  actorRefForState(state) {
605
- const nativeActorRef = state.nativeActorRef;
606
- if (nativeActorRef !== undefined) {
607
- return (0, actor_runtime_state_1.toFrameworkActorRef)(nativeActorRef, state.meshName ?? '');
608
- }
609
- return {
610
- actorId: state.actorId,
611
- objectGeneration: 1n,
612
- meshName: state.meshName ?? '',
613
- nodeRid: this.options.actorCreatedNodeRidProvider?.() ?? LOCAL_DIAGNOSTIC_NODE_RID
614
- };
601
+ return (0, actor_runtime_state_1.toFrameworkActorRef)(state.currentActorRef(), state.meshName ?? '');
615
602
  }
616
603
  }
617
604
  exports.DefaultZLinkActorManager = DefaultZLinkActorManager;
@@ -1,14 +1,11 @@
1
- import type { ApplicationJobOrigin } from '../application-jobs/contracts';
2
- import type { ZLinkActorRequestTerminal } from '../spots/spot-actor-packet-dispatch';
3
- import type { ZLinkMessageFollowOrigin } from '../foundation/service-runtime-contracts';
4
1
  import type { ActorRef, ZLinkActor } from '../../contracts';
5
- import type { Message } from '../../contracts/Common/Message';
6
2
  import type { ZLinkBackendSpotNode } from '../backend';
7
- import type { DefaultZLinkActorManager, ZLinkRemoteBoundSessionTarget } from '../actors';
3
+ import type { DefaultZLinkActorManager } from '../actors';
8
4
  import type { DefaultZLinkSpotManager, ZLinkSpotNodeRuntimeManager } from '../spots';
9
5
  import type { ZLinkEntryActorRuntime } from '../spots/spot-runtime-ports';
10
6
  import type { ZLinkStreamActorLifecyclePort } from '../streams/stream-binding-runtime-ports';
11
7
  import type { ZLinkBoundSessionRelay } from './bound-session-relay';
8
+ import type { ZLinkActorIngressMembership } from '../spots/spot-actor-ingress-admission';
12
9
  export interface ZLinkEntryActorRuntimeOptions {
13
10
  readonly actorManager: () => DefaultZLinkActorManager | undefined;
14
11
  readonly spotManager: () => DefaultZLinkSpotManager | undefined;
@@ -26,9 +23,6 @@ export declare class ZLinkEntryActorRuntimeService implements ZLinkEntryActorRun
26
23
  releaseMembers(meshName: string): Promise<void>;
27
24
  commitActorTransaction(actor: ZLinkActor, onJoined: () => Promise<void>): Promise<void>;
28
25
  destroyActor(node: ZLinkBackendSpotNode, entryNodeRid: ActorRef['nodeRid'], actor: ZLinkActor, signal?: AbortSignal): Promise<void>;
29
- routePacket(actorId: string, parts: readonly Message[], returnResponse?: boolean, remoteBoundSessionTarget?: ZLinkRemoteBoundSessionTarget, fallbackActorRef?: ActorRef, origin?: ApplicationJobOrigin, requestTerminal?: ZLinkActorRequestTerminal, messageFollowOrigin?: ZLinkMessageFollowOrigin): {
30
- readonly handled: boolean;
31
- readonly response?: unknown;
32
- };
26
+ get actorIngressMembership(): ZLinkActorIngressMembership | undefined;
33
27
  private requireActorManager;
34
28
  }
@@ -61,16 +61,8 @@ class ZLinkEntryActorRuntimeService {
61
61
  async destroyActor(node, entryNodeRid, actor, signal) {
62
62
  await this.requireActorManager().destroyActor(node, entryNodeRid, actor, signal);
63
63
  }
64
- routePacket(actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, origin = 'remote', requestTerminal, messageFollowOrigin) {
65
- const spotManager = this.options.spotManager();
66
- const spotId = this.options.actorManager()?.getState(actorId)?.spotId;
67
- if (spotId === undefined || spotManager === undefined) {
68
- return { handled: false };
69
- }
70
- return {
71
- handled: true,
72
- response: spotManager.dispatchRoutedActorPacket(origin, spotId, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin)
73
- };
64
+ get actorIngressMembership() {
65
+ return this.options.spotManager()?.actorIngressMembership;
74
66
  }
75
67
  requireActorManager() {
76
68
  const manager = this.options.actorManager();
@@ -165,7 +165,8 @@ class ZLinkSpotRuntimeOptionsFactory {
165
165
  : (0, actors_1.toFrameworkActorRef)(state.nativeActorRef, state.meshName ?? '');
166
166
  const localNodeRid = state?.meshName === undefined ? undefined : this.meshNodeRoutingId(state.meshName);
167
167
  if (state?.actor === undefined ||
168
- state.remoteActorPacketTarget !== undefined ||
168
+ (state.remoteActorPacketTarget !== undefined &&
169
+ !(0, routing_id_1.routingIdsEqual)(state.remoteActorPacketTarget.targetNodeRid, localNodeRid)) ||
169
170
  actorRef === undefined ||
170
171
  localNodeRid === undefined ||
171
172
  !(0, routing_id_1.routingIdsEqual)(actorRef.nodeRid, localNodeRid)) {
@@ -311,24 +311,23 @@ class ZLinkActorLocationClaims {
311
311
  const current = await store.readAuthority(key);
312
312
  if (!matchesActorAuthority(current, actorRef, ownerToken))
313
313
  return;
314
- const result = await store.compareExchangeAuthority(key, current.storeVersion, {
314
+ let result = await store.compareExchangeAuthority(key, current.storeVersion, {
315
315
  kind: 'delete'
316
316
  });
317
- if (result.kind === 'deleted' ||
318
- (result.kind === 'conflict' && result.current.kind === 'missing')) {
319
- return;
320
- }
321
317
  if (result.kind === 'conflict' &&
322
318
  result.current.kind === 'snapshot' &&
323
319
  matchesActorAuthority(result.current, actorRef, ownerToken)) {
324
- const retry = await store.compareExchangeAuthority(key, result.current.storeVersion, {
320
+ result = await store.compareExchangeAuthority(key, result.current.storeVersion, {
325
321
  kind: 'delete'
326
322
  });
327
- if (retry.kind === 'deleted' ||
328
- (retry.kind === 'conflict' && retry.current.kind === 'missing')) {
329
- return;
330
- }
331
323
  }
324
+ if (result.kind === 'deleted' ||
325
+ (result.kind === 'conflict' && result.current.kind === 'missing')) {
326
+ return;
327
+ }
328
+ throw new Errors_1.ZLinkFrameworkException(result.kind === 'conflict'
329
+ ? classifyMembershipAuthorityFailure(result.current, actorRef)
330
+ : Errors_1.ZLinkFrameworkErrorKind.InternalFailure, `Actor '${actorId}' authority release was rejected with status ${result.kind}.`);
332
331
  }
333
332
  }
334
333
  exports.ZLinkActorLocationClaims = ZLinkActorLocationClaims;
@@ -1,4 +1,5 @@
1
1
  import { inspectRuntime } from '../internal-observation';
2
+ import type { ZLinkActorDispatchOwner, ZLinkActorIngressMembership } from './spot-actor-ingress-admission';
2
3
  import type { ApplicationJobOrigin } from '../application-jobs/contracts';
3
4
  import type { ActorRef, RoutingId, Type, ZLinkMessageSerializer, ZLinkInstanceSpot, ZLinkActor, ZLinkChannelClient, ZLinkFanoutClient, ZLinkSpot, ZLinkSpotActorJoinResult, ZLinkSpotInfo, ZLinkSpotPublisherClient } from '../../contracts';
4
5
  import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
@@ -73,10 +74,7 @@ export interface ZLinkSpotManagerOptions {
73
74
  readonly releaseActorMembers?: (meshName: string, spotId: RoutingId) => Promise<void>;
74
75
  readonly userSpotExecutionMode?: (meshName: string, spotType: Type<ZLinkSpot>) => import('../../contracts').ZLinkUserSpotExecutionMode;
75
76
  readonly userSpotRelocationCoordinationMode?: (meshName: string, spotType: Type<ZLinkSpot>) => import('../../contracts').ZLinkSpotRelocationCoordinationMode;
76
- readonly actorDispatchOwnerResolver?: (actorId: string) => {
77
- readonly actorRef?: ActorRef;
78
- readonly spotId?: RoutingId;
79
- };
77
+ readonly actorDispatchOwnerResolver?: (actorId: string) => ZLinkActorDispatchOwner;
80
78
  readonly channelClient?: ZLinkChannelClient;
81
79
  readonly fanoutClient?: ZLinkFanoutClient;
82
80
  readonly spotPublisherClient?: ZLinkSpotPublisherClient;
@@ -139,6 +137,7 @@ interface ZLinkTargetSpotCloseOperation {
139
137
  }
140
138
  export declare class DefaultZLinkSpotManager {
141
139
  private readonly options;
140
+ readonly actorIngressMembership: ZLinkActorIngressMembership;
142
141
  [inspectRuntime](): Readonly<{
143
142
  activeActivationCount: number;
144
143
  hasCloseableActivation: (meshName: string, spotId: RoutingId) => boolean;
@@ -225,6 +224,7 @@ export declare class DefaultZLinkSpotManager {
225
224
  cancelActorTransfer(spotId: RoutingId, actorId: string): Promise<void>;
226
225
  notifyJoinedSpotActorDisconnected(spotId: RoutingId, actor: ZLinkActor, signal?: AbortSignal): Promise<boolean>;
227
226
  dispatchRoutedActorPacket(origin: ApplicationJobOrigin, spotId: RoutingId, actorId: string, parts: readonly Message[], returnResponse?: boolean, remoteBoundSessionTarget?: ZLinkRemoteBoundSessionTarget, fallbackActorRef?: ActorRef, requestTerminal?: (response: unknown) => Promise<void> | void, messageFollowOrigin?: ZLinkMessageFollowOrigin): Promise<unknown>;
227
+ private dispatchActorPacketToOwner;
228
228
  /** Resolves the current Actor FIFO owner for both Entry and User membership. */
229
229
  admitSourceActorPacketPrefix(actorId: string, records: readonly ZLinkActorHandoffPrefixRecord[]): ZLinkActorHandoffPrefixAdmission;
230
230
  /** Runs an owner-side lifecycle decision in the Actor's existing execution gate. */
@@ -69,6 +69,7 @@ var spot_manager_public_1 = require("./spot-manager-public");
69
69
  Object.defineProperty(exports, "ZLinkPublicSpotManager", { enumerable: true, get: function () { return spot_manager_public_1.ZLinkPublicSpotManager; } });
70
70
  class DefaultZLinkSpotManager {
71
71
  options;
72
+ actorIngressMembership;
72
73
  [internal_observation_1.inspectRuntime]() {
73
74
  return Object.freeze({
74
75
  activeActivationCount: this.activations.activeActivations().length,
@@ -133,6 +134,10 @@ class DefaultZLinkSpotManager {
133
134
  spotRouteResolver: options.spotRouteResolver,
134
135
  actorTransferRuntime: options.actorTransferRuntime
135
136
  });
137
+ this.actorIngressMembership = {
138
+ resolveOwner: options.actorDispatchOwnerResolver,
139
+ dispatchToOwner: (spotId, delivery) => this.dispatchActorPacketToOwner(spotId, delivery)
140
+ };
136
141
  this.activationLifecycle = new spot_activation_1.ZLinkSpotActivationLifecycle({
137
142
  timerClock,
138
143
  spotTimerHandlers: options.spotTimerHandlers,
@@ -163,6 +168,7 @@ class DefaultZLinkSpotManager {
163
168
  createTopicMessage: options.createTopicMessage,
164
169
  nativeSpotNodeProvider: options.nativeSpotNodeProvider,
165
170
  actorResolver: options.actorResolver,
171
+ actorIngressMembership: this.actorIngressMembership,
166
172
  detachedTaskRunner: options.detachedTaskRunner,
167
173
  actorTransferRuntime: options.actorTransferRuntime,
168
174
  boundSessionRuntime: options.boundSessionRuntime,
@@ -920,6 +926,24 @@ class DefaultZLinkSpotManager {
920
926
  }
921
927
  return this.dispatchActorPacket(origin, activation, actorId, parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef, requestTerminal, messageFollowOrigin);
922
928
  }
929
+ dispatchActorPacketToOwner(spotId, delivery) {
930
+ const args = [
931
+ delivery.actorId,
932
+ delivery.parts,
933
+ delivery.returnResponse,
934
+ delivery.remoteBoundSessionTarget,
935
+ delivery.fallbackActorRef,
936
+ delivery.requestTerminal,
937
+ delivery.messageFollowOrigin
938
+ ];
939
+ if (spotId !== undefined) {
940
+ return this.dispatchRoutedActorPacket(delivery.origin, spotId, ...args);
941
+ }
942
+ if (this.options.dispatchEntryActorPacket === undefined) {
943
+ throw new configuration_1.ZLinkConfigurationException('Actor ingress requires an Entry FIFO port.');
944
+ }
945
+ return this.options.dispatchEntryActorPacket(delivery.origin, ...args);
946
+ }
923
947
  /** @internal Replays a record already owned by the Actor handoff queue. */
924
948
  dispatchRoutedActorPacketDirect(origin, spotId, actorId, parts, returnResponse = false, remoteBoundSessionTarget, fallbackActorRef) {
925
949
  const activation = this.activations.resolveUnique(spotId);
@@ -15,6 +15,7 @@ import { type ZLinkSpotAddressTransport, type ZLinkSpotRoutedTransport } from '.
15
15
  import { type ZLinkTimerClock, type ZLinkTimerRelocationState } from './spot-timer';
16
16
  import type { ZLinkDetachedTaskRunner } from './spot-actor-join-dispatch';
17
17
  import type { ZLinkRouteToActorJoinPrewarm } from './spot-actor-packet-dispatch';
18
+ import type { ZLinkActorIngressMembership } from './spot-actor-ingress-admission';
18
19
  import { ZLinkSpotActivation } from './spot-activation-state';
19
20
  import type { ZLinkSpotLocationClaim } from './spot-location-claim';
20
21
  import type { ZLinkSpotActorHandoffRuntime, ZLinkSpotActorTransferRuntime, ZLinkSpotBoundSessionRuntime } from './spot-runtime-ports';
@@ -49,6 +50,7 @@ export interface ZLinkSpotActivationLifecycleOptions {
49
50
  readonly createTopicMessage?: () => ZLinkBackendTopicMessage;
50
51
  readonly nativeSpotNodeProvider?: (meshName: string) => ZLinkBackendSpotNode | undefined;
51
52
  readonly actorResolver?: (actorId: string) => ZLinkActor | undefined;
53
+ readonly actorIngressMembership?: ZLinkActorIngressMembership;
52
54
  readonly actorTransferRuntime?: ZLinkSpotActorTransferRuntime;
53
55
  readonly boundSessionRuntime?: ZLinkSpotBoundSessionRuntime;
54
56
  readonly actorHandoffRuntime?: ZLinkSpotActorHandoffRuntime;
@@ -8,7 +8,7 @@ import type { ZLinkSpotActivationLifecycleOptions } from './spot-activation';
8
8
  import { ZLinkSpotActivation } from './spot-activation-state';
9
9
  import { ZLinkSpotActorJoinDispatch } from './spot-actor-join-dispatch';
10
10
  import type { ZLinkActorPacketExecution } from './spot-actor-packet-dispatch';
11
- type AdmissionOptions = Pick<ZLinkSpotActivationLifecycleOptions, 'actorHandoffRuntime' | 'admission' | 'actorResolver' | 'actorTransferRuntime' | 'boundSessionRuntime' | 'detachedTaskRunner' | 'dispatchErrors' | 'locationClaim' | 'messageSerializers' | 'nativeSpotNodeProvider' | 'createTopicMessage' | 'providerResolver' | 'runtimeEventPublisher' | 'routeToActorJoinPrewarm'>;
11
+ type AdmissionOptions = Pick<ZLinkSpotActivationLifecycleOptions, 'actorHandoffRuntime' | 'admission' | 'actorResolver' | 'actorIngressMembership' | 'actorTransferRuntime' | 'boundSessionRuntime' | 'detachedTaskRunner' | 'dispatchErrors' | 'locationClaim' | 'messageSerializers' | 'nativeSpotNodeProvider' | 'createTopicMessage' | 'providerResolver' | 'runtimeEventPublisher' | 'routeToActorJoinPrewarm'>;
12
12
  export declare class ZLinkSpotActorAdmissionCoordinator {
13
13
  private readonly options;
14
14
  constructor(options: AdmissionOptions);
@@ -96,7 +96,8 @@ class ZLinkSpotActorAdmissionCoordinator {
96
96
  resolveActor: (targetActorId) => this.options.actorResolver === undefined
97
97
  ? activation.resolveJoinedActor(targetActorId)
98
98
  : this.options.actorResolver(targetActorId),
99
- hasDepartedActor: (targetActorId) => activation.hasDepartedActor(targetActorId),
99
+ membershipSpotId: activation.spotId,
100
+ membership: this.options.actorIngressMembership,
100
101
  onDisconnectActor: (actor) => activation.serial.execute(() => activation.spot.onDisconnectActor?.(actor)),
101
102
  actorResponseSender: this.options.boundSessionRuntime?.sendActorResponse.bind(this.options.boundSessionRuntime),
102
103
  actorErrorSender: this.options.boundSessionRuntime?.sendActorError.bind(this.options.boundSessionRuntime),
@@ -1,4 +1,4 @@
1
- import type { ActorRef, ZLinkActor } from '../../contracts';
1
+ import type { ActorRef, RoutingId, ZLinkActor } from '../../contracts';
2
2
  import type { ZLinkActorPacketDelivery, ZLinkActorRequestTerminal } from './spot-actor-packet-dispatch';
3
3
  import type { ZLinkRemoteBoundSessionTarget } from '../actors';
4
4
  import type { ApplicationJobOrigin } from '../application-jobs/contracts';
@@ -16,10 +16,14 @@ export type ZLinkRouteToActorJoinPrewarm = (actorId: string, objectGeneration: b
16
16
  readonly resolve: (value: unknown) => void;
17
17
  readonly reject: (reason: unknown) => void;
18
18
  }) => 'parked' | 'not-found';
19
- export type ZLinkActorIngressRoute = (delivery: ZLinkActorPacketDelivery) => {
20
- readonly handled: boolean;
21
- readonly response?: unknown;
22
- } | undefined;
19
+ export interface ZLinkActorDispatchOwner {
20
+ readonly actorRef?: ActorRef;
21
+ readonly spotId?: RoutingId;
22
+ }
23
+ export interface ZLinkActorIngressMembership {
24
+ readonly resolveOwner?: (actorId: string) => ZLinkActorDispatchOwner;
25
+ readonly dispatchToOwner?: (spotId: RoutingId | undefined, delivery: ZLinkActorPacketDelivery) => Promise<unknown>;
26
+ }
23
27
  export type ZLinkActorIngressAdmission = {
24
28
  readonly kind: 'local';
25
29
  readonly actor: ZLinkActor;
@@ -33,8 +37,8 @@ export type ZLinkActorIngressAdmission = {
33
37
  /** Fixes one packet owner synchronously at the Actor FIFO head. */
34
38
  export declare function resolveActorIngress(delivery: ZLinkActorPacketDelivery, request: boolean, options: {
35
39
  readonly handoffRuntime?: ZLinkSpotActorHandoffRuntime;
36
- readonly routeBeforeLocal?: ZLinkActorIngressRoute;
40
+ readonly spotId?: RoutingId;
41
+ readonly membership?: ZLinkActorIngressMembership;
37
42
  readonly resolveActor: (actorId: string) => ZLinkActor | undefined;
38
- readonly hasDepartedActor?: (actorId: string) => boolean;
39
43
  readonly routeToActorJoinPrewarm?: ZLinkRouteToActorJoinPrewarm;
40
44
  }): ZLinkActorIngressAdmission;
@@ -2,18 +2,26 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveActorIngress = resolveActorIngress;
4
4
  const actor_message_follow_context_1 = require("../actors/actor-message-follow-context");
5
+ const routing_id_1 = require("../routing-id");
5
6
  const framework_errors_internal_1 = require("../framework-errors-internal");
6
7
  /** Fixes one packet owner synchronously at the Actor FIFO head. */
7
8
  function resolveActorIngress(delivery, request, options) {
8
9
  const handoff = options.handoffRuntime?.capture(delivery.origin, delivery.actorId, delivery.parts, delivery.returnResponse, delivery.remoteBoundSessionTarget, delivery.fallbackActorRef, undefined, delivery.messageFollowOrigin);
9
10
  if (handoff !== undefined)
10
11
  return { kind: 'follow', terminal: handoff };
11
- const route = options.routeBeforeLocal?.(delivery);
12
- if (route?.handled === true)
13
- return { kind: 'follow', terminal: Promise.resolve(route.response) };
14
- const actor = options.hasDepartedActor?.(delivery.actorId) === true
15
- ? undefined
16
- : options.resolveActor(delivery.actorId);
12
+ const membership = options.membership;
13
+ const owner = membership?.resolveOwner?.(delivery.actorId);
14
+ if (owner?.actorRef !== undefined && !(0, routing_id_1.routingIdsEqual)(owner.spotId, options.spotId)) {
15
+ if (membership?.dispatchToOwner === undefined) {
16
+ throw new Error('Actor ingress requires the current membership dispatch port.');
17
+ }
18
+ // Transfer the accepted delivery before releasing the source FIFO claim.
19
+ // Its terminal is awaited outside that claim by dispatchActorPacketInMailbox.
20
+ return { kind: 'follow', terminal: membership.dispatchToOwner(owner.spotId, delivery) };
21
+ }
22
+ const actor = membership?.resolveOwner === undefined || owner?.actorRef !== undefined
23
+ ? options.resolveActor(delivery.actorId)
24
+ : undefined;
17
25
  if (actor !== undefined)
18
26
  return { kind: 'local', actor };
19
27
  if (options.routeToActorJoinPrewarm !== undefined &&
@@ -1,4 +1,4 @@
1
- import { type ZLinkRouteToActorJoinPrewarm, type ZLinkActorIngressRoute } from './spot-actor-ingress-admission';
1
+ import { type ZLinkRouteToActorJoinPrewarm, type ZLinkActorIngressMembership } from './spot-actor-ingress-admission';
2
2
  import type { ZLinkSpotActorHandoffRuntime } from './spot-runtime-ports';
3
3
  import type { ApplicationJobOrigin } from '../application-jobs/contracts';
4
4
  import type { ActorRef, RoutingId, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
@@ -38,8 +38,8 @@ interface ZLinkSpotActorPacketDispatchOptions {
38
38
  readonly spotId: () => string;
39
39
  readonly registry: ZLinkSpotActorHandlerRegistryRuntime;
40
40
  readonly resolveActor: (actorId: string) => ZLinkActor | undefined;
41
- readonly hasDepartedActor?: (actorId: string) => boolean;
42
- readonly routeBeforeLocal?: ZLinkActorIngressRoute;
41
+ readonly membershipSpotId?: RoutingId;
42
+ readonly membership?: ZLinkActorIngressMembership;
43
43
  readonly onDisconnectActor: (actor: ZLinkActor) => Promise<void>;
44
44
  readonly actorResponseSender?: (actor: ZLinkActor, packetName: string, requestSeq: bigint, response: unknown, replyOptions: ZLinkActorResponseOptions, fallbackBoundSessionTarget?: ZLinkRemoteBoundSessionTarget, fallbackActorRef?: ActorRef, signal?: AbortSignal) => Promise<void> | void;
45
45
  readonly actorErrorSender?: (actorId: string, packetName: string, requestSeq: bigint, error: unknown, metadata: ReadonlyMap<string, string>, fallbackBoundSessionTarget?: ZLinkRemoteBoundSessionTarget, fallbackActorRef?: ActorRef) => Promise<void> | void;
@@ -80,9 +80,9 @@ class ZLinkSpotActorPacketDispatch {
80
80
  : undefined;
81
81
  const admitted = (0, spot_actor_ingress_admission_1.resolveActorIngress)(delivery, messageKind === ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorRequest, {
82
82
  handoffRuntime,
83
- routeBeforeLocal: this.options.routeBeforeLocal,
83
+ spotId: this.options.membershipSpotId,
84
+ membership: this.options.membership,
84
85
  resolveActor: this.options.resolveActor,
85
- hasDepartedActor: this.options.hasDepartedActor,
86
86
  routeToActorJoinPrewarm: this.options.routeToActorJoinPrewarm
87
87
  });
88
88
  if ('terminal' in admitted)
@@ -100,7 +100,7 @@ class ZLinkEntrySpotActivation {
100
100
  spotId: () => String(this.options.nativeSpot.routingId),
101
101
  registry: this.actorHandlers,
102
102
  resolveActor: (targetActorId) => this.options.entryActorRuntime?.resolveActor(targetActorId),
103
- routeBeforeLocal: (delivery) => this.options.entryActorRuntime?.routePacket(delivery.actorId, delivery.parts, delivery.returnResponse, delivery.remoteBoundSessionTarget, delivery.fallbackActorRef, delivery.origin, delivery.requestTerminal, delivery.messageFollowOrigin),
103
+ membership: this.options.entryActorRuntime?.actorIngressMembership,
104
104
  onDisconnectActor: (actor) => this.notifyDisconnectActor(actor),
105
105
  actorResponseSender: this.options.boundSessionRuntime?.sendActorResponse.bind(this.options.boundSessionRuntime),
106
106
  actorErrorSender: this.options.boundSessionRuntime?.sendActorError.bind(this.options.boundSessionRuntime),
@@ -7,15 +7,13 @@ import type { ZLinkActorHandoffDispatch } from '../actors/actor-handoff';
7
7
  import type { ZLinkActorResponseOptions } from './spot-actor-packet-dispatch';
8
8
  import type { ZLinkRemoteActorJoinActor, ZLinkRoutedActorTransferProvider } from './spot-remote-codec';
9
9
  import type { ZLinkBackendSpotNode } from '../backend/contracts';
10
+ import type { ZLinkActorIngressMembership } from './spot-actor-ingress-admission';
10
11
  export interface ZLinkEntryActorRuntime {
12
+ readonly actorIngressMembership?: ZLinkActorIngressMembership;
11
13
  releaseMembers?(meshName: string): Promise<void>;
12
14
  resolveActor(actorId: string): ZLinkActor | undefined;
13
15
  commitActorTransaction(actor: ZLinkActor, onJoined: () => Promise<void>): Promise<void>;
14
16
  destroyActor(node: ZLinkBackendSpotNode, entryNodeRid: RoutingId, actor: ZLinkActor, signal?: AbortSignal): Promise<void>;
15
- routePacket(actorId: string, parts: readonly Message[], returnResponse?: boolean, remoteBoundSessionTarget?: ZLinkRemoteBoundSessionTarget, fallbackActorRef?: ActorRef, origin?: ApplicationJobOrigin, requestTerminal?: import('./spot-actor-packet-dispatch').ZLinkActorRequestTerminal, messageFollowOrigin?: ZLinkMessageFollowOrigin): {
16
- readonly handled: boolean;
17
- readonly response?: unknown;
18
- };
19
17
  }
20
18
  export interface ZLinkSpotActorTransferRuntime {
21
19
  prepareDeferredJoinAccepted(actorId: string, operationId: ZLinkActorJoinOperationId, actorRef: ActorRef, rawReply: Uint8Array, replyContentType?: string): ZLinkDeferredJoinCompletion;