@zlink-systems/framework 0.22.0 → 0.24.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 (104) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
  3. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
  4. package/dist/runtime/actors/actor-context.js +4 -4
  5. package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
  6. package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
  7. package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
  8. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  9. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +4 -0
  10. package/dist/runtime/backend/node/node-raw-mesh-backend.js +24 -8
  11. package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
  12. package/dist/runtime/channels/channel-transports.js +3 -9
  13. package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
  14. package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
  15. package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
  16. package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
  17. package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
  18. package/dist/runtime/diagnostics/flow-context.js +2 -2
  19. package/dist/runtime/diagnostics/message-flow.js +11 -3
  20. package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
  21. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
  22. package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
  23. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
  24. package/dist/runtime/foundation/service-stateful-runtime.d.ts +5 -0
  25. package/dist/runtime/foundation/service-stateful-runtime.js +52 -2
  26. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
  27. package/dist/runtime/foundation/service-topology-registry.js +12 -4
  28. package/dist/runtime/host/index.js +7 -3
  29. package/dist/runtime/locations/in-memory-location-store.js +5 -0
  30. package/dist/runtime/locations/location-store-repository.js +3 -1
  31. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
  32. package/dist/runtime/spots/index.js +9 -3
  33. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
  34. package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
  35. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  36. package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
  37. package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
  38. package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
  39. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
  40. package/dist/runtime/spots/spot-timer.js +1 -1
  41. package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
  42. package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
  43. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
  44. package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
  45. package/dist/runtime/streams/bound-session-response-target.js +8 -1
  46. package/dist/runtime/streams/bound-session-service.d.ts +1 -0
  47. package/dist/runtime/streams/bound-session-service.js +6 -3
  48. package/dist/runtime/streams/index.js +8 -1
  49. package/dist/runtime/streams/managed-stream.d.ts +3 -0
  50. package/dist/runtime/streams/managed-stream.js +20 -1
  51. package/dist/runtime/streams/protocol.d.ts +5 -1
  52. package/dist/runtime/streams/protocol.js +41 -4
  53. package/dist/runtime/streams/session-actor-coordinator.js +3 -4
  54. package/dist/runtime/streams/session-context.d.ts +9 -2
  55. package/dist/runtime/streams/session-context.js +20 -4
  56. package/dist/runtime/streams/session-local-actors.d.ts +3 -1
  57. package/dist/runtime/streams/session-local-actors.js +17 -2
  58. package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
  59. package/dist/runtime/streams/stream-frame-factory.js +3 -2
  60. package/dist/runtime/streams/stream-session-runtime.js +42 -12
  61. package/package.json +3 -3
  62. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
  63. package/src/contracts/Streams/IZLinkSession.ts +2 -1
  64. package/src/runtime/actors/actor-context.ts +4 -4
  65. package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
  66. package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
  67. package/src/runtime/backend/contracts/index.ts +5 -0
  68. package/src/runtime/backend/node/node-raw-mesh-backend.ts +43 -11
  69. package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
  70. package/src/runtime/channels/channel-transports.ts +3 -9
  71. package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
  72. package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
  73. package/src/runtime/diagnostics/flow-context.ts +6 -4
  74. package/src/runtime/diagnostics/message-flow.ts +12 -3
  75. package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
  76. package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
  77. package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
  78. package/src/runtime/foundation/service-stateful-runtime.ts +71 -5
  79. package/src/runtime/foundation/service-topology-registry.ts +18 -5
  80. package/src/runtime/host/index.ts +11 -4
  81. package/src/runtime/locations/in-memory-location-store.ts +7 -0
  82. package/src/runtime/locations/location-store-repository.ts +4 -1
  83. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
  84. package/src/runtime/spots/index.ts +22 -2
  85. package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
  86. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  87. package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
  88. package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
  89. package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
  90. package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
  91. package/src/runtime/spots/spot-timer.ts +6 -1
  92. package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
  93. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
  94. package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
  95. package/src/runtime/streams/bound-session-response-target.ts +10 -1
  96. package/src/runtime/streams/bound-session-service.ts +13 -3
  97. package/src/runtime/streams/index.ts +8 -1
  98. package/src/runtime/streams/managed-stream.ts +34 -12
  99. package/src/runtime/streams/protocol.ts +44 -5
  100. package/src/runtime/streams/session-actor-coordinator.ts +12 -4
  101. package/src/runtime/streams/session-context.ts +31 -4
  102. package/src/runtime/streams/session-local-actors.ts +19 -3
  103. package/src/runtime/streams/stream-frame-factory.ts +4 -2
  104. package/src/runtime/streams/stream-session-runtime.ts +58 -13
@@ -303,9 +303,10 @@ class RawServiceMeshRuntime {
303
303
  return this.send(targetNodeRoutingId, [(0, service_wire_m6a_codec_1.encodeNodeSendHeader)(), this.applicationFrame(payload)]);
304
304
  }
305
305
  async sendToChannel(channelName, payload) {
306
- const selected = this.topology.selectChannel(channelName, (peer) => this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId));
307
- if (selected === undefined)
308
- return false;
306
+ const selection = this.selectChannelTarget(channelName);
307
+ if (selection.kind !== 'selected')
308
+ return selection.submitResult;
309
+ const selected = selection.peer;
309
310
  const applicationFrame = this.applicationFrame(payload);
310
311
  if (selected.descriptor.nodeRoutingId === this.descriptor.nodeRoutingId) {
311
312
  const applicationJobOwner = await this.reserveLocalIngress();
@@ -320,25 +321,49 @@ class RawServiceMeshRuntime {
320
321
  });
321
322
  if (!accepted)
322
323
  applicationJob.close();
323
- return accepted;
324
+ return accepted ? runtime_values_1.SubmitResult.Ok : runtime_values_1.SubmitResult.NotAdmitted;
324
325
  }
325
326
  finally {
326
327
  applicationJobOwner.close();
327
328
  }
328
329
  }
329
- return this.send(selected.descriptor.nodeRoutingId, [
330
+ return (await this.send(selected.descriptor.nodeRoutingId, [
330
331
  (0, service_wire_m6a_codec_1.encodeChannelSendHeader)(channelName),
331
332
  applicationFrame
332
- ]);
333
+ ]))
334
+ ? runtime_values_1.SubmitResult.Ok
335
+ : runtime_values_1.SubmitResult.NotConnected;
333
336
  }
334
337
  requestToNode(targetNodeRoutingId, payload, timeoutMs) {
335
338
  return this.requestToTarget(targetNodeRoutingId, payload, timeoutMs);
336
339
  }
337
340
  requestToChannel(channelName, payload, timeoutMs) {
341
+ const selection = this.selectChannelTarget(channelName);
342
+ if (selection.kind !== 'selected') {
343
+ const pending = this.operations.register(timeoutMs);
344
+ this.operations.complete(pending.id, selection.requestResult);
345
+ return pending;
346
+ }
347
+ return this.requestToTarget(selection.peer.descriptor.nodeRoutingId, payload, timeoutMs, channelName);
348
+ }
349
+ selectChannelTarget(channelName) {
338
350
  const selected = this.topology.selectChannel(channelName, (peer) => this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId));
339
- return selected === undefined
340
- ? undefined
341
- : this.requestToTarget(selected.descriptor.nodeRoutingId, payload, timeoutMs, channelName);
351
+ if (selected !== undefined)
352
+ return { kind: 'selected', peer: selected };
353
+ return this.topology.hasKnownChannelTarget(channelName)
354
+ ? {
355
+ kind: 'knownButNotReady',
356
+ submitResult: runtime_values_1.SubmitResult.NotConnected,
357
+ requestResult: { terminalResult: runtime_values_1.RequestResult.NotConnected, failureCode: 0 }
358
+ }
359
+ : {
360
+ kind: 'noMember',
361
+ submitResult: runtime_values_1.SubmitResult.NotFound,
362
+ requestResult: {
363
+ terminalResult: runtime_values_1.RequestResult.NotFound,
364
+ failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
365
+ }
366
+ };
342
367
  }
343
368
  isLocalOrReadyPeer(nodeRoutingId) {
344
369
  return nodeRoutingId === this.descriptor.nodeRoutingId || this.isPeerRouteReady(nodeRoutingId);
@@ -16,6 +16,7 @@ export interface ServiceSessionBindingAdmissionClaim {
16
16
  }
17
17
  export type ServiceSessionBindingAdmissionResult = 'passThrough' | 'retained' | 'rejected';
18
18
  export interface ServiceSessionBindingIngressPort {
19
+ actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
19
20
  retainOutbound(claim: ServiceSessionBindingAdmissionClaim, delivery: ServiceSessionBindingRetainedDelivery): Promise<ServiceSessionBindingAdmissionResult>;
20
21
  clearOutbound(actorId: string, error: unknown): Promise<void>;
21
22
  }
@@ -1,3 +1,4 @@
1
+ import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
1
2
  import type { RawServiceMeshRuntime } from './raw-service-mesh-runtime';
2
3
  import { type ReceiveKindData, type StreamSessionActorAuthorityFence } from './service-runtime-contracts';
3
4
  import type { ServiceMailboxRecord } from './service-mailbox';
@@ -221,6 +222,8 @@ export declare class ServiceStatefulRuntime {
221
222
  private userSpotOperationHandler?;
222
223
  private messageFollowHandler?;
223
224
  private mailboxDropHandler?;
225
+ private dispatchErrors?;
226
+ private dispatchErrorMeshName?;
224
227
  private readonly admittedUserSpotOperations;
225
228
  private readonly pendingInstanceActivations;
226
229
  private closed;
@@ -229,6 +232,7 @@ export declare class ServiceStatefulRuntime {
229
232
  readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
230
233
  readonly owner: string;
231
234
  }) => void): void;
235
+ setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void;
232
236
  createSpot(routingId?: string, kind?: 'user' | 'instance', stableType?: "user" | "instance"): ServiceSpotState;
233
237
  restoreUserSpotAuthority(spotId: string, stableType: string, generation: bigint, authorityOwnerGeneration: bigint): ServiceSpotState;
234
238
  restoreSpotAuthority(spotId: string, objectKind: 'user_spot' | 'instance_spot', stableType: string, generation: bigint, authorityOwnerGeneration: bigint): ServiceSpotState;
@@ -247,6 +251,7 @@ export declare class ServiceStatefulRuntime {
247
251
  readonly objectGeneration: bigint;
248
252
  } | undefined;
249
253
  forgetInstanceIntent(spotId: string, objectGeneration: bigint, authorityOwnerGeneration: bigint, storeVersion?: string): void;
254
+ completeClosedInstance(spotId: string, objectGeneration: bigint): void;
250
255
  registerInstanceActivationAuthority(authority: ServiceInstanceActivationAuthority): void;
251
256
  registerAsyncInstanceActivationAuthority(authority: ServiceAsyncInstanceActivationAuthority): void;
252
257
  registerInstanceApplicationLifecycle(lifecycle: ServiceInstanceApplicationLifecycle): void;
@@ -7,6 +7,7 @@ const abort_1 = require("../abort");
7
7
  const execution_1 = require("../execution");
8
8
  const framework_errors_internal_1 = require("../framework-errors-internal");
9
9
  const runtime_values_1 = require("../backend/runtime-values");
10
+ const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
10
11
  const service_runtime_contracts_1 = require("./service-runtime-contracts");
11
12
  const message_follow_suppression_registry_1 = require("./message-follow-suppression-registry");
12
13
  const service_stateful_registry_1 = require("./service-stateful-registry");
@@ -81,6 +82,8 @@ class ServiceStatefulRuntime {
81
82
  userSpotOperationHandler;
82
83
  messageFollowHandler;
83
84
  mailboxDropHandler;
85
+ dispatchErrors;
86
+ dispatchErrorMeshName;
84
87
  admittedUserSpotOperations = new Map();
85
88
  pendingInstanceActivations = new Map();
86
89
  closed = false;
@@ -95,6 +98,10 @@ class ServiceStatefulRuntime {
95
98
  setMailboxDropHandler(handler) {
96
99
  this.mailboxDropHandler = handler;
97
100
  }
101
+ setDispatchErrorReporter(reporter, meshName) {
102
+ this.dispatchErrors = reporter;
103
+ this.dispatchErrorMeshName = meshName;
104
+ }
98
105
  createSpot(routingId, kind = 'user', stableType = kind) {
99
106
  this.requireOpen();
100
107
  return this.registry.createSpot(routingId ?? `spot-${this.nodeRid}-${this.nextSpotId++}`, kind, stableType);
@@ -229,6 +236,12 @@ class ServiceStatefulRuntime {
229
236
  this.instanceIntents.delete(spotId);
230
237
  }
231
238
  }
239
+ completeClosedInstance(spotId, objectGeneration) {
240
+ const current = this.instanceIntents.get(spotId);
241
+ if (current !== undefined && current.route.objectGeneration === objectGeneration) {
242
+ this.forgetClosedInstanceRoute(current.route);
243
+ }
244
+ }
232
245
  registerInstanceActivationAuthority(authority) {
233
246
  this.requireOpen();
234
247
  if (this.asyncInstanceAuthority !== undefined ||
@@ -623,13 +636,32 @@ class ServiceStatefulRuntime {
623
636
  await this.enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload);
624
637
  const targets = this.raw.topology
625
638
  .peers()
626
- .filter((peer) => peer.descriptor.channels.some((channel) => channel.name === channelName && channel.weight > 0));
639
+ .filter((peer) => peer.descriptor.channels.some((channel) => channel.name === channelName && channel.weight > 0) && this.raw.isPeerRouteReady(peer.descriptor.nodeRoutingId));
627
640
  const header = (0, service_stateful_wire_codec_1.encodeLogicalMulticastHeader)(channelName, topic, sourceSpotId);
628
641
  const payloadFrame = (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload);
642
+ const reporter = this.dispatchErrors;
643
+ const captureFailures = reporter?.captureEnabled() === true;
629
644
  for (const target of targets) {
630
645
  // Remote admission ends at the source outbound transport queue. The
631
646
  // receiver's Spot queue and handler completion are not publish results.
632
- await this.raw.sendService(target.descriptor.nodeRoutingId, [header, payloadFrame]);
647
+ const accepted = await this.raw.sendService(target.descriptor.nodeRoutingId, [
648
+ header,
649
+ payloadFrame
650
+ ]);
651
+ if (!accepted) {
652
+ if (captureFailures) {
653
+ reporter.report({
654
+ surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.SpotRoute,
655
+ messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
656
+ reason: serviceSpotPublishFailureReason(undefined, this.raw.isPeerRouteReady(target.descriptor.nodeRoutingId), this.closed),
657
+ action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.Drop,
658
+ meshName: this.dispatchErrorMeshName,
659
+ channelName,
660
+ topic,
661
+ targetRid: target.descriptor.nodeRoutingId
662
+ });
663
+ }
664
+ }
633
665
  }
634
666
  }
635
667
  async sendToSpot(sourceSpotId, requested, payload) {
@@ -3504,6 +3536,24 @@ class ServiceStatefulRuntime {
3504
3536
  }
3505
3537
  }
3506
3538
  exports.ServiceStatefulRuntime = ServiceStatefulRuntime;
3539
+ function serviceSpotPublishFailureReason(result, routeReady, closed) {
3540
+ if (closed || result === runtime_values_1.SubmitResult.Terminated)
3541
+ return 'shutdown';
3542
+ switch (result) {
3543
+ case runtime_values_1.SubmitResult.Backpressured:
3544
+ return 'backpressure';
3545
+ case runtime_values_1.SubmitResult.NotAdmitted:
3546
+ case runtime_values_1.SubmitResult.NotConnected:
3547
+ case runtime_values_1.SubmitResult.NotFound:
3548
+ case runtime_values_1.SubmitResult.InvalidHandle:
3549
+ return 'stale_target';
3550
+ default:
3551
+ // RawServiceMeshRuntime exposes failed Core submissions as false. A
3552
+ // ready route therefore identifies queue admission failure; a route
3553
+ // that became unready after selection is a stale target.
3554
+ return routeReady ? 'backpressure' : 'stale_target';
3555
+ }
3556
+ }
3507
3557
  function isEntrySpotFence(fence) {
3508
3558
  return (fence.spot.spotId === fence.targetNodeRid &&
3509
3559
  fence.spot.generation === fence.targetNodeGeneration &&
@@ -55,6 +55,7 @@ export declare class ServiceTopologyRegistry {
55
55
  markNotRequired(descriptor: ServiceNodeDescriptor): void;
56
56
  forgetNotRequired(nodeRoutingId: string): void;
57
57
  knownDescriptor(nodeRoutingId: string): ServiceNodeDescriptor | undefined;
58
+ hasKnownChannelTarget(channelName: string): boolean;
58
59
  private remember;
59
60
  selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
60
61
  selectPlacement(isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
@@ -170,6 +170,10 @@ class ServiceTopologyRegistry {
170
170
  const descriptor = admitted ?? this.notRequiredByRid.get(nodeRoutingId) ?? this.knownByRid.get(nodeRoutingId);
171
171
  return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
172
172
  }
173
+ hasKnownChannelTarget(channelName) {
174
+ requireText(channelName, 'channelName');
175
+ return [this.local, ...this.knownByRid.values()].some((descriptor) => selectableChannel(descriptor, channelName) !== undefined);
176
+ }
173
177
  remember(descriptor) {
174
178
  const current = this.knownByRid.get(descriptor.nodeRoutingId);
175
179
  if (current === undefined ||
@@ -190,11 +194,9 @@ class ServiceTopologyRegistry {
190
194
  return [local, ...this.peersByRid.values()]
191
195
  .map((peer) => ({
192
196
  peer: clonePeer(peer),
193
- channel: findChannel(peer.descriptor, channelName)
197
+ channel: selectableChannel(peer.descriptor, channelName)
194
198
  }))
195
- .filter((value) => value.channel !== undefined &&
196
- value.peer.descriptor.state === 'serving' &&
197
- value.channel.weight > 0);
199
+ .filter((value) => value.channel !== undefined);
198
200
  }, (value) => value.channel.weight, (value) => value.peer.descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.peer.descriptor.nodeRoutingId, right.peer.descriptor.nodeRoutingId), (value) => isReady(value.peer))?.peer;
199
201
  }
200
202
  selectPlacement(isReady = () => true) {
@@ -338,6 +340,12 @@ function validateSortedUnique(values, key, field) {
338
340
  function findChannel(descriptor, channelName) {
339
341
  return descriptor.channels.find((channel) => channel.name === channelName);
340
342
  }
343
+ function selectableChannel(descriptor, channelName) {
344
+ if (descriptor.state !== 'serving')
345
+ return undefined;
346
+ const channel = findChannel(descriptor, channelName);
347
+ return channel !== undefined && channel.weight > 0 ? channel : undefined;
348
+ }
341
349
  function requireText(value, field) {
342
350
  if (value.length === 0 || value.includes('\0')) {
343
351
  throw new TypeError(`${field} must be non-empty text without NUL.`);
@@ -1410,8 +1410,12 @@ class ZLinkFrameworkRuntimeHost {
1410
1410
  // coordinator after application initialization. The legacy location
1411
1411
  // claim would expose a Creating object before that Ready barrier.
1412
1412
  locationLifecycle: () => undefined,
1413
- releaseInstanceAuthority: (meshName, spotId, objectGeneration) => this.locationOwner.currentLifecycle?.releaseSpot(meshName, spotId, objectGeneration) ??
1414
- Promise.resolve(),
1413
+ releaseInstanceAuthority: async (meshName, spotId, objectGeneration) => {
1414
+ await this.locationOwner.currentLifecycle?.releaseSpot(meshName, spotId, objectGeneration);
1415
+ this.spotNodeRuntime
1416
+ ?.meshNode(meshName)
1417
+ ?.completeClosedInstance?.(spotId, objectGeneration);
1418
+ },
1415
1419
  beginInstanceIdleClosingAuthority: (meshName, spotId) => this.locationOwner.currentLifecycle?.beginInstanceSpotClosing(meshName, spotId) ??
1416
1420
  Promise.resolve(undefined),
1417
1421
  beginInstanceClosingAuthority: (meshName, spotId) => this.locationOwner.currentLifecycle?.beginInstanceSpotClosing(meshName, spotId) ??
@@ -2178,7 +2182,7 @@ class ZLinkFrameworkRuntimeHost {
2178
2182
  const current = (0, diagnostics_1.currentOrCreateFlow)('Lifecycle', false);
2179
2183
  const flow = current?.flowOrigin === 'Lifecycle'
2180
2184
  ? current
2181
- : (0, diagnostics_1.createInboundFlow)(undefined, 'Lifecycle', this.flowCreationEnabled());
2185
+ : (0, diagnostics_1.createInboundFlow)(undefined, 'Lifecycle', this.flowCreationEnabled(), undefined);
2182
2186
  return (0, diagnostics_1.runWithFlow)(flow, operation);
2183
2187
  }
2184
2188
  requirePrimaryMeshNode() {
@@ -134,6 +134,11 @@ class ZLinkInMemoryLocationStore {
134
134
  meshNodeDescriptorFingerprint(current) === meshNodeDescriptorFingerprint(descriptor)) {
135
135
  return stored(this.meshNodes.generations.get(key) ?? 1n, current.updatedAt);
136
136
  }
137
+ if (intent === internal_location_contracts_1.ZLinkLocationWriteIntent.NewClaim &&
138
+ currentLease !== undefined &&
139
+ currentLease.leaseExpiresAt.getTime() > updatedAt.getTime()) {
140
+ return rejectedConflict();
141
+ }
137
142
  if (current.ownerId !== descriptor.ownerId ||
138
143
  current.leaseGeneration !== descriptor.leaseGeneration ||
139
144
  current.lifecycleGeneration !== descriptor.lifecycleGeneration ||
@@ -1675,7 +1675,9 @@ class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMem
1675
1675
  const renew = intent === 2 && canRenew(stored, descriptor);
1676
1676
  if (!takeover && !renew) {
1677
1677
  return {
1678
- status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
1678
+ status: intent === internal_location_contracts_1.ZLinkLocationWriteIntent.NewClaim && currentLeaseGeneration !== undefined
1679
+ ? Locations_1.ZLinkLocationWriteStatus.RejectedConflict
1680
+ : Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
1679
1681
  generation,
1680
1682
  updatedAt: current.value.storeNow
1681
1683
  };
@@ -66,22 +66,24 @@ function read64(bytes, at) { if (at.value + 8 > bytes.length)
66
66
  r = (r << 8n) | BigInt(bytes[at.value++]); return r; }
67
67
  function u32(out, value) { if (!Number.isInteger(value) || value < 0 || value > 0xffffffff)
68
68
  throw new RangeError("u32"); out.push(value >>> 24, (value >>> 16) & 255, (value >>> 8) & 255, value & 255); }
69
+ function appendBytes(out, bytes) { for (const byte of bytes)
70
+ out.push(byte); }
69
71
  function read32(bytes, at) { if (at.value + 4 > bytes.length)
70
72
  throw new RangeError("truncated u32"); return ((bytes[at.value++] << 24) | (bytes[at.value++] << 16) | (bytes[at.value++] << 8) | bytes[at.value++]) >>> 0; }
71
73
  function text8(out, value) { const b = text.encode(value); if (b.length === 0 || b.length > 255 || b.includes(0))
72
- throw new RangeError("text8"); out.push(b.length, ...b); }
74
+ throw new RangeError("text8"); out.push(b.length); appendBytes(out, b); }
73
75
  function readText8(bytes, at) { const n = bytes[at.value++]; if (n === 0 || at.value + n > bytes.length)
74
76
  throw new RangeError("truncated text8"); const b = bytes.slice(at.value, at.value + n); at.value += n; if (b.includes(0))
75
77
  throw new RangeError("text8 NUL"); return decodeText.decode(b); }
76
78
  function rid(out, value) { if (value.length === 0 || value.length > 255)
77
- throw new RangeError("rid"); out.push(value.length, ...value); }
79
+ throw new RangeError("rid"); out.push(value.length); appendBytes(out, value); }
78
80
  function readRid(bytes, at) { const n = bytes[at.value++]; if (n === 0 || at.value + n > bytes.length)
79
81
  throw new RangeError("truncated rid"); const r = bytes.slice(at.value, at.value + n); at.value += n; return r; }
80
82
  function fence(out, value) { if (value.generation === 0n || value.targetNodeGeneration === 0n || value.expectedAuthorityOwnerGeneration === 0n || value.expectedOwnerLeaseGeneration === 0n)
81
83
  throw new RangeError("invalid fence"); text8(out, value.id); u64(out, value.generation); rid(out, value.targetNodeRid); u64(out, value.targetNodeGeneration); u64(out, value.expectedAuthorityOwnerGeneration); u64(out, value.expectedOwnerLeaseGeneration); }
82
84
  function readFence(bytes, at) { const value = { id: readText8(bytes, at), generation: read64(bytes, at), targetNodeRid: readRid(bytes, at), targetNodeGeneration: read64(bytes, at), expectedAuthorityOwnerGeneration: read64(bytes, at), expectedOwnerLeaseGeneration: read64(bytes, at) }; if (value.generation === 0n || value.targetNodeGeneration === 0n || value.expectedAuthorityOwnerGeneration === 0n || value.expectedOwnerLeaseGeneration === 0n)
83
85
  throw new RangeError("invalid fence"); return value; }
84
- function encodePayload(value) { const body = []; text8(body, value.packetName); text8(body, value.contentType); u32(body, value.payload.length); body.push(...value.payload); const out = [1]; u32(out, body.length); out.push(...body); return Uint8Array.from(out); }
86
+ function encodePayload(value) { const body = []; text8(body, value.packetName); text8(body, value.contentType); u32(body, value.payload.length); appendBytes(body, value.payload); const out = [1]; u32(out, body.length); appendBytes(out, body); return Uint8Array.from(out); }
85
87
  function decodePayload(bytes) { const at = { value: 0 }; if (bytes[at.value++] !== 1)
86
88
  throw new RangeError("payload version"); const length = read32(bytes, at); if (length !== bytes.length - at.value)
87
89
  throw new RangeError("payload length"); const end = bytes.length; const packetName = readText8(bytes, at); const contentType = readText8(bytes, at); const length2 = read32(bytes, at); if (length2 !== end - at.value)
@@ -93,10 +95,10 @@ function decodeActorJoin28(frames) { if (frames.length < 1 || frames.length > 2)
93
95
  throw new RangeError("actorJoin frames"); const bytes = frames[0]; const at = { value: 0 }; if (bytes.length < 5 || bytes[at.value++] !== 90 || bytes[at.value++] !== 77 || bytes[at.value++] !== 1 || bytes[at.value++] !== 28 || bytes[at.value++] !== 0)
94
96
  throw new RangeError("actorJoin header"); const correlation = read64(bytes, at); const actor = readFence(bytes, at); const entry = bytes[at.value++]; const targetSpot = readFence(bytes, at); if (correlation === 0n || entry > 1 || at.value !== bytes.length)
95
97
  throw new RangeError("actorJoin body"); const payload = frames.length === 2 ? decodePayload(frames[1]) : undefined; return payload === undefined ? { correlation, actor, entry: entry === 1, targetSpot } : { correlation, actor, entry: entry === 1, targetSpot, payload }; }
96
- function bytes64(out, v) { u64(out, BigInt(v.length)); out.push(...v); }
98
+ function bytes64(out, v) { u64(out, BigInt(v.length)); appendBytes(out, v); }
97
99
  function body16(out, write) { const b = []; write(b); if (b.length > 65535)
98
- throw new RangeError("body16"); out.push(b.length >>> 8, b.length & 255, ...b); }
99
- function body64(out, write) { const b = []; write(b); u64(out, BigInt(b.length)); out.push(...b); }
100
+ throw new RangeError("body16"); out.push(b.length >>> 8, b.length & 255); appendBytes(out, b); }
101
+ function body64(out, write) { const b = []; write(b); u64(out, BigInt(b.length)); appendBytes(out, b); }
100
102
  function sorted(v, compare, name) { for (let i = 1; i < v.length; i++)
101
103
  if (compare(v[i - 1], v[i]) >= 0)
102
104
  throw new RangeError(name); }
@@ -112,7 +114,7 @@ function readText16(bytes, at, minimum, maximum, name) { const n = read16(bytes,
112
114
  throw new RangeError(name); const b = take(bytes, at, n, name); if (b.includes(0))
113
115
  throw new RangeError(name + " NUL"); return decodeText.decode(b); }
114
116
  function writeText16(out, value, minimum, maximum, name) { const b = text.encode(value); if (b.length < minimum || b.length > maximum || b.includes(0))
115
- throw new RangeError(name); out.push(b.length >>> 8, b.length & 255, ...b); }
117
+ throw new RangeError(name); out.push(b.length >>> 8, b.length & 255); appendBytes(out, b); }
116
118
  function boundedEnd(bytes, at, length, name) { if (at.value + length > bytes.length)
117
119
  throw new RangeError("truncated " + name); return at.value + length; }
118
120
  function exactEnd(at, end, name) { if (at.value !== end)
@@ -305,7 +307,7 @@ function encodeRelocationEnvelopeV1(value) { const out = []; if (value.relocatio
305
307
  exactEnd(at, w.frozenRecord.length, "frozen record");
306
308
  u64(out, w.participantId);
307
309
  u64(out, w.order);
308
- out.push(...w.frozenRecord);
310
+ appendBytes(out, w.frozenRecord);
309
311
  } sorted(value.timerRegistrations, (a, b) => a.participantId === b.participantId ? compareUtf8(a.name, b.name) : a.participantId < b.participantId ? -1 : 1, "timerRegistrations"); u32(out, value.timerRegistrations.length); for (const t of value.timerRegistrations) {
310
312
  if (t.participantId === 0n || t.periodMilliseconds === 0n || t.maxCatchUpTicks === 0n || t.overrunPolicy < 1 || t.overrunPolicy > 3)
311
313
  throw new RangeError("timer");
@@ -385,17 +387,17 @@ function crc32c(bytes) { let crc = 0xffffffff; for (const octet of bytes) {
385
387
  for (let bit = 0; bit < 8; bit++)
386
388
  crc = (crc >>> 1) ^ ((crc & 1) !== 0 ? 0x82f63b78 : 0);
387
389
  } return (~crc) >>> 0; }
388
- function encodeDurable(magic, body, maximum) { const out = [...magic, 1, 0, 0]; u32(out, body.length); out.push(...body); if (out.length + 4 > maximum)
390
+ function encodeDurable(magic, body, maximum) { const out = [...magic, 1, 0, 0]; u32(out, body.length); appendBytes(out, body); if (out.length + 4 > maximum)
389
391
  throw new RangeError("durable format bytes"); u32(out, crc32c(Uint8Array.from(out))); return Uint8Array.from(out); }
390
392
  function decodeDurable(bytes, magic, maximum) { if (bytes.length > maximum || bytes.length < 15 || magic.some((v, i) => bytes[i] !== v) || bytes[4] !== 1 || bytes[5] !== 0 || bytes[6] !== 0)
391
393
  throw new RangeError("durable header"); const at = { value: 7 }, length = read32(bytes, at), end = boundedEnd(bytes, at, length, "durable body"); if (end + 4 !== bytes.length)
392
394
  throw new RangeError("durable length"); const body = bytes.slice(at.value, end); at.value = end; const expected = read32(bytes, at); if (crc32c(bytes.slice(0, end)) !== expected)
393
395
  throw new RangeError("durable checksum"); return body; }
394
396
  function encodeRelocationDataChunkV1(value) { const body = []; u32(body, value.order); if (value.data.length > 67108864)
395
- throw new RangeError("chunk data"); u32(body, value.data.length); body.push(...value.data); return encodeDurable([90, 76, 84, 67], body, 67108886); }
397
+ throw new RangeError("chunk data"); u32(body, value.data.length); appendBytes(body, value.data); return encodeDurable([90, 76, 84, 67], body, 67108886); }
396
398
  function decodeRelocationDataChunkV1(bytes) { const body = decodeDurable(bytes, [90, 76, 84, 67], 67108886), at = { value: 0 }, order = read32(body, at), data = take(body, at, read32(body, at), "chunk data"); exactEnd(at, body.length, "chunk"); return { order, data }; }
397
399
  function encodeRelocationManifestV1(value) { if (Number(value.logicalFormatVersion) !== 1 || value.totalLength > 274877906944n || value.inventoryDigestSha256.length !== 32 || value.chunks.length > 4096)
398
- throw new RangeError("manifest"); sorted(value.chunks, (a, b) => a.order - b.order, "manifest chunks"); const body = [1]; u64(body, value.totalLength); u32(body, value.totalChecksumCrc32c); body.push(32, ...value.inventoryDigestSha256); u32(body, value.chunks.length); for (const chunk of value.chunks) {
400
+ throw new RangeError("manifest"); sorted(value.chunks, (a, b) => a.order - b.order, "manifest chunks"); const body = [1]; u64(body, value.totalLength); u32(body, value.totalChecksumCrc32c); body.push(32); appendBytes(body, value.inventoryDigestSha256); u32(body, value.chunks.length); for (const chunk of value.chunks) {
399
401
  if (chunk.length === 0n)
400
402
  throw new RangeError("chunk length");
401
403
  u32(body, chunk.order);
@@ -568,14 +570,14 @@ function b3WriteSourceBase(o, v) { b3WriteId(o, v.relocation, "relocation"); if
568
570
  throw new RangeError("sender role"); writeRelocationObject(o, v.object); }
569
571
  function b3ReadSourceBase(bytes, at) { const relocation = b3ReadId(bytes, at, "relocation"), targetAttemptGeneration = nonzero64(bytes, at, "target attempt"), coordinator = b3ReadCoordinator(bytes, at), senderRole = b3ReadRole(bytes, at); if (senderRole !== "source")
570
572
  throw new RangeError("sender role"); return { relocation, targetAttemptGeneration, coordinator, senderRole, object: readRelocationObject(bytes, at) }; }
571
- function encodeRelocationData31(v) { const o = b3Header(31); b3WriteSourceBase(o, v); const at = { value: 0 }; skipFrozenRecord(v.record, at); exactEnd(at, v.record.length, "frozen record"); o.push(...v.record); return Uint8Array.from(o); }
573
+ function encodeRelocationData31(v) { const o = b3Header(31); b3WriteSourceBase(o, v); const at = { value: 0 }; skipFrozenRecord(v.record, at); exactEnd(at, v.record.length, "frozen record"); appendBytes(o, v.record); return Uint8Array.from(o); }
572
574
  function decodeRelocationData31(bytes) { const at = b3ReadHeader(bytes, 31, "relocationData"), v = b3ReadSourceBase(bytes, at), start = at.value; skipFrozenRecord(bytes, at); const record = bytes.slice(start, at.value); b3Finish(at, bytes, "relocationData"); return { ...v, record }; }
573
575
  function encodeRelocationCutover34(v) { const o = b3Header(34); b3WriteSourceBase(o, v); if (v.boundaryRecordCount < 0n || v.boundaryRecordCount > 9223372036854775807n)
574
576
  throw new RangeError("boundary count"); u64(o, v.boundaryRecordCount); u32(o, v.boundaryChecksumCrc32c); return Uint8Array.from(o); }
575
577
  function decodeRelocationCutover34(bytes) { const at = b3ReadHeader(bytes, 34, "relocationCutover"), v = b3ReadSourceBase(bytes, at), boundaryRecordCount = read64(bytes, at); if (boundaryRecordCount > 9223372036854775807n)
576
578
  throw new RangeError("boundary count"); const boundaryChecksumCrc32c = read32(bytes, at); b3Finish(at, bytes, "relocationCutover"); return { ...v, boundaryRecordCount, boundaryChecksumCrc32c }; }
577
579
  function encodeRelocationState52(v) { const o = b3Header(52); b3WriteSourceBase(o, v); if (v.chunkData.length > 67108864)
578
- throw new RangeError("chunk data"); u32(o, v.chunkOrdinal); u32(o, v.chunkData.length); o.push(...v.chunkData); return Uint8Array.from(o); }
580
+ throw new RangeError("chunk data"); u32(o, v.chunkOrdinal); u32(o, v.chunkData.length); appendBytes(o, v.chunkData); return Uint8Array.from(o); }
579
581
  function decodeRelocationState52(bytes) { const at = b3ReadHeader(bytes, 52, "relocationState"), v = b3ReadSourceBase(bytes, at), chunkOrdinal = read32(bytes, at), length = read32(bytes, at); if (length > 67108864)
580
582
  throw new RangeError("chunk data"); const chunkData = take(bytes, at, length, "chunk data"); b3Finish(at, bytes, "relocationState"); return { ...v, chunkOrdinal, chunkData }; }
581
583
  function encodeRelocationPrepare40(v) { const o = b3Header(40); b3WriteId(o, v.relocation, "relocation"); if (v.targetAttemptGeneration === 0n || v.sourceNodeGeneration === 0n)
@@ -615,7 +617,7 @@ function b4ReadReservation(bytes, at) { return { reservationId: readText8(bytes,
615
617
  throw new RangeError("pending capacity"); return v; })() }; }
616
618
  function b4Close(out, v) { const body = []; text8(body, v.spotId); if (v.objectGeneration === 0n || v.targetNodeGeneration === 0n || v.expectedAuthorityOwnerGeneration === 0n)
617
619
  throw new RangeError("close fence"); u64(body, v.objectGeneration); rid(body, v.targetNodeRid); u64(body, v.targetNodeGeneration); u64(body, v.expectedAuthorityOwnerGeneration); writeText16(body, v.expectedStoreVersion, 1, 4096, "store version"); out.push(1); if (body.length > 65535)
618
- throw new RangeError("close fence"); out.push(body.length >>> 8, body.length & 255, ...body); }
620
+ throw new RangeError("close fence"); out.push(body.length >>> 8, body.length & 255); appendBytes(out, body); }
619
621
  function b4ReadClose(bytes, at) { if (read8(bytes, at) !== 1)
620
622
  throw new RangeError("close fence version"); const end = boundedEnd(bytes, at, read16(bytes, at), "close fence"); const value = { spotId: readText8(bytes, at), objectGeneration: nonzero64(bytes, at, "object generation"), targetNodeRid: readRid(bytes, at), targetNodeGeneration: nonzero64(bytes, at, "target node generation"), expectedAuthorityOwnerGeneration: nonzero64(bytes, at, "authority generation"), expectedStoreVersion: readText16(bytes, at, 1, 4096, "store version") }; exactEnd(at, end, "close fence"); return value; }
621
623
  function b4CommandBase(out, v) { if (v.correlation === 0n || v.sourceNodeGeneration === 0n)
@@ -630,14 +632,14 @@ function decodeUserSpotClose48(bytes) { const at = b4ReadHeader(bytes, 48, "user
630
632
  function encodeActorCreate49(v) { const out = b4Header(49); b4CommandBase(out, v); text8(out, v.actorId); text8(out, v.stableType); b4Reservation(out, v.reservation); if (v.deadlineUnixMs === 0n)
631
633
  throw new RangeError("deadline"); u64(out, v.deadlineUnixMs); return Uint8Array.from(out); }
632
634
  function decodeActorCreate49(bytes) { const at = b4ReadHeader(bytes, 49, "actorCreate"), base = b4ReadCommandBase(bytes, at), actorId = readText8(bytes, at), stableType = readText8(bytes, at), reservation = b4ReadReservation(bytes, at), deadlineUnixMs = nonzero64(bytes, at, "deadline"); b4Finish(at, bytes, "actorCreate"); return { ...base, actorId, stableType, reservation, deadlineUnixMs }; }
633
- function b4ZljrPayload(v) { if (v.metadata.length > 262144 || v.request.length > 1048576 || v.reply.length > 1048576)
634
- throw new RangeError("ZLJR length"); const out = [90, 76, 74, 82, 1]; u32(out, v.metadata.length); u32(out, v.request.length); u32(out, v.reply.length); out.push(...v.metadata, ...v.request, ...v.reply); return Uint8Array.from(out); }
635
+ function b4ZljrPayload(v) { if (v.metadata.length > 262144)
636
+ throw new RangeError("ZLJR length"); const out = [90, 76, 74, 82, 1]; u32(out, v.metadata.length); u32(out, v.request.length); u32(out, v.reply.length); appendBytes(out, v.metadata); appendBytes(out, v.request); appendBytes(out, v.reply); return Uint8Array.from(out); }
635
637
  function b4ReadZljrPayload(bytes) { const at = { value: 0 }; if (read32(bytes, at) !== 0x5a4c4a52 || read8(bytes, at) !== 1)
636
- throw new RangeError("ZLJR header"); const metadataLength = read32(bytes, at), requestLength = read32(bytes, at), replyLength = read32(bytes, at); if (metadataLength > 262144 || requestLength > 1048576 || replyLength > 1048576)
638
+ throw new RangeError("ZLJR header"); const metadataLength = read32(bytes, at), requestLength = read32(bytes, at), replyLength = read32(bytes, at); if (metadataLength > 262144)
637
639
  throw new RangeError("ZLJR length"); const metadata = take(bytes, at, metadataLength, "ZLJR metadata"), request = take(bytes, at, requestLength, "ZLJR request"), reply = take(bytes, at, replyLength, "ZLJR reply"); b4Finish(at, bytes, "ZLJR"); return { metadata, request, reply }; }
638
640
  function encodeZljrRecordV1(v) { if (v.source.nodeGeneration === 0n || v.source.ownerLeaseGeneration === 0n)
639
641
  throw new RangeError("ZLJR record"); const source = []; rid(source, v.source.nodeRid); u64(source, v.source.nodeGeneration); text8(source, v.source.ownerId); u64(source, v.source.ownerLeaseGeneration); if (source.length > 65535)
640
- throw new RangeError("ZLJR source"); const payload = b4ZljrPayload(v), body = []; text8(body, "__zlink.actor.routed_join.recovery"); text8(body, "application/x-zlink-actor-routed-join-recovery-v1"); u32(body, payload.length); body.push(...payload); const out = [1, 1, source.length >>> 8, source.length & 255, ...source, 0]; u64(out, v.operation.high); u64(out, v.operation.low); u32(out, 0); out.push(0, 0, 1); u32(out, body.length); out.push(...body); return Uint8Array.from(out); }
642
+ throw new RangeError("ZLJR source"); const payload = b4ZljrPayload(v), body = []; text8(body, "__zlink.actor.routed_join.recovery"); text8(body, "application/x-zlink-actor-routed-join-recovery-v1"); u32(body, payload.length); appendBytes(body, payload); const out = [1, 1, source.length >>> 8, source.length & 255, ...source, 0]; u64(out, v.operation.high); u64(out, v.operation.low); u32(out, 0); out.push(0, 0, 1); u32(out, body.length); appendBytes(out, body); return Uint8Array.from(out); }
641
643
  function decodeZljrRecordV1(bytes) { const at = { value: 0 }; if (read8(bytes, at) !== 1 || read8(bytes, at) !== 1)
642
644
  throw new RangeError("ZLJR frozen record"); const sourceEnd = boundedEnd(bytes, at, read16(bytes, at), "ZLJR source"), nodeRid = readRid(bytes, at), nodeGeneration = nonzero64(bytes, at, "source node generation"), ownerId = readText8(bytes, at), ownerLeaseGeneration = nonzero64(bytes, at, "source owner lease"); exactEnd(at, sourceEnd, "ZLJR source"); if (read8(bytes, at) !== 0)
643
645
  throw new RangeError("ZLJR metadata flag"); const operation = { high: read64(bytes, at), low: read64(bytes, at) }; if (read32(bytes, at) !== 0 || read16(bytes, at) !== 0 || read8(bytes, at) !== 1)
@@ -231,6 +231,7 @@ class DefaultZLinkSpotManager {
231
231
  await activation.nativeSpot?.dispose();
232
232
  }
233
233
  async materializeInstance(meshName, instanceType, spotId, objectGeneration, signal) {
234
+ const readyTarget = this.options.instanceSpotApplicationTargetProvider?.(meshName, spotId);
234
235
  const existing = this.pendingInstanceMaterializations.get(instanceMaterializationKey(meshName, spotId, objectGeneration));
235
236
  if (existing !== undefined) {
236
237
  await (0, abort_1.awaitWithAbort)(existing, signal);
@@ -238,18 +239,23 @@ class DefaultZLinkSpotManager {
238
239
  }
239
240
  const release = await this.acquireInstanceActivation(meshName, signal);
240
241
  try {
241
- await this.materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, signal);
242
+ await this.materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, readyTarget?.objectGeneration === objectGeneration, signal);
242
243
  }
243
244
  finally {
244
245
  release();
245
246
  }
246
247
  }
247
- async materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, signal) {
248
+ async materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, requiresReadyTarget, signal) {
248
249
  const factory = this.requireInstanceFactory(meshName, instanceType);
249
250
  const key = `${meshName}\0${String(spotId)}`;
250
251
  const materializationKey = instanceMaterializationKey(meshName, spotId, objectGeneration);
251
252
  const materializationPrefix = instanceMaterializationPrefix(meshName, spotId);
252
253
  for (;;) {
254
+ if (requiresReadyTarget &&
255
+ this.options.instanceSpotApplicationTargetProvider?.(meshName, spotId)?.objectGeneration !==
256
+ objectGeneration) {
257
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotGenerationStale, `Instance Spot '${String(spotId)}' no longer has its Ready application target.`);
258
+ }
253
259
  // A close seals admission before cleanup removes the activation. An
254
260
  // explicit Instance intent arriving at that boundary must wait for the
255
261
  // old resources to be released before materializing the replacement.
@@ -907,7 +913,7 @@ class DefaultZLinkSpotManager {
907
913
  throw new configuration_1.ZLinkConfigurationException('MeshNode Instance Spot record is missing the owner Spot RID.');
908
914
  }
909
915
  const envelope = (0, channel_envelope_1.decodeChannelEnvelope)(record.parts, undefined, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
910
- const inboundFlow = (0, diagnostics_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
916
+ const inboundFlow = (0, diagnostics_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined);
911
917
  await (0, diagnostics_1.runWithFlow)(inboundFlow, async () => {
912
918
  const request = record.operationKind === service_runtime_contracts_1.OperationKind.InstanceSpotRequest;
913
919
  this.traceInstanceMessage(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Received, meshName, spotId, record, envelope, 'activating');
@@ -1,4 +1,4 @@
1
- import type { ActorRef, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
1
+ import type { ActorRef, RoutingId, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
2
2
  import type { Message } from '../../contracts/Common/Message';
3
3
  import type { ZLinkRemoteBoundSessionTarget } from '../actors';
4
4
  import { ZLinkSpotActorHandlerRegistryRuntime } from '../actors';
@@ -24,6 +24,7 @@ export interface ZLinkActorPacketDelivery {
24
24
  readonly fallbackActorRef?: ActorRef;
25
25
  readonly requestTerminal?: ZLinkActorRequestTerminal;
26
26
  readonly messageFollowOrigin?: ZLinkMessageFollowOrigin;
27
+ readonly sourceSessionRid?: RoutingId;
27
28
  }
28
29
  /**
29
30
  * Spec 15 §4.2 relocation temporary queue: consulted when {@code
@@ -6,6 +6,7 @@ const contracts_1 = require("../../contracts");
6
6
  const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
7
7
  const diagnostics_1 = require("../diagnostics");
8
8
  const flow_context_1 = require("../diagnostics/flow-context");
9
+ const managed_stream_1 = require("../streams/managed-stream");
9
10
  const actors_1 = require("../actors");
10
11
  const protocol_1 = require("../streams/protocol");
11
12
  const payload_codec_1 = require("../messaging/payload-codec");
@@ -48,7 +49,11 @@ class ZLinkSpotActorPacketDispatch {
48
49
  this.reportInvalidFrame(actorId, ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorSend, error);
49
50
  throw error;
50
51
  }
51
- return (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(header.flowId, header.flowOrigin, flowEnabled), async () => {
52
+ return (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(header.flowId, header.flowOrigin, flowEnabled, !flowEnabled
53
+ ? undefined
54
+ : delivery.sourceSessionRid === undefined
55
+ ? (0, flow_context_1.currentFlowContext)()?.streamSessionId
56
+ : (0, managed_stream_1.streamSessionIdFromRoutingId)(delivery.sourceSessionRid)), async () => {
52
57
  const messageKind = header.kind === protocol_1.ZLinkStreamMessageKind.Request
53
58
  ? ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorRequest
54
59
  : ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorSend;
@@ -86,6 +86,7 @@ class ZLinkSpotActorPacketDrain {
86
86
  actorId,
87
87
  parts,
88
88
  returnResponse: false,
89
+ sourceSessionRid,
89
90
  fallbackActorRef: actorRef
90
91
  });
91
92
  }
@@ -133,6 +134,7 @@ class ZLinkSpotActorPacketDrain {
133
134
  actorId,
134
135
  parts,
135
136
  returnResponse: true,
137
+ sourceSessionRid: info.sourceSessionRid,
136
138
  fallbackActorRef: actorRef
137
139
  });
138
140
  this.options.replyActorNoBind?.(info, [this.encodeActorReplyFrame(parts[0], protocol_1.ZLinkStreamMessageKind.Response, response)], runtime_values_1.RequestResult.Ok);
@@ -143,6 +143,9 @@ class ZLinkSpotNodeRuntimeManager {
143
143
  applicationJobReceiveFlowFailureSink: this.options.applicationJobReceiveFlowFailureSink,
144
144
  peerAdmissionSealed: () => this.options.peerAdmissionSealed?.(spotNodeName) ?? false
145
145
  });
146
+ if (this.options.dispatchErrors !== undefined) {
147
+ node.setDispatchErrorReporter?.(this.options.dispatchErrors, spotNodeName);
148
+ }
146
149
  node.setMailboxRecordDroppedHandler?.((record) => this.options.dispatchErrors?.report({
147
150
  surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.RouteMeshChannel,
148
151
  messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
@@ -813,7 +816,8 @@ class ZLinkSpotNodeRuntimeManager {
813
816
  }
814
817
  const parts = (0, flow_context_1.runWithOutboundFlow)(this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, () => (0, channel_envelope_1.encodeChannelPublishEnvelopeParts)(channelName, topic, packetName, event, undefined, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, metadata));
815
818
  try {
816
- publisher.publish(channelName, topic, parts, { flags });
819
+ const processing = publisher.publish(channelName, topic, parts, { flags });
820
+ void Promise.resolve(processing).catch(() => undefined);
817
821
  return { status: submission_result_1.ZLinkSubmitStatus.Submitted };
818
822
  }
819
823
  catch (error) {
@@ -83,7 +83,7 @@ class ZLinkSpotRoutePacketDispatch {
83
83
  let response;
84
84
  const applicationClaim = this.options.claimApplicationWork?.();
85
85
  try {
86
- await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true), () => this.options.serial.execute(async () => {
86
+ await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined), () => this.options.serial.execute(async () => {
87
87
  const spot = this.options.getTarget();
88
88
  // Keep the wire payload raw until this Spot has acquired its
89
89
  // execution authority. A rejected or superseded work item must not
@@ -12,7 +12,7 @@ class ZLinkSpotRoutedBoundSessionDispatch {
12
12
  async dispatch(received) {
13
13
  const boundSessionSend = (0, spot_remote_route_codec_1.decodeRemoteBoundSessionSend)(received.parts, this.options.channelCodecs(), this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
14
14
  if (boundSessionSend !== undefined) {
15
- await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(boundSessionSend.flowId ?? boundSessionSend.envelope?.header.flowId, boundSessionSend.flowOrigin ?? boundSessionSend.envelope?.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true), () => this.options.routedBoundSessionReceiver?.(boundSessionSend.actorId, boundSessionSend.message, boundSessionSend.packetName, boundSessionSend.metadata, boundSessionSend.actorRef, boundSessionSend.actorPacketTarget));
15
+ await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(boundSessionSend.flowId ?? boundSessionSend.envelope?.header.flowId, boundSessionSend.flowOrigin ?? boundSessionSend.envelope?.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined), () => this.options.routedBoundSessionReceiver?.(boundSessionSend.actorId, boundSessionSend.message, boundSessionSend.packetName, boundSessionSend.metadata, boundSessionSend.actorRef, boundSessionSend.actorPacketTarget));
16
16
  if ((0, spot_route_replies_1.hasReplyToken)(received.replyToken)) {
17
17
  (0, spot_route_replies_1.submitRoutePayloadReply)(received, boundSessionSend.envelope, { ok: true });
18
18
  }
@@ -131,7 +131,7 @@ class ZLinkSpotSubscriptionDispatch {
131
131
  }
132
132
  const spot = this.options.getTarget();
133
133
  const subSource = message.routingId === null ? undefined : String(message.routingId);
134
- const inboundFlow = (0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
134
+ const inboundFlow = (0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined);
135
135
  try {
136
136
  await this.options.serial.execute(() => (0, flow_context_1.runWithFlow)(inboundFlow, async () => {
137
137
  // Payload deserialization belongs to the Spot serial turn. Queue
@@ -62,7 +62,7 @@ class ZLinkSpotTimerRegistry {
62
62
  let callbackStarted = false;
63
63
  const timer = startOutsideStateLane(() => new ZLinkManagedTimer(name, periodMs, normalizeTimerOptions(options), async (tick) => {
64
64
  callbackStarted = false;
65
- const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled());
65
+ const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled(), undefined);
66
66
  const operation = () => {
67
67
  const current = this.timers.get(name);
68
68
  if (current === undefined ||