@zlink-systems/framework 0.27.0 → 0.28.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 (121) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActorClient.d.ts +1 -1
  3. package/dist/contracts/Channels/Calls.d.ts +1 -1
  4. package/dist/contracts/Configuration/Builders.d.ts +0 -1
  5. package/dist/contracts/Configuration/Configs.d.ts +0 -1
  6. package/dist/contracts/Configuration/RegistrationTypes.d.ts +0 -5
  7. package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
  8. package/dist/contracts/Spots/Contracts.d.ts +1 -1
  9. package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
  10. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
  11. package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
  12. package/dist/runtime/actors/actor-client.js +7 -10
  13. package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
  14. package/dist/runtime/actors/actor-request-deadline.js +4 -29
  15. package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
  16. package/dist/runtime/backend/contracts/index.d.ts +1 -4
  17. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
  18. package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
  19. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  20. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
  21. package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
  22. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  23. package/dist/runtime/channels/channel-clients.js +8 -10
  24. package/dist/runtime/channels/channel-envelope.d.ts +1 -1
  25. package/dist/runtime/channels/channel-envelope.js +4 -10
  26. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  27. package/dist/runtime/channels/channel-outbound-operations.js +20 -36
  28. package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
  29. package/dist/runtime/channels/channel-runtime-manager.js +8 -8
  30. package/dist/runtime/channels/channel-socket-options.js +0 -10
  31. package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
  32. package/dist/runtime/channels/channel-socket-registry.js +3 -14
  33. package/dist/runtime/channels/channel-transports.d.ts +10 -14
  34. package/dist/runtime/channels/channel-transports.js +11 -14
  35. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  36. package/dist/runtime/channels/local-spot-route-transport.js +1 -3
  37. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
  38. package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
  39. package/dist/runtime/channels/source-spot-router.d.ts +1 -1
  40. package/dist/runtime/channels/source-spot-router.js +2 -4
  41. package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
  42. package/dist/runtime/channels/spot-node-route-transport.js +2 -3
  43. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
  44. package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
  45. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
  46. package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
  47. package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
  48. package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
  49. package/dist/runtime/host/actor-packet-relay.js +8 -10
  50. package/dist/runtime/host/index.js +1 -15
  51. package/dist/runtime/host/remote-bound-session-relay.js +8 -8
  52. package/dist/runtime/host/spot-address-transport.d.ts +1 -9
  53. package/dist/runtime/host/spot-address-transport.js +46 -86
  54. package/dist/runtime/locations/location-store-repository.js +6 -4
  55. package/dist/runtime/messaging/request-deadline.d.ts +3 -0
  56. package/dist/runtime/messaging/request-deadline.js +39 -0
  57. package/dist/runtime/messaging/submission-result.d.ts +2 -1
  58. package/dist/runtime/messaging/submission-result.js +11 -4
  59. package/dist/runtime/spots/spot-outbound.d.ts +3 -5
  60. package/dist/runtime/spots/spot-outbound.js +12 -15
  61. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
  62. package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
  63. package/dist/runtime/streams/bound-session-service.js +1 -1
  64. package/dist/runtime/streams/index.d.ts +2 -2
  65. package/dist/runtime/streams/index.js +2 -2
  66. package/dist/runtime/streams/managed-stream.d.ts +2 -2
  67. package/dist/runtime/streams/managed-stream.js +3 -5
  68. package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
  69. package/dist/runtime/streams/session-calls.d.ts +5 -7
  70. package/dist/runtime/streams/session-calls.js +6 -23
  71. package/dist/runtime/streams/session-context.d.ts +4 -4
  72. package/dist/runtime/streams/session-context.js +3 -6
  73. package/package.json +3 -3
  74. package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
  75. package/src/contracts/Channels/Calls.ts +1 -1
  76. package/src/contracts/Configuration/Builders.ts +0 -1
  77. package/src/contracts/Configuration/Configs.ts +0 -1
  78. package/src/contracts/Configuration/RegistrationBuilders.ts +0 -3
  79. package/src/contracts/Configuration/RegistrationTypes.ts +0 -5
  80. package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
  81. package/src/contracts/Spots/Contracts.ts +1 -1
  82. package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
  83. package/src/contracts/Streams/IZLinkSession.ts +2 -3
  84. package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
  85. package/src/runtime/actors/actor-client.ts +9 -15
  86. package/src/runtime/actors/actor-request-deadline.ts +4 -49
  87. package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
  88. package/src/runtime/backend/contracts/index.ts +1 -8
  89. package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
  90. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
  91. package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
  92. package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
  93. package/src/runtime/channels/channel-clients.ts +9 -19
  94. package/src/runtime/channels/channel-envelope.ts +16 -28
  95. package/src/runtime/channels/channel-outbound-operations.ts +36 -38
  96. package/src/runtime/channels/channel-runtime-manager.ts +6 -28
  97. package/src/runtime/channels/channel-socket-options.ts +0 -13
  98. package/src/runtime/channels/channel-socket-registry.ts +3 -17
  99. package/src/runtime/channels/channel-transports.ts +7 -33
  100. package/src/runtime/channels/local-spot-route-transport.ts +1 -5
  101. package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
  102. package/src/runtime/channels/source-spot-router.ts +2 -9
  103. package/src/runtime/channels/spot-node-route-transport.ts +2 -7
  104. package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
  105. package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
  106. package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
  107. package/src/runtime/host/actor-packet-relay.ts +8 -15
  108. package/src/runtime/host/index.ts +2 -26
  109. package/src/runtime/host/remote-bound-session-relay.ts +8 -16
  110. package/src/runtime/host/spot-address-transport.ts +106 -165
  111. package/src/runtime/locations/location-store-repository.ts +7 -4
  112. package/src/runtime/messaging/request-deadline.ts +58 -0
  113. package/src/runtime/messaging/submission-result.ts +18 -4
  114. package/src/runtime/spots/spot-outbound.ts +14 -24
  115. package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
  116. package/src/runtime/streams/bound-session-service.ts +1 -1
  117. package/src/runtime/streams/index.ts +2 -4
  118. package/src/runtime/streams/managed-stream.ts +3 -13
  119. package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
  120. package/src/runtime/streams/session-calls.ts +8 -34
  121. package/src/runtime/streams/session-context.ts +6 -15
@@ -55,12 +55,6 @@ export interface ZLinkHostSpotAddressTransportOptions {
55
55
  readonly completions: (meshName: string) => ZLinkMeshCompletionTable | undefined;
56
56
  readonly codecs?: ZLinkChannelEnvelopeCodecRegistry;
57
57
  readonly defaultRequestTimeoutMs: number;
58
- /** Internal one-way deadline policy; public calls do not expose this option. */
59
- readonly defaultSendTimeoutMs?: number;
60
- /** Resolves the send timeout for a configured Spot Mesh. */
61
- readonly sendTimeoutMsForMesh?: (meshName: string) => number;
62
- /** Resolves the send timeout for a resolved Spot route channel. */
63
- readonly sendTimeoutMsForRouteChannel?: (routeChannelId: string) => number;
64
58
  readonly dispatchErrors?: ZLinkDispatchErrorReporter;
65
59
  }
66
60
 
@@ -93,7 +87,7 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
93
87
  sendToSpotAddress(
94
88
  spotId: RoutingId,
95
89
  message: unknown,
96
- call: ZLinkSpotAddressCallOptions
90
+ call: Omit<ZLinkSpotAddressCallOptions, 'timeoutMs' | 'signal'>
97
91
  ): Promise<ZLinkSubmitResult> {
98
92
  // Call-scoped flow (spec 27 §4): the envelope encoders and the
99
93
  // traceInstanceAddress points share one ambient flow that does not
@@ -107,157 +101,136 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
107
101
  private async sendToSpotAddressScoped(
108
102
  spotId: RoutingId,
109
103
  message: unknown,
110
- call: ZLinkSpotAddressCallOptions
104
+ call: Omit<ZLinkSpotAddressCallOptions, 'timeoutMs' | 'signal'>
111
105
  ): Promise<ZLinkSubmitResult> {
112
- const timeoutMs = initialSendTimeoutMs(this.options, call);
113
- const deadline = createSpotAddressDeadline(timeoutMs, call.signal);
114
- try {
115
- const existing = await this.resolveExisting(spotId, deadline.signal);
116
- deadline.requireRemaining();
117
- if (existing !== undefined) {
118
- this.validateExisting(existing, call);
119
- deadline.setOwnerTimeout(this.sendTimeoutMsForRouteChannel(existing.routerChannelId));
120
- try {
121
- const result = await awaitWithAbort(
122
- this.options.routed.sendToSpot(existing, message, {
123
- instanceSpot: call.instanceSpot,
124
- timeoutMs: deadline.requireRemaining(),
125
- signal: deadline.signal,
126
- metadata: call.metadata
127
- }),
128
- deadline.signal
106
+ const activationDeadlineUnixMs = BigInt(
107
+ Math.round(Date.now() + this.options.defaultRequestTimeoutMs)
108
+ );
109
+ const existing = await this.resolveExisting(spotId);
110
+ if (existing !== undefined) {
111
+ this.validateExisting(existing, call);
112
+ try {
113
+ const result = await this.options.routed.sendToSpot(existing, message, {
114
+ instanceSpot: call.instanceSpot,
115
+ metadata: call.metadata
116
+ });
117
+ if (result.status === ZLinkSubmitStatus.Submitted) {
118
+ this.traceInstanceAddress(
119
+ ZLinkMessageFlowOutcome.Sent,
120
+ ZLinkDispatchMessageKind.Send,
121
+ spotId,
122
+ message,
123
+ existing.routerChannelId,
124
+ existing.stableType,
125
+ existing.targetNodeRid
129
126
  );
130
- if (result.status === ZLinkSubmitStatus.Submitted) {
131
- this.traceInstanceAddress(
132
- ZLinkMessageFlowOutcome.Sent,
133
- ZLinkDispatchMessageKind.Send,
134
- spotId,
135
- message,
136
- existing.routerChannelId,
137
- existing.stableType,
138
- existing.targetNodeRid
139
- );
140
- } else {
141
- this.traceInstanceAddress(
142
- submitResultFlowOutcome(result.status),
143
- ZLinkDispatchMessageKind.Send,
144
- spotId,
145
- message,
146
- existing.routerChannelId,
147
- existing.stableType,
148
- existing.targetNodeRid,
149
- submitResultReason(result.status)
150
- );
151
- }
152
- if (
153
- result.status === ZLinkSubmitStatus.TargetNotFound ||
154
- result.status === ZLinkSubmitStatus.RouteNotConnected
155
- ) {
156
- this.options.resolver()?.invalidate?.(spotId);
157
- }
158
- return result;
159
- } catch (error) {
160
- if (isSpotRouteRefreshError(error)) {
161
- this.options.resolver()?.invalidate?.(spotId);
162
- }
163
- const reason = addressedInstanceErrorReason(error);
127
+ } else {
164
128
  this.traceInstanceAddress(
165
- reason === ZLinkDispatchErrorReason.Backpressure
166
- ? ZLinkMessageFlowOutcome.Backpressured
167
- : ZLinkMessageFlowOutcome.Dropped,
129
+ submitResultFlowOutcome(result.status),
168
130
  ZLinkDispatchMessageKind.Send,
169
131
  spotId,
170
132
  message,
171
133
  existing.routerChannelId,
172
134
  existing.stableType,
173
135
  existing.targetNodeRid,
174
- reason
136
+ submitResultReason(result.status)
175
137
  );
176
- throw error;
177
138
  }
178
- }
179
- if (!call.instanceSpot) {
180
- return { status: ZLinkSubmitStatus.TargetNotFound };
181
- }
182
- const selected = this.selectMissingTarget(spotId, call);
183
- if (selected.kind === 'unsupported') {
184
- this.traceInstanceAddress(
185
- ZLinkMessageFlowOutcome.Dropped,
186
- ZLinkDispatchMessageKind.Send,
187
- spotId,
188
- message,
189
- call.initialMeshName,
190
- call.instanceSpotType,
191
- undefined,
192
- ZLinkDispatchErrorReason.StaleTarget
193
- );
194
- return { status: ZLinkSubmitStatus.TargetNotFound };
195
- }
196
- if (selected.kind === 'capacity') {
197
- const error = missingInstancePlacementCapacity(spotId, call.instanceSpotType);
139
+ if (
140
+ result.status === ZLinkSubmitStatus.TargetNotFound ||
141
+ result.status === ZLinkSubmitStatus.RouteNotConnected
142
+ ) {
143
+ this.options.resolver()?.invalidate?.(spotId);
144
+ }
145
+ return result;
146
+ } catch (error) {
147
+ if (isSpotRouteRefreshError(error)) {
148
+ this.options.resolver()?.invalidate?.(spotId);
149
+ }
150
+ const reason = addressedInstanceErrorReason(error);
198
151
  this.traceInstanceAddress(
199
- ZLinkMessageFlowOutcome.Backpressured,
152
+ reason === ZLinkDispatchErrorReason.Backpressure
153
+ ? ZLinkMessageFlowOutcome.Backpressured
154
+ : ZLinkMessageFlowOutcome.Dropped,
200
155
  ZLinkDispatchMessageKind.Send,
201
156
  spotId,
202
157
  message,
203
- call.initialMeshName,
204
- call.instanceSpotType,
205
- undefined,
206
- ZLinkDispatchErrorReason.Backpressure
158
+ existing.routerChannelId,
159
+ existing.stableType,
160
+ existing.targetNodeRid,
161
+ reason
207
162
  );
208
163
  throw error;
209
164
  }
210
- if (selected.kind === 'unavailable') {
211
- this.traceInstanceAddress(
212
- ZLinkMessageFlowOutcome.Dropped,
213
- ZLinkDispatchMessageKind.Send,
214
- spotId,
215
- message,
216
- call.initialMeshName,
217
- call.instanceSpotType,
218
- undefined,
219
- ZLinkDispatchErrorReason.StaleTarget
220
- );
221
- return { status: ZLinkSubmitStatus.RouteNotConnected };
222
- }
223
- deadline.setOwnerTimeout(this.sendTimeoutMsForMesh(selected.meshName));
224
- const encoded = this.encode(ZLinkChannelMessageKind.Command, selected.meshName, message);
225
- const sourceSpotId =
226
- call.sourceSpot === undefined ? undefined : String(call.sourceSpot.routingId);
227
- const mapped = classifySubmitResult(
228
- await awaitWithAbort(
229
- selected.node.sendToMissingInstanceSpot(
230
- selected.target,
231
- encoded,
232
- BigInt(deadline.deadlineUnixMs),
233
- sourceSpotId,
234
- call.metadata
235
- ),
236
- deadline.signal
237
- ),
238
- 'Instance Spot submission'
165
+ }
166
+ if (!call.instanceSpot) {
167
+ return { status: ZLinkSubmitStatus.TargetNotFound };
168
+ }
169
+ const selected = this.selectMissingTarget(spotId, call);
170
+ if (selected.kind === 'unsupported') {
171
+ this.traceInstanceAddress(
172
+ ZLinkMessageFlowOutcome.Dropped,
173
+ ZLinkDispatchMessageKind.Send,
174
+ spotId,
175
+ message,
176
+ call.initialMeshName,
177
+ call.instanceSpotType,
178
+ undefined,
179
+ ZLinkDispatchErrorReason.StaleTarget
239
180
  );
181
+ return { status: ZLinkSubmitStatus.TargetNotFound };
182
+ }
183
+ if (selected.kind === 'capacity') {
184
+ const error = missingInstancePlacementCapacity(spotId, call.instanceSpotType);
240
185
  this.traceInstanceAddress(
241
- submitResultFlowOutcome(mapped.status),
186
+ ZLinkMessageFlowOutcome.Backpressured,
242
187
  ZLinkDispatchMessageKind.Send,
243
188
  spotId,
244
189
  message,
245
- selected.meshName,
246
- selected.target.stableType,
247
- selected.target.targetNodeRid,
248
- mapped.status === ZLinkSubmitStatus.Submitted
249
- ? undefined
250
- : submitResultReason(mapped.status)
190
+ call.initialMeshName,
191
+ call.instanceSpotType,
192
+ undefined,
193
+ ZLinkDispatchErrorReason.Backpressure
251
194
  );
252
- return mapped;
253
- } catch (error) {
254
- if (deadline.expired()) {
255
- return { status: ZLinkSubmitStatus.TimedOut };
256
- }
257
195
  throw error;
258
- } finally {
259
- deadline.close();
260
196
  }
197
+ if (selected.kind === 'unavailable') {
198
+ this.traceInstanceAddress(
199
+ ZLinkMessageFlowOutcome.Dropped,
200
+ ZLinkDispatchMessageKind.Send,
201
+ spotId,
202
+ message,
203
+ call.initialMeshName,
204
+ call.instanceSpotType,
205
+ undefined,
206
+ ZLinkDispatchErrorReason.StaleTarget
207
+ );
208
+ return { status: ZLinkSubmitStatus.RouteNotConnected };
209
+ }
210
+ const encoded = this.encode(ZLinkChannelMessageKind.Command, selected.meshName, message);
211
+ const sourceSpotId =
212
+ call.sourceSpot === undefined ? undefined : String(call.sourceSpot.routingId);
213
+ const mapped = classifySubmitResult(
214
+ await selected.node.sendToMissingInstanceSpot(
215
+ selected.target,
216
+ encoded,
217
+ activationDeadlineUnixMs,
218
+ sourceSpotId,
219
+ call.metadata
220
+ ),
221
+ 'Instance Spot submission'
222
+ );
223
+ this.traceInstanceAddress(
224
+ submitResultFlowOutcome(mapped.status),
225
+ ZLinkDispatchMessageKind.Send,
226
+ spotId,
227
+ message,
228
+ selected.meshName,
229
+ selected.target.stableType,
230
+ selected.target.targetNodeRid,
231
+ mapped.status === ZLinkSubmitStatus.Submitted ? undefined : submitResultReason(mapped.status)
232
+ );
233
+ return mapped;
261
234
  }
262
235
 
263
236
  requestToSpotAddress<TReply = unknown>(
@@ -642,22 +615,6 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
642
615
  return { kind: 'unavailable' };
643
616
  }
644
617
 
645
- private sendTimeoutMsForMesh(meshName: string): number {
646
- return (
647
- this.options.sendTimeoutMsForMesh?.(meshName) ??
648
- this.options.defaultSendTimeoutMs ??
649
- this.options.defaultRequestTimeoutMs
650
- );
651
- }
652
-
653
- private sendTimeoutMsForRouteChannel(routeChannelId: string): number {
654
- return (
655
- this.options.sendTimeoutMsForRouteChannel?.(routeChannelId) ??
656
- this.options.defaultSendTimeoutMs ??
657
- this.options.defaultRequestTimeoutMs
658
- );
659
- }
660
-
661
618
  private validateExisting(
662
619
  target: import('../spots/spot-routing-internal').ZLinkSpotRouteTarget,
663
620
  call: ZLinkSpotAddressCallOptions
@@ -845,21 +802,9 @@ function addressedInstanceErrorReason(error: unknown): ZLinkDispatchErrorReason
845
802
  return ZLinkDispatchErrorReason.HandlerException;
846
803
  }
847
804
 
848
- function initialSendTimeoutMs(
849
- options: ZLinkHostSpotAddressTransportOptions,
850
- call: ZLinkSpotAddressCallOptions
851
- ): number {
852
- return call.initialMeshName === undefined
853
- ? (options.defaultSendTimeoutMs ?? options.defaultRequestTimeoutMs)
854
- : (options.sendTimeoutMsForMesh?.(call.initialMeshName) ??
855
- options.defaultSendTimeoutMs ??
856
- options.defaultRequestTimeoutMs);
857
- }
858
-
859
805
  interface ZLinkSpotAddressDeadline {
860
806
  readonly deadlineUnixMs: number;
861
807
  readonly signal: AbortSignal;
862
- setOwnerTimeout(timeoutMs: number): void;
863
808
  requireRemaining(): number;
864
809
  expired(): boolean;
865
810
  close(): void;
@@ -871,7 +816,7 @@ function createSpotAddressDeadline(
871
816
  ): ZLinkSpotAddressDeadline {
872
817
  const startedAtMs = performance.now();
873
818
  const startedAtUnixMs = Date.now();
874
- let deadlineMs = startedAtMs + Math.max(0, timeoutMs);
819
+ const deadlineMs = startedAtMs + Math.max(0, timeoutMs);
875
820
  const controller = new AbortController();
876
821
  let expired = false;
877
822
  let timeout: ReturnType<typeof setTimeout> | undefined;
@@ -897,10 +842,6 @@ function createSpotAddressDeadline(
897
842
  return Math.round(startedAtUnixMs + (deadlineMs - startedAtMs));
898
843
  },
899
844
  signal: controller.signal,
900
- setOwnerTimeout(ownerTimeoutMs: number) {
901
- deadlineMs = startedAtMs + Math.max(0, ownerTimeoutMs);
902
- arm();
903
- },
904
845
  requireRemaining() {
905
846
  const remainingMs = deadlineMs - performance.now();
906
847
  if (remainingMs <= 0) {
@@ -1193,7 +1193,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1193
1193
  this.provider.read(leaseKey, signal)
1194
1194
  ]);
1195
1195
  const descriptor = liveTargetDescriptor(descriptorRead, leaseRead, request.target);
1196
- if (descriptor === undefined) {
1196
+ if (descriptor === undefined || descriptor.state !== ZLinkFrameworkRuntimeState.Serving) {
1197
1197
  return { kind: 'conflict', current: { kind: 'missing', storeNow: current.storeNow } };
1198
1198
  }
1199
1199
  const [capacityRead, objectGenerationRead, authorityOwnerGenerationRead] = await Promise.all([
@@ -1394,9 +1394,11 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1394
1394
  ) {
1395
1395
  return { kind: 'stale' };
1396
1396
  }
1397
+ const descriptorKey = meshKey(request.target.meshName, request.target.nodeRid);
1397
1398
  const leaseKey = ownerKey(request.target.owner.ownerId);
1398
1399
  const capacityRowKey = capacityKey(request.target.meshName, String(request.target.nodeRid));
1399
- const [leaseRead, capacityRead] = await Promise.all([
1400
+ const [descriptorRead, leaseRead, capacityRead] = await Promise.all([
1401
+ this.provider.read(descriptorKey, signal),
1400
1402
  this.provider.read(leaseKey, signal),
1401
1403
  this.provider.read(capacityRowKey, signal)
1402
1404
  ]);
@@ -1406,7 +1408,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1406
1408
  // rather than read the absence as expiry.
1407
1409
  throw new Error('Location Store owner lease record is invalid.');
1408
1410
  }
1409
- if (!sameLiveOwner(leaseRead, record.snapshot)) return { kind: 'stale' };
1411
+ if (liveTargetDescriptor(descriptorRead, leaseRead, request.target) === undefined)
1412
+ return { kind: 'stale' };
1410
1413
  const capacity =
1411
1414
  capacityRead.kind === 'missing'
1412
1415
  ? emptyCapacityRecord()
@@ -1415,6 +1418,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1415
1418
  {
1416
1419
  conditions: [
1417
1420
  { kind: 'version', key: rowKey, expected: current.value.version },
1421
+ versionCondition(descriptorKey, descriptorRead),
1418
1422
  leaseValueCondition(leaseKey, {
1419
1423
  ownerId: record.snapshot.ownerId,
1420
1424
  leaseGeneration: record.snapshot.ownerLeaseGeneration
@@ -3904,7 +3908,6 @@ function liveTargetDescriptor(
3904
3908
  descriptor.ownerId === target.owner.ownerId &&
3905
3909
  descriptor.leaseGeneration === target.owner.leaseGeneration &&
3906
3910
  descriptor.objectRole === ZLinkObjectRole.Server &&
3907
- descriptor.state === ZLinkFrameworkRuntimeState.Serving &&
3908
3911
  lease.ownerId === target.owner.ownerId &&
3909
3912
  BigInt(lease.leaseGeneration) === target.owner.leaseGeneration &&
3910
3913
  leaseRead.value.expiresAt !== undefined &&
@@ -0,0 +1,58 @@
1
+ import { awaitWithAbort } from '../abort';
2
+ import {
3
+ createInternalFrameworkException,
4
+ ZLinkFrameworkInternalErrorKind
5
+ } from '../framework-errors-internal';
6
+
7
+ export function remainingRequestTimeout(
8
+ operation: string,
9
+ deadlineMs: number | undefined
10
+ ): number | undefined {
11
+ if (deadlineMs === undefined) return undefined;
12
+ const remaining = deadlineMs - performance.now();
13
+ if (remaining <= 0) {
14
+ throw createInternalFrameworkException(
15
+ ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
16
+ `${operation} exceeded its deadline.`
17
+ );
18
+ }
19
+ return Math.max(1, Math.ceil(remaining));
20
+ }
21
+
22
+ /** Bounds caller waiting across outbound admission and reply; late completions stay observed. */
23
+ export async function waitRequestReply<T>(
24
+ reply: Promise<T>,
25
+ operation: string,
26
+ deadlineMs: number | undefined,
27
+ signal?: AbortSignal,
28
+ endWait?: () => void,
29
+ discardLateReply?: (reply: T) => void
30
+ ): Promise<T> {
31
+ let timer: ReturnType<typeof setTimeout> | undefined;
32
+ let waiting = reply;
33
+ if (deadlineMs !== undefined) {
34
+ const timeout = new Promise<never>((_resolve, reject) => {
35
+ const remaining = remainingRequestTimeout(operation, deadlineMs);
36
+ timer = setTimeout(
37
+ () =>
38
+ reject(
39
+ createInternalFrameworkException(
40
+ ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
41
+ `${operation} exceeded its deadline.`
42
+ )
43
+ ),
44
+ remaining
45
+ );
46
+ });
47
+ waiting = Promise.race([reply, timeout]);
48
+ }
49
+ try {
50
+ return await awaitWithAbort(waiting, signal);
51
+ } catch (error) {
52
+ endWait?.();
53
+ if (discardLateReply !== undefined) void reply.then(discardLateReply, () => undefined);
54
+ throw error;
55
+ } finally {
56
+ clearTimeout(timer);
57
+ }
58
+ }
@@ -18,7 +18,7 @@ export interface ZLinkSubmitResult {
18
18
  readonly status: ZLinkSubmitStatus;
19
19
  }
20
20
 
21
- /** Projects the Core submit result once; phase only determines capacity refusal meaning. */
21
+ /** Projects immediate refusal and pending admission failure from their Core phase. */
22
22
  export function submitToRequestResult(result: number, phase: 'submit' | 'completion'): number {
23
23
  switch (result) {
24
24
  case SubmitResult.Ok:
@@ -28,7 +28,7 @@ export function submitToRequestResult(result: number, phase: 'submit' | 'complet
28
28
  case SubmitResult.NotConnected:
29
29
  return RequestResult.NotConnected;
30
30
  case SubmitResult.NotFound:
31
- return RequestResult.NotFound;
31
+ return phase === 'submit' ? RequestResult.NotFound : RequestResult.NotConnected;
32
32
  case SubmitResult.NotAdmitted:
33
33
  return RequestResult.Rejected;
34
34
  case SubmitResult.InvalidHandle:
@@ -49,7 +49,7 @@ export function submitToRequestResult(result: number, phase: 'submit' | 'complet
49
49
  export function classifySubmitResult(
50
50
  result: number,
51
51
  operation: string,
52
- phase: 'submit' | 'completion' = 'completion'
52
+ phase: 'submit' | 'completion' = 'submit'
53
53
  ): ZLinkSubmitResult {
54
54
  const terminal = submitToRequestResult(result, phase);
55
55
  switch (terminal) {
@@ -80,12 +80,17 @@ export function requireOneWayCompletion(
80
80
  case ZLinkSubmitStatus.Submitted:
81
81
  return;
82
82
  case ZLinkSubmitStatus.TimedOut:
83
- case ZLinkSubmitStatus.Backpressured:
84
83
  throw createInternalFrameworkException(
85
84
  ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
86
85
  `${operation} did not obtain admission before its deadline.`,
87
86
  true
88
87
  );
88
+ case ZLinkSubmitStatus.Backpressured:
89
+ throw createInternalFrameworkException(
90
+ ZLinkFrameworkInternalErrorKind.RouteNotConnected,
91
+ `${operation} has no admission wait token.`,
92
+ true
93
+ );
89
94
  case ZLinkSubmitStatus.TargetNotFound:
90
95
  throw createInternalFrameworkException(notFoundKind, `${operation} target was not found.`);
91
96
  case ZLinkSubmitStatus.RouteNotConnected:
@@ -106,6 +111,15 @@ export function requirePublishCompletion(result: ZLinkSubmitResult, operation: s
106
111
  requireOneWayCompletion(result, operation);
107
112
  }
108
113
 
114
+ export function requireClassicFanoutCompletion(result: ZLinkSubmitResult, operation: string): void {
115
+ requireOneWayCompletion(
116
+ result.status === ZLinkSubmitStatus.Backpressured
117
+ ? { status: ZLinkSubmitStatus.TimedOut }
118
+ : result,
119
+ operation
120
+ );
121
+ }
122
+
109
123
  export function throwAlreadySubmitted(operation: string): never {
110
124
  throw createInternalFrameworkException(
111
125
  ZLinkFrameworkInternalErrorKind.AlreadySubmitted,
@@ -191,7 +191,7 @@ export interface ZLinkSpotAddressTransport {
191
191
  sendToSpotAddress(
192
192
  spotId: RoutingId,
193
193
  message: unknown,
194
- options: ZLinkSpotAddressCallOptions
194
+ options: Omit<ZLinkSpotAddressCallOptions, 'timeoutMs' | 'signal'>
195
195
  ): Promise<ZLinkSubmitResult>;
196
196
  requestToSpotAddress<TReply = unknown>(
197
197
  spotId: RoutingId,
@@ -228,14 +228,12 @@ export interface ZLinkSpotRoutedTransport {
228
228
  export interface ZLinkSpotRoutedSendOptions {
229
229
  readonly instanceSpot?: boolean;
230
230
  readonly packetName?: string;
231
- /** Internal end-to-end budget remaining for route admission. */
232
- readonly timeoutMs?: number;
233
- readonly signal?: AbortSignal;
234
231
  readonly metadata?: ReadonlyMap<string, string>;
235
232
  }
236
233
 
237
234
  export interface ZLinkSpotRoutedRequestOptions extends ZLinkSpotRoutedSendOptions {
238
235
  readonly timeoutMs?: number;
236
+ readonly signal?: AbortSignal;
239
237
  }
240
238
 
241
239
  interface MutableAddressCallOptions {
@@ -283,13 +281,13 @@ function createAddressedSpotSendCall(
283
281
  );
284
282
  return this;
285
283
  },
286
- async submit(signal?: AbortSignal): Promise<void> {
284
+ async submit(): Promise<void> {
287
285
  markSubmitted(options);
288
286
  const pending = startRequestOnSerial(serial, () => ({
289
287
  pending: transport.sendToSpotAddress(
290
288
  spotId,
291
289
  message,
292
- freezeAddressCallOptions(options, signal, sourceSpotProvider?.())
290
+ freezeAddressCallOptions(options, sourceSpotProvider?.())
293
291
  )
294
292
  }));
295
293
  const result = await (serial.isCurrentTurn ? pending : deliverOnSerial(serial, pending));
@@ -314,11 +312,11 @@ function createAddressedSpotRequestCall(
314
312
  markSubmitted(options);
315
313
  rejectSameSpotAsyncRequest(serial, spotId, waitPolicy);
316
314
  return startRequestOnSerial<TReply>(serial, () => ({
317
- pending: transport.requestToSpotAddress<TReply>(
318
- spotId,
319
- request,
320
- freezeAddressCallOptions(options, signal, sourceSpotProvider?.())
321
- )
315
+ pending: transport.requestToSpotAddress<TReply>(spotId, request, {
316
+ ...freezeAddressCallOptions(options, sourceSpotProvider?.()),
317
+ timeoutMs: options.timeoutMs,
318
+ signal
319
+ })
322
320
  }));
323
321
  };
324
322
  return {
@@ -422,7 +420,6 @@ function markSubmitted(options: MutableAddressCallOptions): void {
422
420
 
423
421
  function freezeAddressCallOptions(
424
422
  options: MutableAddressCallOptions,
425
- signal: AbortSignal | undefined,
426
423
  sourceSpot: ZLinkBackendSpot | undefined
427
424
  ): ZLinkSpotAddressCallOptions {
428
425
  return {
@@ -430,8 +427,6 @@ function freezeAddressCallOptions(
430
427
  instanceSpot: options.instanceSpot,
431
428
  instanceSpotType: options.instanceSpotType,
432
429
  initialMeshName: options.initialMeshName,
433
- timeoutMs: options.timeoutMs,
434
- signal,
435
430
  sourceSpot
436
431
  };
437
432
  }
@@ -453,8 +448,8 @@ function wrapSendCall(serial: ZLinkSpotSerialTurnExecutor, inner: ZLinkSendCall)
453
448
  else inner.metadata(key);
454
449
  return this;
455
450
  },
456
- async submit(signal?: AbortSignal) {
457
- const result = await runInternalTransportStart(serial, () => inner.submit(signal));
451
+ async submit() {
452
+ const result = await runInternalTransportStart(serial, () => inner.submit());
458
453
  return result;
459
454
  }
460
455
  };
@@ -548,11 +543,10 @@ function wrapRoutedSpotSendCall(
548
543
  }
549
544
  return this;
550
545
  },
551
- async submit(signal?: AbortSignal): Promise<void> {
546
+ async submit(): Promise<void> {
552
547
  const pending = startRequestOnSerial(serial, () => ({
553
548
  pending: sendToSpotHandle(transport, spot, message, {
554
549
  metadata,
555
- signal,
556
550
  spotRouterChannelIdForMesh,
557
551
  sourceSpot: sourceSpotProvider?.()
558
552
  })
@@ -645,7 +639,7 @@ export async function sendToSpotHandle(
645
639
  transport: ZLinkSpotRoutedTransport,
646
640
  spot: SpotHandle,
647
641
  message: unknown,
648
- options: ZLinkSpotHandleCallOptions = {}
642
+ options: Omit<ZLinkSpotHandleCallOptions, 'timeoutMs' | 'signal'> = {}
649
643
  ): Promise<ZLinkSubmitResult> {
650
644
  const packetName = resolveFrameworkPacketName(message, undefined, 'SPOT');
651
645
  const sendResolved = async (resolved: ResolvedSpotHandle): Promise<ZLinkSubmitResult> => {
@@ -653,20 +647,16 @@ export async function sendToSpotHandle(
653
647
  if (options.sourceSpot !== undefined && transport.sendFromSpotToSpot !== undefined) {
654
648
  return transport.sendFromSpotToSpot(options.sourceSpot, target, message, {
655
649
  packetName,
656
- timeoutMs: options.timeoutMs,
657
- signal: options.signal,
658
650
  metadata: options.metadata
659
651
  });
660
652
  }
661
653
  return transport.sendToSpot(target, message, {
662
654
  packetName,
663
- timeoutMs: options.timeoutMs,
664
- signal: options.signal,
665
655
  metadata: options.metadata
666
656
  });
667
657
  };
668
658
 
669
- return sendResolved(await requireSpotRef(spot, options.signal));
659
+ return sendResolved(await requireSpotRef(spot));
670
660
  }
671
661
 
672
662
  export async function requestToSpotHandle<TReply = unknown>(