@zlink-systems/framework 0.29.1 → 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 (152) 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/application-jobs/application-ingress-record-owner.d.ts +3 -1
  13. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  14. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  15. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  16. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  17. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  18. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  19. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  20. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  21. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  22. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  23. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  24. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  25. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  26. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  28. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  29. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  30. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  31. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  32. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  33. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  34. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  35. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  36. package/dist/runtime/diagnostics/message-flow.js +3 -3
  37. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  38. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  39. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  40. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  41. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  42. package/dist/runtime/foundation/service-mailbox.js +2 -1
  43. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  44. package/dist/runtime/foundation/service-stateful-runtime.js +34 -23
  45. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  46. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  47. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  48. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  49. package/dist/runtime/host/actor-packet-relay.js +7 -7
  50. package/dist/runtime/host/actor-runtime-options-factory.js +1 -1
  51. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  52. package/dist/runtime/host/application-job-queue.js +66 -10
  53. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  54. package/dist/runtime/host/index.js +15 -11
  55. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  56. package/dist/runtime/host/instance-activation-authority.js +89 -25
  57. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  58. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  59. package/dist/runtime/host/spot-address-transport.js +25 -12
  60. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  61. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  62. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  63. package/dist/runtime/locations/domain-store-contract.d.ts +2 -2
  64. package/dist/runtime/locations/in-memory-authority-store.d.ts +4 -2
  65. package/dist/runtime/locations/in-memory-authority-store.js +80 -8
  66. package/dist/runtime/locations/in-memory-location-store.d.ts +2 -2
  67. package/dist/runtime/locations/in-memory-location-store.js +19 -3
  68. package/dist/runtime/locations/internal-location-contracts.d.ts +3 -0
  69. package/dist/runtime/locations/location-store-repository.d.ts +3 -2
  70. package/dist/runtime/locations/location-store-repository.js +165 -22
  71. package/dist/runtime/locations/resolvers.d.ts +2 -1
  72. package/dist/runtime/locations/resolvers.js +10 -1
  73. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  74. package/dist/runtime/messaging/submission-result.js +2 -2
  75. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  76. package/dist/runtime/spots/index.d.ts +4 -3
  77. package/dist/runtime/spots/index.js +14 -14
  78. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  79. package/dist/runtime/spots/spot-activation.js +4 -4
  80. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  81. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  82. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  83. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  84. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  85. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  86. package/dist/runtime/spots/spot-entry-activation.js +3 -1
  87. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +2 -1
  88. package/dist/runtime/spots/spot-node-runtime-manager.js +4 -11
  89. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  90. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  91. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  92. package/package.json +3 -3
  93. package/src/contracts/Configuration/Builders.ts +1 -2
  94. package/src/contracts/Configuration/Configs.ts +0 -6
  95. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  96. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  97. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  98. package/src/contracts/Configuration/index.ts +1 -6
  99. package/src/internal.ts +1 -0
  100. package/src/runtime/actors/actor-handoff.ts +13 -4
  101. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  102. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  103. package/src/runtime/application-jobs/contracts.ts +14 -1
  104. package/src/runtime/backend/contracts/index.ts +10 -18
  105. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  106. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  107. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  108. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  109. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  110. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  111. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  112. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  113. package/src/runtime/backend/raw-binding-port.ts +12 -2
  114. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  115. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  116. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  117. package/src/runtime/diagnostics/message-flow.ts +1 -1
  118. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  119. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  120. package/src/runtime/foundation/service-mailbox.ts +7 -1
  121. package/src/runtime/foundation/service-stateful-runtime.ts +61 -35
  122. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  123. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  124. package/src/runtime/host/actor-packet-relay.ts +7 -0
  125. package/src/runtime/host/actor-runtime-options-factory.ts +1 -0
  126. package/src/runtime/host/application-job-queue.ts +77 -10
  127. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  128. package/src/runtime/host/index.ts +24 -5
  129. package/src/runtime/host/instance-activation-authority.ts +132 -27
  130. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  131. package/src/runtime/host/spot-address-transport.ts +45 -22
  132. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  133. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  134. package/src/runtime/locations/domain-store-contract.ts +6 -2
  135. package/src/runtime/locations/in-memory-authority-store.ts +113 -9
  136. package/src/runtime/locations/in-memory-location-store.ts +25 -3
  137. package/src/runtime/locations/internal-location-contracts.ts +4 -0
  138. package/src/runtime/locations/location-store-repository.ts +220 -15
  139. package/src/runtime/locations/resolvers.ts +25 -2
  140. package/src/runtime/messaging/submission-result.ts +2 -2
  141. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  142. package/src/runtime/spots/index.ts +16 -0
  143. package/src/runtime/spots/spot-activation.ts +5 -0
  144. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  145. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  146. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  147. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  148. package/src/runtime/spots/spot-entry-activation.ts +3 -0
  149. package/src/runtime/spots/spot-node-runtime-manager.ts +7 -12
  150. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  151. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  152. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -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,
@@ -39,7 +39,10 @@ import type {
39
39
  ZLinkObjectCreationCompleteResult,
40
40
  ZLinkObjectReserveResult
41
41
  } from '../../contracts/Locations/Authority';
42
- import type { ZLinkObjectReserveRequest } from './internal-location-contracts';
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,
@@ -32,6 +32,7 @@ import type {
32
32
  ZLinkObjectCommitResult,
33
33
  ZLinkObjectCreationCompleteRequest,
34
34
  ZLinkObjectCreationCompleteResult,
35
+ ZLinkEndedOwnerReleaseIntent,
35
36
  ZLinkObjectReserveRequest,
36
37
  ZLinkObjectReserveResult,
37
38
  ZLinkCreationOperationIdentity,
@@ -42,7 +43,13 @@ import type {
42
43
  } from './internal-location-contracts';
43
44
  import { encodeAuthorityKey } from './authority-key-codec';
44
45
  import { creationTerminalPreimage } from './opaque-record-key';
45
- import { ServiceRelocationAuthorityPayloadCodec } from '../foundation/service-relocation-runtime';
46
+ import {
47
+ ServiceRelocationAuthorityPayloadCodec,
48
+ serviceRelocationAuthorityApplicationPayload
49
+ } from '../foundation/service-relocation-runtime';
50
+ import { decodeServiceReadySpotAuthority } from '../foundation/service-authority-payload-codec';
51
+ import { decodeActorAuthorityPayload } from '../actors/actor-authority-payload-codec';
52
+ import { ZLinkSpotKind } from '../../contracts';
46
53
 
47
54
  const MAX_GENERATION = 0x7fff_ffff_ffff_ffffn;
48
55
 
@@ -64,6 +71,7 @@ export interface ZLinkInMemoryAuthorityValidation {
64
71
  currentActive: ZLinkCapacityVector
65
72
  ): boolean;
66
73
  identityClaimed?(authorityKey: string): boolean;
74
+ hasSpotMembers?(spotId: string, objectGeneration: bigint): boolean;
67
75
  }
68
76
 
69
77
  interface AuthorityRow {
@@ -203,6 +211,26 @@ export class ZLinkInMemoryAuthorityStore {
203
211
  if (!this.isOwnerLive(row.snapshot)) {
204
212
  return { kind: 'conflict', current: this.read(keyValue) };
205
213
  }
214
+ const actor =
215
+ row.snapshot.allocation.objectKind === 'actor'
216
+ ? decodeActorAuthorityPayload(
217
+ serviceRelocationAuthorityApplicationPayload(mutation.payload)
218
+ )
219
+ : undefined;
220
+ const previousActor =
221
+ actor === undefined
222
+ ? undefined
223
+ : decodeActorAuthorityPayload(
224
+ serviceRelocationAuthorityApplicationPayload(row.snapshot.payload)
225
+ );
226
+ if (
227
+ actor?.currentSpotKind === ZLinkSpotKind.User &&
228
+ (previousActor?.currentSpotKind !== ZLinkSpotKind.User ||
229
+ previousActor.currentSpotId !== actor.currentSpotId ||
230
+ previousActor.currentSpotGeneration !== actor.currentSpotGeneration) &&
231
+ !this.acceptsUserSpotMembership(actor.currentSpotId, actor.currentSpotGeneration)
232
+ )
233
+ return { kind: 'conflict', current: this.read(keyValue) };
206
234
  if (
207
235
  mutation.generationTransition === 'reincarnate' &&
208
236
  (this.objectGeneration >= MAX_GENERATION || this.ownerGeneration >= MAX_GENERATION)
@@ -283,27 +311,41 @@ export class ZLinkInMemoryAuthorityStore {
283
311
  async releaseEndedReservation(
284
312
  key: ZLinkAuthorityKey,
285
313
  expectedStoreVersion: string,
286
- signal?: AbortSignal
314
+ signal?: AbortSignal,
315
+ request?: ZLinkEndedOwnerReleaseIntent
287
316
  ): Promise<boolean> {
288
317
  signal?.throwIfAborted();
289
- return this.tryReleaseEndedReservation(key, expectedStoreVersion);
318
+ return this.tryReleaseEndedReservation(key, expectedStoreVersion, request);
319
+ }
320
+
321
+ acceptsUserSpotMembership(spotId: string, objectGeneration: bigint): boolean {
322
+ const row = this.rows.get(encodeAuthorityKey('user_spot', spotId).value);
323
+ return (
324
+ row !== undefined &&
325
+ row.snapshot.allocation.objectKind === 'user_spot' &&
326
+ row.snapshot.allocation.state === 'active' &&
327
+ row.snapshot.objectGeneration === objectGeneration &&
328
+ decodeServiceReadySpotAuthority(row.snapshot.payload)?.kind === 'user_spot' &&
329
+ this.isOwnerLive(row.snapshot)
330
+ );
290
331
  }
291
332
 
292
333
  private tryReleaseEndedReservation(
293
334
  key: ZLinkAuthorityKey,
294
335
  expectedStoreVersion: string,
295
- request?: ZLinkObjectReserveRequest
336
+ request?: ZLinkEndedOwnerReleaseIntent
296
337
  ): boolean {
297
338
  const current = this.rows.get(key.value);
298
339
  if (
299
340
  current === undefined ||
300
341
  current.snapshot.storeVersion.value !== expectedStoreVersion ||
301
342
  (current.snapshot.allocation.state === 'active'
302
- ? request?.key.kind !== 'actor'
343
+ ? request === undefined
303
344
  : current.creation === undefined)
304
345
  )
305
346
  return false;
306
347
  if (current.snapshot.allocation.state === 'active') {
348
+ let unavailable = false;
307
349
  for (const aggregate of this.aggregates.values()) {
308
350
  if (
309
351
  aggregate.state === 'prepared' &&
@@ -311,9 +353,9 @@ export class ZLinkInMemoryAuthorityStore {
311
353
  (participant) => participant.authorityKey.value === key.value
312
354
  )
313
355
  )
314
- return false;
356
+ unavailable = true;
315
357
  }
316
- if (relocationAuthorityCodec.read(current.snapshot.payload) !== undefined) return false;
358
+ unavailable ||= relocationAuthorityCodec.read(current.snapshot.payload) !== undefined;
317
359
  if (
318
360
  this.validation.isOwnerLive({
319
361
  ownerId: current.snapshot.ownerId,
@@ -321,10 +363,37 @@ export class ZLinkInMemoryAuthorityStore {
321
363
  })
322
364
  )
323
365
  return false;
324
- if (request?.actorRelocationPolicy !== 'disabled')
366
+ const spot =
367
+ current.snapshot.allocation.objectKind === 'actor'
368
+ ? undefined
369
+ : decodeServiceReadySpotAuthority(current.snapshot.payload);
370
+ if (current.snapshot.allocation.objectKind !== 'actor') {
371
+ unavailable ||=
372
+ spot === undefined ||
373
+ spot.kind !== request?.key.kind ||
374
+ spot.stableType !== request.intent.stableType ||
375
+ spot.spotId !== request.key.globalId ||
376
+ spot.activationRecovery !== undefined;
377
+ if (spot?.kind === 'user_spot') {
378
+ unavailable ||=
379
+ this.validation.hasSpotMembers?.(spot.spotId, current.snapshot.objectGeneration) ===
380
+ true;
381
+ for (const row of this.rows.values()) {
382
+ if (row.snapshot.allocation.objectKind !== 'actor') continue;
383
+ const actor = decodeActorAuthorityPayload(
384
+ serviceRelocationAuthorityApplicationPayload(row.snapshot.payload)
385
+ );
386
+ unavailable ||=
387
+ actor?.currentSpotKind === ZLinkSpotKind.User &&
388
+ actor.currentSpotId === spot.spotId &&
389
+ actor.currentSpotGeneration === current.snapshot.objectGeneration;
390
+ }
391
+ }
392
+ }
393
+ if (unavailable || request?.actorRelocationPolicy !== 'disabled')
325
394
  throw new ZLinkFrameworkException(
326
395
  ZLinkFrameworkErrorKind.Unavailable,
327
- 'Actor owner lease is unavailable.'
396
+ 'Object owner lease is unavailable.'
328
397
  );
329
398
  } else if (
330
399
  this.validation.isTargetLive(
@@ -532,6 +601,17 @@ export class ZLinkInMemoryAuthorityStore {
532
601
  if (!this.creationTerminalAvailable(terminalRecord)) {
533
602
  return { kind: 'stale' };
534
603
  }
604
+ const actor =
605
+ request.completion.kind === 'created'
606
+ ? decodeActorAuthorityPayload(
607
+ serviceRelocationAuthorityApplicationPayload(request.completion.readyPayload)
608
+ )
609
+ : undefined;
610
+ if (
611
+ actor?.currentSpotKind === ZLinkSpotKind.User &&
612
+ !this.acceptsUserSpotMembership(actor.currentSpotId, actor.currentSpotGeneration)
613
+ )
614
+ return { kind: 'stale' };
535
615
  const nextVersion = this.tryNextStoreVersion();
536
616
  if (nextVersion === undefined) return { kind: 'generationExhausted' };
537
617
  const pending = row.snapshot.allocation;
@@ -698,6 +778,30 @@ export class ZLinkInMemoryAuthorityStore {
698
778
  ) {
699
779
  return { kind: 'stale' };
700
780
  }
781
+ const actor =
782
+ row.snapshot.allocation.objectKind === 'actor'
783
+ ? decodeActorAuthorityPayload(
784
+ serviceRelocationAuthorityApplicationPayload(participant.authorityPayload)
785
+ )
786
+ : undefined;
787
+ if (actor?.currentSpotKind === ZLinkSpotKind.User) {
788
+ const participantSpot = aggregate.request.participants.find(
789
+ (candidate) =>
790
+ candidate.authorityKey.value ===
791
+ encodeAuthorityKey('user_spot', actor.currentSpotId).value
792
+ );
793
+ if (participantSpot === undefined) {
794
+ if (!this.acceptsUserSpotMembership(actor.currentSpotId, actor.currentSpotGeneration))
795
+ return { kind: 'stale' };
796
+ } else {
797
+ const spot = this.rows.get(participantSpot.authorityKey.value);
798
+ if (
799
+ spot?.snapshot.allocation.objectKind !== 'user_spot' ||
800
+ spot.snapshot.objectGeneration !== actor.currentSpotGeneration
801
+ )
802
+ return { kind: 'stale' };
803
+ }
804
+ }
701
805
  if (participant.ownerTransition === 'newOwner') {
702
806
  if (
703
807
  !this.validation.isTargetLive(
@@ -1,4 +1,5 @@
1
1
  import type { RoutingId } from '../../contracts/Common';
2
+ import { ZLinkSpotKind } from '../../contracts';
2
3
  import { ZLINK_MAX_IDENTITY_TEXT_BYTES } from '../../contracts/Common/CoreTypes';
3
4
  import {
4
5
  isValidPublicWeight,
@@ -58,6 +59,7 @@ import {
58
59
  type ZLinkObjectCommitResult,
59
60
  type ZLinkObjectCreationCompleteRequest,
60
61
  type ZLinkObjectCreationCompleteResult,
62
+ type ZLinkEndedOwnerReleaseIntent,
61
63
  type ZLinkObjectReserveRequest,
62
64
  type ZLinkObjectReserveResult,
63
65
  type ZLinkOwnerLeaseClaimResult,
@@ -143,7 +145,18 @@ export class ZLinkInMemoryLocationStore
143
145
  typeCapacity.limit)))
144
146
  );
145
147
  },
146
- identityClaimed: (authorityKey) => this.entrySpotClaims.has(authorityKey)
148
+ identityClaimed: (authorityKey) => this.entrySpotClaims.has(authorityKey),
149
+ hasSpotMembers: (spotId, objectGeneration) => {
150
+ for (const actor of this.actors.rows.values()) {
151
+ if (
152
+ actor.spotKind === ZLinkSpotKind.User &&
153
+ String(actor.spotId) === spotId &&
154
+ actor.spotGeneration === objectGeneration
155
+ )
156
+ return true;
157
+ }
158
+ return false;
159
+ }
147
160
  },
148
161
  now
149
162
  );
@@ -205,9 +218,10 @@ export class ZLinkInMemoryLocationStore
205
218
  async releaseEndedReservation(
206
219
  key: ZLinkAuthorityKey,
207
220
  expectedStoreVersion: string,
208
- signal?: AbortSignal
221
+ signal?: AbortSignal,
222
+ request?: ZLinkEndedOwnerReleaseIntent
209
223
  ): Promise<boolean> {
210
- return this.authority.releaseEndedReservation(key, expectedStoreVersion, signal);
224
+ return this.authority.releaseEndedReservation(key, expectedStoreVersion, signal, request);
211
225
  }
212
226
 
213
227
  async abort(
@@ -705,6 +719,14 @@ export class ZLinkInMemoryLocationStore
705
719
  });
706
720
  const updatedAt = this.now();
707
721
  const current = this.actors.rows.get(key);
722
+ if (
723
+ actor.spotKind === ZLinkSpotKind.User &&
724
+ (current?.spotKind !== ZLinkSpotKind.User ||
725
+ String(current.spotId) !== String(actor.spotId) ||
726
+ current.spotGeneration !== actor.spotGeneration) &&
727
+ !this.authority.acceptsUserSpotMembership(String(actor.spotId), actor.spotGeneration)
728
+ )
729
+ return rejectedConflict();
708
730
  if (
709
731
  intent === ZLinkLocationWriteIntent.NewClaim &&
710
732
  current !== undefined &&
@@ -5,6 +5,10 @@ import type { ZLinkRelocationConfiguration } from '../../contracts/Configuration
5
5
  export interface ZLinkObjectReserveRequest extends ReserveRequest {
6
6
  readonly actorRelocationPolicy?: ZLinkRelocationConfiguration<unknown>['kind'];
7
7
  }
8
+ export type ZLinkEndedOwnerReleaseIntent = Pick<
9
+ ZLinkObjectReserveRequest,
10
+ 'key' | 'actorRelocationPolicy'
11
+ > & { readonly intent: Pick<ZLinkObjectReserveRequest['intent'], 'stableType'> };
8
12
  export type * from '../../contracts/Locations/Authority';
9
13
  export type {
10
14
  ZLinkActorLocation,