@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,4 +1,9 @@
1
- import type { Context, CoreHwmBudgetSnapshot, TopicMessage } from '@zlink-systems/zlink';
1
+ import type {
2
+ Context,
3
+ CoreHwmBudgetSnapshot,
4
+ ReceiveFlowState,
5
+ TopicMessage
6
+ } from '@zlink-systems/zlink';
2
7
  import type { ZLinkCoreHwmOptions } from '../../../contracts/Configuration';
3
8
  import type {
4
9
  ZLinkBackendAdapterFactory,
@@ -60,10 +65,12 @@ class ZLinkNodeChannelBackendAdapter implements ZLinkChannelBackendAdapter {
60
65
  }
61
66
 
62
67
  createRouterSocket(context: ZLinkBackendContext): ZLinkBackendRouterSocket {
63
- return wrapSocket(
64
- zlink.createRouterSocket(asNodeContext(context)),
65
- true
66
- ) as unknown as ZLinkBackendRouterSocket;
68
+ const socket = zlink.createRouterSocket(asNodeContext(context));
69
+ const adapter = wrapSocket(socket, true);
70
+ adapter.setReceiveFlowState = (state: ReceiveFlowState): void => {
71
+ socket.setReceiveFlowState(state);
72
+ };
73
+ return adapter as unknown as ZLinkBackendRouterSocket;
67
74
  }
68
75
 
69
76
  createPublisherSocket(context: ZLinkBackendContext): ZLinkBackendPublisherSocket {
@@ -123,6 +123,14 @@ export async function submitBindingAsyncSend(
123
123
  operation: ZLinkBindingAsyncSendOperation,
124
124
  payload: unknown
125
125
  ): Promise<void> {
126
+ const submission = submitBindingSend(operation, payload);
127
+ if (submission.result === SubmitResult.Backpressured) await submission.admitted;
128
+ }
129
+
130
+ export function submitBindingSend(
131
+ operation: ZLinkBindingAsyncSendOperation,
132
+ payload: unknown
133
+ ): import('../raw-binding-port').ZLinkRawSendSubmission {
126
134
  let submission: import('@zlink-systems/zlink').SendSubmission;
127
135
  try {
128
136
  let current: ZLinkBindingAsyncSendSubmitOperation | undefined;
@@ -137,12 +145,14 @@ export async function submitBindingAsyncSend(
137
145
  throw translateBindingResultError(error, 'submit');
138
146
  }
139
147
  if (submission.result === SubmitResult.Backpressured) {
140
- try {
141
- await submission.admitted;
142
- } catch (error) {
143
- throw translateBindingResultError(error, 'completion');
144
- }
148
+ return {
149
+ result: submission.result,
150
+ admitted: submission.admitted.catch((error) => {
151
+ throw translateBindingResultError(error, 'completion');
152
+ })
153
+ };
145
154
  }
155
+ return { result: SubmitResult.Ok, admitted: submission.admitted };
146
156
  }
147
157
 
148
158
  export function submitBindingSyncSend(
@@ -166,7 +176,8 @@ export function submitBindingSyncSend(
166
176
  export async function submitBindingRequest(
167
177
  operation: ZLinkBindingRequestOperation,
168
178
  payload: unknown,
169
- timeoutMs: number | undefined
179
+ timeoutMs: number | undefined,
180
+ recordReceived?: () => void
170
181
  ): Promise<readonly unknown[]> {
171
182
  const deadlineMs = timeoutMs === undefined ? undefined : performance.now() + timeoutMs;
172
183
  try {
@@ -184,7 +195,7 @@ export async function submitBindingRequest(
184
195
  if (timeoutMs !== undefined) {
185
196
  current = current.timeout(timeoutMs);
186
197
  }
187
- return await bindingRequestReply(current.submit(), deadlineMs);
198
+ return await bindingRequestReply(current.submit(), deadlineMs, recordReceived);
188
199
  } catch (error) {
189
200
  throw translateBindingResultError(error);
190
201
  }
@@ -192,7 +203,8 @@ export async function submitBindingRequest(
192
203
 
193
204
  export async function bindingRequestReply(
194
205
  submission: import('@zlink-systems/zlink').RequestSubmission,
195
- deadlineMs: number | undefined
206
+ deadlineMs: number | undefined,
207
+ recordReceived?: () => void
196
208
  ): Promise<import('@zlink-systems/zlink').Message[]> {
197
209
  const reply = submission.reply;
198
210
  if (submission.result === SubmitResult.Backpressured) {
@@ -202,13 +214,22 @@ export async function bindingRequestReply(
202
214
  'Request',
203
215
  deadlineMs
204
216
  );
217
+ recordReceived?.();
205
218
  return replies;
206
219
  } catch (error) {
207
- void reply.then(closeBindingReply, () => undefined);
220
+ void reply.then(
221
+ (parts) => {
222
+ recordReceived?.();
223
+ closeBindingReply(parts);
224
+ },
225
+ () => undefined
226
+ );
208
227
  throw error;
209
228
  }
210
229
  }
211
- return reply;
230
+ const replies = await reply;
231
+ recordReceived?.();
232
+ return replies;
212
233
  }
213
234
 
214
235
  export function closeBindingReply(
@@ -248,25 +269,6 @@ export function isContextTerminatedError(error: unknown): boolean {
248
269
  return error instanceof Error && /context was terminated/i.test(error.message);
249
270
  }
250
271
 
251
- export function disableSocketLinger(target: unknown): void {
252
- if (
253
- target !== null &&
254
- typeof target === 'object' &&
255
- 'options' in target &&
256
- typeof target.options === 'object' &&
257
- target.options !== null &&
258
- 'linger' in target.options
259
- ) {
260
- try {
261
- (target.options as { linger: number }).linger = 0;
262
- } catch (error) {
263
- if (!isContextTerminatedError(error)) {
264
- throw error;
265
- }
266
- }
267
- }
268
- }
269
-
270
272
  export function toNativeRoutingId(routingId: unknown): unknown {
271
273
  if (typeof routingId === 'string') return zlink.RoutingId.from(routingId);
272
274
  const toHex = (routingId as { readonly toHex?: unknown } | null)?.toHex;
@@ -10,15 +10,7 @@ import type { Context } from '@zlink-systems/zlink';
10
10
  export class ZLinkNodeMeshBackendAdapter implements ZLinkMeshBackendAdapter {
11
11
  createMeshNode(
12
12
  context: ZLinkBackendContext,
13
- options: {
14
- readonly meshName: string;
15
- readonly routingId?: string;
16
- readonly receiveTimeoutMs?: number;
17
- readonly trustProfile?: string;
18
- readonly applicationJobQueue: import('../../application-jobs/contracts').ApplicationJobQueuePort;
19
- readonly applicationJobReceiveFlowFailureSink?: (error: unknown) => void;
20
- readonly peerAdmissionSealed?: () => boolean;
21
- }
13
+ options: Parameters<ZLinkMeshBackendAdapter['createMeshNode']>[1]
22
14
  ): ZLinkBackendMeshNode {
23
15
  if (options.trustProfile !== undefined) {
24
16
  throw new Error(
@@ -37,7 +29,8 @@ export class ZLinkNodeMeshBackendAdapter implements ZLinkMeshBackendAdapter {
37
29
  new ZLinkNodeRawBindingPort(bindingContext, options.receiveTimeoutMs),
38
30
  options.applicationJobQueue,
39
31
  options.applicationJobReceiveFlowFailureSink,
40
- options.peerAdmissionSealed
32
+ options.peerAdmissionSealed,
33
+ options.infrastructureTaskRunner
41
34
  );
42
35
  }
43
36
  }
@@ -22,7 +22,8 @@ import {
22
22
  import {
23
23
  translateBindingResultError,
24
24
  bindingRequestReply,
25
- submitBindingAsyncSend
25
+ submitBindingAsyncSend,
26
+ submitBindingSend
26
27
  } from './node-backend-adapter-support';
27
28
  import { isEndpointCloseIgnorableError } from './node-socket-backend-adapter';
28
29
  import { ZLinkNodeEventLoopPoller } from './node-event-loop-poller';
@@ -171,13 +172,6 @@ abstract class NodeRawSocketPort<TSocket extends Socket> implements ZLinkRawSock
171
172
  this.endpoints.delete(`connect\0${endpoint}`);
172
173
  }
173
174
 
174
- setReceiveFlowState(state: 'running' | 'paused'): void {
175
- this.requireOpen();
176
- this.socket.setReceiveFlowState(
177
- state === 'paused' ? ReceiveFlowState.PAUSED : ReceiveFlowState.RUNNING
178
- );
179
- }
180
-
181
175
  monitor(): ZLinkRawMonitorPort {
182
176
  this.requireOpen();
183
177
  const nativeMonitor = this.socket.monitorOpen();
@@ -232,6 +226,14 @@ abstract class NodeRawSocketPort<TSocket extends Socket> implements ZLinkRawSock
232
226
 
233
227
  class NodeRawRouterPort extends NodeRawSocketPort<RouterSocket> implements ZLinkRawRouterPort {
234
228
  private nextIncomingRequestSequence = 1n;
229
+
230
+ setReceiveFlowState(state: 'running' | 'paused'): void {
231
+ this.requireOpen();
232
+ this.socket.setReceiveFlowState(
233
+ state === 'paused' ? ReceiveFlowState.PAUSED : ReceiveFlowState.RUNNING
234
+ );
235
+ }
236
+
235
237
  disconnectRid(routingId: string): void {
236
238
  this.requireOpen();
237
239
  (
@@ -273,10 +275,17 @@ class NodeRawRouterPort extends NodeRawSocketPort<RouterSocket> implements ZLink
273
275
  return submitBindingAsyncSend(this.socket.send(bindingRoutingId(targetRid)), parts);
274
276
  }
275
277
 
278
+ submitSend(targetRid: string, parts: readonly Uint8Array[]) {
279
+ this.requireOpen();
280
+ requireParts(parts);
281
+ return submitBindingSend(this.socket.send(bindingRoutingId(targetRid)), parts);
282
+ }
283
+
276
284
  async request(
277
285
  targetRid: string,
278
286
  parts: readonly Uint8Array[],
279
- timeoutMs: number
287
+ timeoutMs: number,
288
+ recordReceived?: () => void
280
289
  ): Promise<readonly Buffer[]> {
281
290
  this.requireOpen();
282
291
  const deadlineMs = performance.now() + timeoutMs;
@@ -289,7 +298,7 @@ class NodeRawRouterPort extends NodeRawSocketPort<RouterSocket> implements ZLink
289
298
  throw translateBindingResultError(error, 'submit');
290
299
  }
291
300
  try {
292
- const replies = await bindingRequestReply(submission, deadlineMs);
301
+ const replies = await bindingRequestReply(submission, deadlineMs, recordReceived);
293
302
  return copyAndClose(replies);
294
303
  } catch (error) {
295
304
  throw translateBindingResultError(error, 'completion');
@@ -17,6 +17,7 @@ import {
17
17
  type SubmitResult as SubmitResultValue
18
18
  } from '@zlink-systems/zlink';
19
19
  import { randomBytes } from 'node:crypto';
20
+ import type { ZLinkMeshCompletionHandler } from '../mesh-completion-table';
20
21
  import { setImmediate as yieldToIO } from 'node:timers/promises';
21
22
  import { type RoutingId, ZLinkFrameworkException } from '../../../contracts';
22
23
 
@@ -32,7 +33,6 @@ import {
32
33
  ZLINK_MAX_CAPACITY,
33
34
  ZLINK_MAX_PUBLIC_WEIGHT
34
35
  } from '../../../contracts/Configuration/RegistrationBuilderPolicy';
35
- import { shouldCompactBackingArray } from '../../admission';
36
36
  import type { ApplicationJobQueuePort } from '../../application-jobs/contracts';
37
37
  import type { ZLinkDispatchErrorReporter } from '../../channels/dispatch-error-reporter';
38
38
  import { ZLINK_NATIVE_CORRELATION_OPERATION_NAMESPACE } from '../../foundation/operation-identity';
@@ -91,6 +91,7 @@ import type {
91
91
  ServiceActorCreateRecord,
92
92
  ServiceDirectSpotRouteFence,
93
93
  ServiceInstanceActivationTarget,
94
+ ServiceInstanceColdActivationTarget,
94
95
  ServiceInstanceRouteFence,
95
96
  ServiceRetiredBoundSessionRouteFence,
96
97
  ServiceSpotRouteFence,
@@ -170,9 +171,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
170
171
  readonly lifecycleGeneration?: bigint;
171
172
  }
172
173
  >();
173
- private readonly completions: Array<PendingCompletion | undefined> = [];
174
- private completionHead = 0;
175
- private completionCount = 0;
174
+ private completionHandler?: ZLinkMeshCompletionHandler;
176
175
  private readonly routingId: string;
177
176
  private readonly lifecycleGeneration: bigint;
178
177
  private bindEndpoint?: string;
@@ -231,7 +230,8 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
231
230
  private readonly bindingPort: ZLinkRawBindingPort,
232
231
  private readonly applicationJobQueue?: ApplicationJobQueuePort,
233
232
  private readonly applicationJobReceiveFlowFailureSink?: (error: unknown) => void,
234
- private readonly peerAdmissionSealed?: () => boolean
233
+ private readonly peerAdmissionSealed?: () => boolean,
234
+ private readonly infrastructureTaskRunner?: import('../../spots/spot-actor-join-dispatch').ZLinkDetachedTaskRunner
235
235
  ) {
236
236
  if (meshName.length === 0) throw new TypeError('MeshName must be non-empty.');
237
237
  if (routingId === undefined || routingId.length === 0) {
@@ -321,7 +321,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
321
321
  }
322
322
 
323
323
  async sendToMissingInstanceSpot(
324
- target: ServiceInstanceActivationTarget,
324
+ target: ServiceInstanceColdActivationTarget,
325
325
  parts: MessageLike | readonly MessageLike[],
326
326
  deadlineUnixMs: bigint,
327
327
  sourceSpotId?: string,
@@ -338,7 +338,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
338
338
  }
339
339
 
340
340
  requestToMissingInstanceSpot(
341
- target: ServiceInstanceActivationTarget,
341
+ target: ServiceInstanceColdActivationTarget,
342
342
  parts: MessageLike | readonly MessageLike[],
343
343
  deadlineUnixMs: bigint,
344
344
  sourceSpotId?: string,
@@ -386,6 +386,8 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
386
386
  resolveAdvertisedEndpoint: (boundEndpoint) => this.resolveAdvertisedEndpoint(boundEndpoint),
387
387
  bindingPort: this.bindingPort,
388
388
  applicationJobQueue: this.requireApplicationJobQueue(),
389
+ onReceiveReady: () => this.onReadable(true, false),
390
+ infrastructureTaskRunner: this.infrastructureTaskRunner,
389
391
  peerAdmissionSealed: this.peerAdmissionSealed,
390
392
  onReceiveFlowConfigFailure: this.applicationJobReceiveFlowFailureSink,
391
393
  onMailboxReady: (domain) =>
@@ -481,7 +483,6 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
481
483
  this.stateful = undefined;
482
484
  this.runtime?.close();
483
485
  this.runtime = undefined;
484
- this.clearCompletions();
485
486
  this.closed = true;
486
487
  }
487
488
 
@@ -730,7 +731,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
730
731
  drainingPeerCount: peers.filter((peer) => peer.descriptor.state === 'draining').length,
731
732
  pendingApplicationMessages: BigInt(this.runtime?.mailbox.pendingMessages('application') ?? 0),
732
733
  pendingInfrastructureMessages: BigInt(
733
- (this.runtime?.mailbox.pendingMessages('infrastructure') ?? 0) + this.completionCount
734
+ this.runtime?.mailbox.pendingMessages('infrastructure') ?? 0
734
735
  ),
735
736
  pendingBytes: BigInt(
736
737
  (this.runtime?.mailbox.pendingBytes('application') ?? 0) +
@@ -846,6 +847,10 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
846
847
  };
847
848
  }
848
849
 
850
+ setCompletionHandler(handler: ZLinkMeshCompletionHandler): void {
851
+ this.completionHandler = handler;
852
+ }
853
+
849
854
  setReadyHandler(handler: (readyDomains: number) => number): void {
850
855
  this.readyHandler = handler;
851
856
  this.notifyReady();
@@ -869,7 +874,6 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
869
874
  } {
870
875
  const target = requireRawReadyBatch(batch);
871
876
  if ((domains & ReadyDomain.Infrastructure) !== 0) {
872
- this.drainCompletions(target);
873
877
  this.drainMailbox('infrastructure', target);
874
878
  }
875
879
  if ((domains & ReadyDomain.Application) !== 0) {
@@ -878,7 +882,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
878
882
  const runtime = this.runtime;
879
883
  const infrastructureResidue =
880
884
  (domains & ReadyDomain.Infrastructure) !== 0 &&
881
- (this.completionCount > 0 || (runtime?.mailbox.pendingMessages('infrastructure') ?? 0) > 0);
885
+ (runtime?.mailbox.pendingMessages('infrastructure') ?? 0) > 0;
882
886
  const applicationResidue =
883
887
  (domains & ReadyDomain.Application) !== 0 &&
884
888
  (runtime?.mailbox.pendingMessages('application') ?? 0) > 0;
@@ -1391,7 +1395,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1391
1395
  ): MeshOperationId {
1392
1396
  const binding = this.requireStateful().registry.binding(actor);
1393
1397
  if (binding === undefined) {
1394
- return this.enqueueImmediateFailure(OperationKind.StreamUnbind, RequestResult.NotFound);
1398
+ return this.observeImmediateFailure(OperationKind.StreamUnbind, RequestResult.NotFound);
1395
1399
  }
1396
1400
  return this.observeStateful(
1397
1401
  OperationKind.StreamUnbind,
@@ -1559,7 +1563,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1559
1563
  private notifyReady(): void {
1560
1564
  const runtime = this.runtime;
1561
1565
  let domains = ReadyDomain.None;
1562
- if (this.completionCount > 0 || (runtime?.mailbox.pendingMessages('infrastructure') ?? 0) > 0) {
1566
+ if ((runtime?.mailbox.pendingMessages('infrastructure') ?? 0) > 0) {
1563
1567
  domains |= ReadyDomain.Infrastructure;
1564
1568
  }
1565
1569
  if ((runtime?.mailbox.pendingMessages('application') ?? 0) > 0) {
@@ -1575,8 +1579,8 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1575
1579
  ): MeshOperationId {
1576
1580
  const id = { high: 1n, low };
1577
1581
  void promise.then(
1578
- (result) => this.enqueueCompletion(id, operationKind, result),
1579
- (error) => this.enqueueCompletion(id, operationKind, requestFailureResult(error))
1582
+ (result) => this.deliverCompletion(id, operationKind, result),
1583
+ (error) => this.deliverCompletion(id, operationKind, requestFailureResult(error))
1580
1584
  );
1581
1585
  return id;
1582
1586
  }
@@ -1587,66 +1591,40 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1587
1591
  ): MeshOperationId {
1588
1592
  const id = { high: ZLINK_NATIVE_CORRELATION_OPERATION_NAMESPACE, low: pending.id };
1589
1593
  void pending.promise.then(
1590
- (result) => this.enqueueCompletion(id, operationKind, result),
1591
- (error) => this.enqueueCompletion(id, operationKind, requestFailureResult(error))
1594
+ (result) => this.deliverCompletion(id, operationKind, result),
1595
+ (error) => this.deliverCompletion(id, operationKind, requestFailureResult(error))
1592
1596
  );
1593
1597
  return id;
1594
1598
  }
1595
1599
 
1596
- private enqueueImmediateFailure(operationKind: number, terminalResult: number): MeshOperationId {
1600
+ private observeImmediateFailure(operationKind: number, terminalResult: number): MeshOperationId {
1597
1601
  const low = this.nextPeerIntent++;
1598
- const id = { high: 1n, low };
1599
- this.enqueueCompletion(id, operationKind, { terminalResult, failureCode: 0 });
1600
- return id;
1602
+ return this.observeCompletion(
1603
+ low,
1604
+ operationKind,
1605
+ Promise.resolve({ terminalResult, failureCode: 0 })
1606
+ );
1601
1607
  }
1602
1608
 
1603
- private enqueueCompletion(
1609
+ private deliverCompletion(
1604
1610
  operationId: MeshOperationId,
1605
1611
  operationKind: number,
1606
1612
  result: RawServiceRequestResult | ServiceStatefulResult
1607
1613
  ): void {
1608
- if (this.closed) return;
1609
- this.completions.push({ operationId, operationKind, result });
1610
- this.completionCount += 1;
1611
- this.readyHandler?.(ReadyDomain.Infrastructure);
1612
- }
1613
-
1614
- private drainCompletions(batch: RawReadyBatch): void {
1615
- while (!batch.full && this.completionCount > 0) {
1616
- const completion = this.takeCompletion();
1617
- if (completion === undefined) continue;
1618
- batch.push(
1619
- {
1620
- ownerKind: ReadyOwnerKind.Node,
1621
- domain: ReadyDomain.Infrastructure,
1622
- spotId: null,
1623
- actor: null,
1624
- terminalCompletion: true
1625
- },
1626
- new CompletionClaim(completion)
1614
+ if (this.completionHandler !== undefined) {
1615
+ this.completionHandler({ operationId, terminalResult: result.terminalResult }, () =>
1616
+ completionRecord(operationId, operationKind, result)
1627
1617
  );
1618
+ return;
1628
1619
  }
1629
- }
1630
-
1631
- private takeCompletion(): PendingCompletion | undefined {
1632
- const completion = this.completions[this.completionHead];
1633
- if (completion === undefined) return undefined;
1634
- this.completions[this.completionHead] = undefined;
1635
- this.completionHead += 1;
1636
- this.completionCount -= 1;
1637
- if (this.completionCount === 0) {
1638
- this.clearCompletions();
1639
- } else if (shouldCompactBackingArray(this.completionHead, this.completions.length)) {
1640
- this.completions.splice(0, this.completionHead);
1641
- this.completionHead = 0;
1642
- }
1643
- return completion;
1644
- }
1645
-
1646
- private clearCompletions(): void {
1647
- this.completions.length = 0;
1648
- this.completionHead = 0;
1649
- this.completionCount = 0;
1620
+ // The raw pull contract uses the existing infrastructure mailbox. A
1621
+ // registered Framework owner receives the terminal directly above.
1622
+ this.runtime?.mailbox.tryEnqueue({
1623
+ owner: this.routingId,
1624
+ domain: 'infrastructure',
1625
+ parts: [],
1626
+ completion: completionRecord(operationId, operationKind, result)
1627
+ });
1650
1628
  }
1651
1629
 
1652
1630
  private drainMailbox(domain: ServiceMailboxDomain, batch: RawReadyBatch): void {
@@ -1655,7 +1633,16 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1655
1633
  while (!batch.full) {
1656
1634
  const claim = runtime.mailbox.tryClaim(domain, MAX_DRAIN_RECORDS, Number.MAX_SAFE_INTEGER);
1657
1635
  if (claim === undefined) break;
1658
- const owner = readyOwner(claim.owner, this.routingId, this.stateful, domain);
1636
+ const owner =
1637
+ claim.records[0]?.completion === undefined
1638
+ ? readyOwner(claim.owner, this.routingId, this.stateful, domain)
1639
+ : {
1640
+ ownerKind: ReadyOwnerKind.Node,
1641
+ domain: ReadyDomain.Infrastructure,
1642
+ spotId: null,
1643
+ actor: null,
1644
+ terminalCompletion: true
1645
+ };
1659
1646
  batch.push(owner, new MailboxClaim(runtime, claim));
1660
1647
  }
1661
1648
  }
@@ -1690,12 +1677,6 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
1690
1677
  }
1691
1678
  }
1692
1679
 
1693
- interface PendingCompletion {
1694
- readonly operationId: MeshOperationId;
1695
- readonly operationKind: number;
1696
- readonly result: RawServiceRequestResult | ServiceStatefulResult;
1697
- }
1698
-
1699
1680
  interface RawClaim {
1700
1681
  recvBatch(batch: ReceiveBatch): {
1701
1682
  readonly ok: boolean;
@@ -2166,54 +2147,43 @@ class MailboxClaim implements RawClaim {
2166
2147
  }
2167
2148
  }
2168
2149
 
2169
- class CompletionClaim implements RawClaim {
2170
- private consumed = false;
2171
-
2172
- constructor(private readonly completion: PendingCompletion) {}
2173
-
2174
- recvBatch(): { readonly ok: boolean; readonly records: ReceiveRecord[] } {
2175
- if (this.consumed) return { ok: false, records: [] };
2176
- this.consumed = true;
2177
- const payload = this.completion.result.payload;
2178
- return {
2179
- ok: true,
2180
- records: [
2181
- {
2182
- kind: ReceiveKind.Completion,
2183
- domain: ReadyDomain.Infrastructure,
2184
- sourceNodeRid: null,
2185
- sourceSpotId: null,
2186
- sourceBindingGeneration: 0n,
2187
- sourceActor: null,
2188
- operationId: this.completion.operationId,
2189
- operationKind: this.completion.operationKind,
2190
- channelName: null,
2191
- topic: null,
2192
- applicationMetadata: null,
2193
- kindData:
2194
- 'kindData' in this.completion.result ? (this.completion.result.kindData ?? null) : null,
2195
- terminalResult: this.completion.result.terminalResult,
2196
- failureErrno: this.completion.result.failureCode,
2197
- parts:
2198
- payload === undefined
2199
- ? []
2200
- : payload.contentType === MULTIPART_CONTENT_TYPE
2201
- ? decodeMultipart(payload.payload)
2202
- : [Message.from(payload.payload)],
2203
- reply: () => SubmitResult.InvalidState,
2204
- replyActorJoin: () => SubmitResult.NotSupported
2205
- }
2206
- ]
2207
- };
2208
- }
2209
-
2210
- release(): void {}
2150
+ function completionRecord(
2151
+ operationId: MeshOperationId,
2152
+ operationKind: number,
2153
+ result: RawServiceRequestResult | ServiceStatefulResult
2154
+ ): ReceiveRecord {
2155
+ const payload = result.payload;
2156
+ return {
2157
+ kind: ReceiveKind.Completion,
2158
+ domain: ReadyDomain.Infrastructure,
2159
+ sourceNodeRid: null,
2160
+ sourceSpotId: null,
2161
+ sourceBindingGeneration: 0n,
2162
+ sourceActor: null,
2163
+ operationId,
2164
+ operationKind,
2165
+ channelName: null,
2166
+ topic: null,
2167
+ applicationMetadata: null,
2168
+ kindData: 'kindData' in result ? (result.kindData ?? null) : null,
2169
+ terminalResult: result.terminalResult,
2170
+ failureErrno: result.failureCode,
2171
+ parts:
2172
+ payload === undefined
2173
+ ? []
2174
+ : payload.contentType === MULTIPART_CONTENT_TYPE
2175
+ ? decodeMultipart(payload.payload)
2176
+ : [ZLinkBufferMessage.from(payload.payload)],
2177
+ reply: () => SubmitResult.InvalidState,
2178
+ replyActorJoin: () => SubmitResult.NotSupported
2179
+ };
2211
2180
  }
2212
2181
 
2213
2182
  function decodeMultipartRecord(
2214
2183
  runtime: RawServiceMeshRuntime,
2215
2184
  record: ServiceMailboxRecord
2216
2185
  ): ReceiveRecord {
2186
+ if (record.completion !== undefined) return record.completion;
2217
2187
  const stateful = statefulMailboxData(record);
2218
2188
  if (stateful !== undefined) {
2219
2189
  return decodeStatefulRecord(record, stateful);
@@ -3,7 +3,6 @@ import type { Socket } from '@zlink-systems/zlink';
3
3
  import type { ZLinkBackendObject } from '../contracts';
4
4
  import {
5
5
  closeBindingHandle,
6
- disableSocketLinger,
7
6
  isBindingNotFound,
8
7
  isContextTerminatedError,
9
8
  isRouteRecvRetryable,
@@ -36,10 +35,12 @@ export function wrapSocket<T extends { close(): void }>(
36
35
  recvTimeout?: number;
37
36
  sendTimeout?: number;
38
37
  noDrop?: boolean;
38
+ linger?: number;
39
39
  maxMsgSize?: bigint;
40
40
  lastEndpoint?: string;
41
41
  };
42
42
  };
43
+ if (socket.options !== undefined) socket.options.linger = 0;
43
44
  const hasRequest = typeof (nativeInstance as { request?: unknown }).request === 'function';
44
45
  const hasRoutedPeer =
45
46
  hasRequest && typeof (nativeInstance as { reply?: unknown }).reply === 'function';
@@ -50,9 +51,10 @@ export function wrapSocket<T extends { close(): void }>(
50
51
  : new ZLinkNodeEventLoopPoller(nativeInstance as unknown as Socket, pollCompletion, () => {});
51
52
  const adapter = {
52
53
  nativeInstance,
54
+ receiveAdmission: undefined as
55
+ import('../../foundation/service-liveness-registry').ServiceLivenessConnection | undefined,
53
56
  async dispose(): Promise<void> {
54
57
  eventLoopPoller?.dispose();
55
- disableSocketLinger(nativeInstance);
56
58
  closeBindingHandle(nativeInstance);
57
59
  },
58
60
  close(): void {
@@ -76,11 +78,6 @@ export function wrapSocket<T extends { close(): void }>(
76
78
  .setChannelName;
77
79
  setChannelName?.call(nativeInstance, channelName);
78
80
  },
79
- setReceiveFlowState(state: 0 | 1): void {
80
- (nativeInstance as T & { setReceiveFlowState(value: 0 | 1): void }).setReceiveFlowState(
81
- state
82
- );
83
- },
84
81
  setProbe(enabled: boolean): void {
85
82
  requireSocketOptions(socket).probe = enabled;
86
83
  },
@@ -114,6 +111,12 @@ export function wrapSocket<T extends { close(): void }>(
114
111
  toNativeRoutingId(routingId)
115
112
  );
116
113
  },
114
+ get lingerMs(): number {
115
+ return requireSocketOptions(socket).linger!;
116
+ },
117
+ set lingerMs(value: number) {
118
+ requireSocketOptions(socket).linger = value;
119
+ },
117
120
  get peerWeight(): number {
118
121
  return socket.options?.peerWeight ?? ZLINK_DEFAULT_PUBLIC_WEIGHT;
119
122
  },
@@ -219,7 +222,8 @@ export function wrapSocket<T extends { close(): void }>(
219
222
  return submitBindingRequest(
220
223
  (nativeInstance as T & { request(): ZLinkBindingRequestOperation }).request(),
221
224
  payload,
222
- timeoutMs
225
+ timeoutMs,
226
+ adapter.receiveAdmission?.recordReceived
223
227
  );
224
228
  }
225
229
  throw new TypeError('Backend request requires a DEALER or ROUTER socket.');