@zlink-systems/framework 0.27.0 → 0.29.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 (191) 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 +2 -1
  5. package/dist/contracts/Configuration/Configs.d.ts +0 -1
  6. package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
  7. package/dist/contracts/Configuration/InternalDefaults.js +4 -1
  8. package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
  9. package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
  10. package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
  11. package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -5
  12. package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
  13. package/dist/contracts/Spots/Contracts.d.ts +1 -1
  14. package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
  15. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
  16. package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
  17. package/dist/runtime/actors/actor-client.js +7 -10
  18. package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
  19. package/dist/runtime/actors/actor-mailbox.js +2 -2
  20. package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
  21. package/dist/runtime/actors/actor-request-deadline.js +4 -29
  22. package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
  23. package/dist/runtime/backend/contracts/index.d.ts +1 -4
  24. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
  25. package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
  26. package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
  27. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
  28. package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
  29. package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
  30. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  31. package/dist/runtime/channels/channel-clients.js +8 -10
  32. package/dist/runtime/channels/channel-envelope.d.ts +1 -1
  33. package/dist/runtime/channels/channel-envelope.js +4 -10
  34. package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
  35. package/dist/runtime/channels/channel-outbound-operations.js +20 -36
  36. package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
  37. package/dist/runtime/channels/channel-runtime-manager.js +8 -8
  38. package/dist/runtime/channels/channel-socket-options.js +0 -10
  39. package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
  40. package/dist/runtime/channels/channel-socket-registry.js +3 -14
  41. package/dist/runtime/channels/channel-transports.d.ts +10 -14
  42. package/dist/runtime/channels/channel-transports.js +11 -14
  43. package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
  44. package/dist/runtime/channels/local-spot-route-transport.js +1 -3
  45. package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
  46. package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
  47. package/dist/runtime/channels/source-spot-router.d.ts +1 -1
  48. package/dist/runtime/channels/source-spot-router.js +2 -4
  49. package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
  50. package/dist/runtime/channels/spot-node-route-transport.js +2 -3
  51. package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
  52. package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
  53. package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
  54. package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
  55. package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
  56. package/dist/runtime/execution/serial-execution-queue.js +116 -28
  57. package/dist/runtime/foundation/operation-registry.d.ts +3 -2
  58. package/dist/runtime/foundation/operation-registry.js +5 -1
  59. package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
  60. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
  61. package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
  62. package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
  63. package/dist/runtime/foundation/service-stateful-registry.js +5 -2
  64. package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
  65. package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
  66. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
  67. package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
  68. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
  69. package/dist/runtime/foundation/service-topology-registry.js +16 -17
  70. package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
  71. package/dist/runtime/host/actor-packet-relay.js +8 -10
  72. package/dist/runtime/host/index.js +1 -15
  73. package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
  74. package/dist/runtime/host/instance-activation-authority.js +46 -97
  75. package/dist/runtime/host/remote-bound-session-relay.js +8 -8
  76. package/dist/runtime/host/spot-address-transport.d.ts +1 -9
  77. package/dist/runtime/host/spot-address-transport.js +47 -87
  78. package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
  79. package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
  80. package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
  81. package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
  82. package/dist/runtime/locations/in-memory-authority-store.js +22 -8
  83. package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
  84. package/dist/runtime/locations/in-memory-location-store.js +3 -0
  85. package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
  86. package/dist/runtime/locations/location-store-repository.d.ts +1 -0
  87. package/dist/runtime/locations/location-store-repository.js +98 -53
  88. package/dist/runtime/messaging/request-deadline.d.ts +3 -0
  89. package/dist/runtime/messaging/request-deadline.js +39 -0
  90. package/dist/runtime/messaging/submission-result.d.ts +2 -1
  91. package/dist/runtime/messaging/submission-result.js +11 -4
  92. package/dist/runtime/spots/spot-outbound.d.ts +3 -5
  93. package/dist/runtime/spots/spot-outbound.js +12 -15
  94. package/dist/runtime/spots/spot-serial-executor.js +4 -1
  95. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  96. package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
  97. package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
  98. package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
  99. package/dist/runtime/streams/bound-session-service.js +1 -1
  100. package/dist/runtime/streams/index.d.ts +2 -3
  101. package/dist/runtime/streams/index.js +6 -5
  102. package/dist/runtime/streams/managed-stream.d.ts +2 -2
  103. package/dist/runtime/streams/managed-stream.js +3 -5
  104. package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
  105. package/dist/runtime/streams/protocol.js +0 -9
  106. package/dist/runtime/streams/session-calls.d.ts +5 -7
  107. package/dist/runtime/streams/session-calls.js +6 -23
  108. package/dist/runtime/streams/session-context.d.ts +4 -8
  109. package/dist/runtime/streams/session-context.js +3 -32
  110. package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
  111. package/dist/runtime/streams/session-serial-executor.js +0 -3
  112. package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
  113. package/dist/runtime/streams/stream-message-utils.js +0 -20
  114. package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
  115. package/dist/runtime/streams/stream-session-runtime.js +40 -85
  116. package/package.json +3 -3
  117. package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
  118. package/src/contracts/Channels/Calls.ts +1 -1
  119. package/src/contracts/Configuration/Builders.ts +2 -1
  120. package/src/contracts/Configuration/Configs.ts +0 -1
  121. package/src/contracts/Configuration/InternalDefaults.ts +4 -0
  122. package/src/contracts/Configuration/RegistrationBuilders.ts +13 -3
  123. package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
  124. package/src/contracts/Configuration/RegistrationTypes.ts +3 -5
  125. package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
  126. package/src/contracts/Spots/Contracts.ts +1 -1
  127. package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
  128. package/src/contracts/Streams/IZLinkSession.ts +2 -3
  129. package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
  130. package/src/runtime/actors/actor-client.ts +9 -15
  131. package/src/runtime/actors/actor-mailbox.ts +7 -2
  132. package/src/runtime/actors/actor-request-deadline.ts +4 -49
  133. package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
  134. package/src/runtime/backend/contracts/index.ts +1 -8
  135. package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
  136. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
  137. package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
  138. package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
  139. package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
  140. package/src/runtime/channels/channel-clients.ts +9 -19
  141. package/src/runtime/channels/channel-envelope.ts +16 -28
  142. package/src/runtime/channels/channel-outbound-operations.ts +36 -38
  143. package/src/runtime/channels/channel-runtime-manager.ts +6 -28
  144. package/src/runtime/channels/channel-socket-options.ts +0 -13
  145. package/src/runtime/channels/channel-socket-registry.ts +3 -17
  146. package/src/runtime/channels/channel-transports.ts +7 -33
  147. package/src/runtime/channels/local-spot-route-transport.ts +1 -5
  148. package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
  149. package/src/runtime/channels/source-spot-router.ts +2 -9
  150. package/src/runtime/channels/spot-node-route-transport.ts +2 -7
  151. package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
  152. package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
  153. package/src/runtime/execution/serial-execution-queue.ts +117 -29
  154. package/src/runtime/foundation/operation-registry.ts +16 -9
  155. package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
  156. package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
  157. package/src/runtime/foundation/service-stateful-registry.ts +10 -5
  158. package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
  159. package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
  160. package/src/runtime/foundation/service-topology-registry.ts +30 -20
  161. package/src/runtime/host/actor-packet-relay.ts +8 -15
  162. package/src/runtime/host/index.ts +2 -26
  163. package/src/runtime/host/instance-activation-authority.ts +58 -125
  164. package/src/runtime/host/remote-bound-session-relay.ts +8 -16
  165. package/src/runtime/host/spot-address-transport.ts +107 -166
  166. package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
  167. package/src/runtime/locations/domain-store-contract.ts +5 -0
  168. package/src/runtime/locations/in-memory-authority-store.ts +42 -8
  169. package/src/runtime/locations/in-memory-location-store.ts +8 -0
  170. package/src/runtime/locations/internal-store-contracts.ts +1 -0
  171. package/src/runtime/locations/location-store-repository.ts +130 -68
  172. package/src/runtime/messaging/request-deadline.ts +58 -0
  173. package/src/runtime/messaging/submission-result.ts +18 -4
  174. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  175. package/src/runtime/spots/spot-outbound.ts +14 -24
  176. package/src/runtime/spots/spot-serial-executor.ts +4 -1
  177. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
  178. package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
  179. package/src/runtime/streams/bound-session-service.ts +1 -1
  180. package/src/runtime/streams/index.ts +5 -5
  181. package/src/runtime/streams/managed-stream.ts +3 -13
  182. package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
  183. package/src/runtime/streams/protocol.ts +0 -13
  184. package/src/runtime/streams/session-calls.ts +8 -34
  185. package/src/runtime/streams/session-context.ts +6 -47
  186. package/src/runtime/streams/session-serial-executor.ts +0 -8
  187. package/src/runtime/streams/stream-message-utils.ts +1 -27
  188. package/src/runtime/streams/stream-session-runtime.ts +54 -97
  189. package/dist/runtime/streams/session-requests.d.ts +0 -25
  190. package/dist/runtime/streams/session-requests.js +0 -101
  191. package/src/runtime/streams/session-requests.ts +0 -110
@@ -61,12 +61,10 @@ import {
61
61
  import { createSessionDispatchContext, DefaultZLinkSessionContext } from './session-context';
62
62
  import { ZLinkSessionSerialExecutor } from './session-serial-executor';
63
63
  import { ownedMessage } from './stream-message-utils';
64
+ import { normalizeStreamLiveness } from '../../contracts/Configuration/RegistrationNormalizers';
64
65
 
65
66
  const ZLINK_SEND_DONT_WAIT = 1;
66
67
  const ZLINK_RECV_DONT_WAIT = 1;
67
- const ZLINK_STREAM_HEARTBEAT_INTERVAL_MS = 1_000;
68
- const ZLINK_STREAM_HEARTBEAT_TIMEOUT_MS = 5_000;
69
- const ZLINK_STREAM_APPLICATION_IDLE_TIMEOUT_MS = 30_000;
70
68
  const ZLINK_STREAM_RECEIVE_FRAME_BATCH_LIMIT = 64;
71
69
  const ZLINK_STREAM_ACTOR_BINDING_REPLACEMENT_CLOSE_DELAY_MS = 100;
72
70
  const ZLINK_STREAM_MONITOR_IDLE_MIN_DELAY_MS = 1;
@@ -89,9 +87,11 @@ interface ZLinkStreamLivenessOptions {
89
87
  readonly claimApplicationWork?: () => ZLinkApplicationWorkClaim;
90
88
  }
91
89
 
90
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
91
+
92
92
  const systemLivenessClock: ZLinkStreamLivenessClock = {
93
93
  now: () => performance.now(),
94
- setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
94
+ setTimer: (callback, delayMs) => setTimeout(callback, Math.min(delayMs, MAX_TIMER_DELAY_MS)),
95
95
  clearTimer: (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>)
96
96
  };
97
97
 
@@ -127,6 +127,9 @@ export interface ZLinkStreamSessionContextFactory {
127
127
  }
128
128
 
129
129
  export interface ZLinkStreamSessionRuntimeOptions {
130
+ readonly heartbeatIntervalMs?: number;
131
+ readonly heartbeatTimeoutMs?: number;
132
+ readonly idleTimeoutMs?: number;
130
133
  readonly socket: ZLinkBackendStreamSocket;
131
134
  readonly nativeSessionService?: StreamSessionService;
132
135
  readonly meshCompletions?: ZLinkMeshCompletionTable;
@@ -165,8 +168,10 @@ export class ZLinkStreamSessionRuntime {
165
168
  private closeReason = 'client_close';
166
169
  private metricsClosed = false;
167
170
  private readonly livenessClock: ZLinkStreamLivenessClock;
171
+ private readonly liveness: ReturnType<typeof normalizeStreamLiveness>;
168
172
  private lastApplicationActivityAt = 0;
169
173
  private lastInboundFrameAt = 0;
174
+ private lastHeartbeatPingAt = 0;
170
175
  private livenessTimer: unknown;
171
176
 
172
177
  constructor(
@@ -177,6 +182,7 @@ export class ZLinkStreamSessionRuntime {
177
182
  session: ZLinkStreamSessionRuntime
178
183
  ) => void = () => {}
179
184
  ) {
185
+ this.liveness = normalizeStreamLiveness(options);
180
186
  this.livenessClock = options.livenessClock ?? systemLivenessClock;
181
187
  this.stream = new ZLinkManagedStream(
182
188
  options.socket,
@@ -238,7 +244,7 @@ export class ZLinkStreamSessionRuntime {
238
244
  }
239
245
 
240
246
  enqueueConnected(localAddr?: string, remoteAddr?: string): void {
241
- this.serial.executeInfrastructure(async () => this.markConnected(localAddr, remoteAddr));
247
+ this.serial.executeControl(async () => this.markConnected(localAddr, remoteAddr));
242
248
  }
243
249
 
244
250
  enqueuePacket(
@@ -255,46 +261,22 @@ export class ZLinkStreamSessionRuntime {
255
261
  terminalOwner?.close();
256
262
  };
257
263
  if (decodedHeader.kind === ZLinkStreamMessageKind.Control) {
258
- if (
259
- messageToBytes(payload).length === 0 &&
260
- (decodedHeader.name === ZLINK_STREAM_HEARTBEAT_PONG ||
261
- decodedHeader.name === ZLINK_STREAM_HEARTBEAT_PING)
262
- ) {
263
- void this.handleControl(decodedHeader, payload)
264
- .catch((error) => {
265
- this.options.onError?.(error);
266
- })
267
- .finally(() => {
268
- applicationJobPermit?.releaseAfterInternalProcessing();
269
- payload.close();
270
- releaseTerminal();
271
- });
272
- return;
273
- }
274
- this.enqueueControl(
275
- async () => {
276
- try {
277
- const dispatch = () => this.dispatchPacket(payload, decodedHeader);
278
- if (applicationJobPermit === undefined) await dispatch();
279
- else await runWithApplicationJobPermit(applicationJobPermit, dispatch);
280
- } finally {
281
- releaseTerminal();
282
- }
283
- },
284
- () => {
264
+ void this.handleControl(decodedHeader, payload)
265
+ .catch((error) => {
266
+ this.options.onError?.(error);
267
+ })
268
+ .finally(() => {
285
269
  applicationJobPermit?.releaseAfterInternalProcessing();
286
270
  payload.close();
287
271
  releaseTerminal();
288
- },
289
- () => this.traceShutdownDrop(decodedHeader)
290
- );
272
+ });
291
273
  return;
292
274
  }
293
275
  if (
294
276
  decodedHeader.kind === ZLinkStreamMessageKind.Response ||
295
277
  decodedHeader.kind === ZLinkStreamMessageKind.Error
296
278
  ) {
297
- this.enqueueInfrastructure(
279
+ this.enqueueControl(
298
280
  async () => {
299
281
  try {
300
282
  await this.dispatchPacket(payload, decodedHeader);
@@ -340,8 +322,7 @@ export class ZLinkStreamSessionRuntime {
340
322
  }
341
323
 
342
324
  /**
343
- * Starts the callback on the session lane and observes its terminal result
344
- * without retaining that lane while application code awaits unrelated work.
325
+ * Retains the session turn through the callback's terminal result.
345
326
  */
346
327
  enqueueActorBindingReplaced(
347
328
  actor: ServiceActorRef,
@@ -352,7 +333,6 @@ export class ZLinkStreamSessionRuntime {
352
333
  if (!this.context.beginActorBindingReplacement(actor, retiredSession)) return;
353
334
  const session = await this.requireSession();
354
335
  let callbackDeadlineTimer: unknown;
355
- let callbackTerminal = false;
356
336
  let forcedClose = false;
357
337
  const scheduleClose = (): void => {
358
338
  if (forcedClose) return;
@@ -367,23 +347,6 @@ export class ZLinkStreamSessionRuntime {
367
347
  void this.close().catch((error) => this.options.onError?.(error));
368
348
  }, ZLINK_STREAM_ACTOR_BINDING_REPLACEMENT_CLOSE_DELAY_MS);
369
349
  };
370
- const completeCallback = (): void => {
371
- if (callbackTerminal) return;
372
- callbackTerminal = true;
373
- if (callbackDeadlineTimer !== undefined) {
374
- this.livenessClock.clearTimer(callbackDeadlineTimer);
375
- callbackDeadlineTimer = undefined;
376
- }
377
- scheduleClose();
378
- };
379
- const reportCallbackFailure = (error: unknown): void => {
380
- try {
381
- this.options.onError?.(error);
382
- } catch {
383
- // Diagnostics must not prevent the lifecycle terminal transition.
384
- }
385
- completeCallback();
386
- };
387
350
  callbackDeadlineTimer = this.livenessClock.setTimer(() => {
388
351
  if (
389
352
  this.disposed ||
@@ -396,18 +359,15 @@ export class ZLinkStreamSessionRuntime {
396
359
  void this.close().catch((error) => this.options.onError?.(error));
397
360
  }, this.options.replacementCallbackTimeoutMs ?? DEFAULT_SESSION_REPLACEMENT_CALLBACK_TIMEOUT_MS);
398
361
 
399
- const callback = session.onActorBindingReplaced;
400
- if (callback === undefined) {
401
- completeCallback();
402
- return;
403
- }
404
362
  try {
405
- // Start the callback on this lifecycle turn, but do not retain the
406
- // serial lane while application code awaits an unrelated operation.
407
- const result = callback.call(session, this.context, actor.actorId);
408
- void Promise.resolve(result).then(() => completeCallback(), reportCallbackFailure);
363
+ await session.onActorBindingReplaced?.(this.context, actor.actorId);
409
364
  } catch (error) {
410
- reportCallbackFailure(error);
365
+ this.options.onError?.(error);
366
+ } finally {
367
+ if (callbackDeadlineTimer !== undefined) {
368
+ this.livenessClock.clearTimer(callbackDeadlineTimer);
369
+ }
370
+ scheduleClose();
411
371
  }
412
372
  });
413
373
  }
@@ -460,6 +420,7 @@ export class ZLinkStreamSessionRuntime {
460
420
  this.connected = true;
461
421
  const connectedAt = this.livenessClock.now();
462
422
  this.lastApplicationActivityAt = connectedAt;
423
+ this.lastHeartbeatPingAt = connectedAt;
463
424
  this.lastInboundFrameAt = Math.max(this.lastInboundFrameAt, connectedAt);
464
425
  this.scheduleLivenessCheck();
465
426
  this.options.metrics?.change(METRIC_NAMES.StreamConnectionsActive, 1, { transport: 'tcp' });
@@ -475,15 +436,8 @@ export class ZLinkStreamSessionRuntime {
475
436
  let dispatchPayload = payload;
476
437
  let enteredDispatch = false;
477
438
  try {
478
- if (decodedHeader.kind === ZLinkStreamMessageKind.Control) {
479
- await this.handleControl(decodedHeader, payload);
480
- return;
481
- }
482
439
  this.lastApplicationActivityAt = this.livenessClock.now();
483
440
  dispatchPayload = this.context.payloadForHeader(decodedHeader, payload);
484
- if (this.context.tryCompleteResponse(decodedHeader, dispatchPayload)) {
485
- return;
486
- }
487
441
  const dispatchActor = this.context.actorForSlot(decodedHeader.actorSlot);
488
442
  if (decodedHeader.actorSlot !== undefined && dispatchActor === undefined) {
489
443
  const staleFlowEnabled = this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true;
@@ -674,14 +628,21 @@ export class ZLinkStreamSessionRuntime {
674
628
  if (this.disposed || this.disconnected || this.livenessTimer !== undefined) {
675
629
  return;
676
630
  }
677
- this.livenessTimer = this.livenessClock.setTimer(() => {
678
- this.livenessTimer = undefined;
679
- this.serial.executeInfrastructure(
680
- async () => this.runLivenessCheck(),
681
- {},
682
- (error) => this.options.onError?.(error)
683
- );
684
- }, ZLINK_STREAM_HEARTBEAT_INTERVAL_MS);
631
+ const now = this.livenessClock.now();
632
+ const due = Math.min(
633
+ this.lastHeartbeatPingAt + this.liveness.heartbeatIntervalMs,
634
+ this.lastInboundFrameAt + this.liveness.heartbeatTimeoutMs,
635
+ this.liveness.idleTimeoutMs > 0
636
+ ? this.lastApplicationActivityAt + this.liveness.idleTimeoutMs
637
+ : Infinity
638
+ );
639
+ this.livenessTimer = this.livenessClock.setTimer(
640
+ () => {
641
+ this.livenessTimer = undefined;
642
+ void this.runLivenessCheck().catch((error) => this.options.onError?.(error));
643
+ },
644
+ Math.max(0, due - now)
645
+ );
685
646
  }
686
647
 
687
648
  private async runLivenessCheck(): Promise<void> {
@@ -689,7 +650,10 @@ export class ZLinkStreamSessionRuntime {
689
650
  return;
690
651
  }
691
652
  const now = this.livenessClock.now();
692
- if (now - this.lastApplicationActivityAt >= ZLINK_STREAM_APPLICATION_IDLE_TIMEOUT_MS) {
653
+ if (
654
+ this.liveness.idleTimeoutMs > 0 &&
655
+ now - this.lastApplicationActivityAt >= this.liveness.idleTimeoutMs
656
+ ) {
693
657
  await this.closeForLiveness(
694
658
  ZLinkStreamCloseReasonCode.IdleTimeout,
695
659
  'idle_timeout',
@@ -697,7 +661,7 @@ export class ZLinkStreamSessionRuntime {
697
661
  );
698
662
  return;
699
663
  }
700
- if (now - this.lastInboundFrameAt >= ZLINK_STREAM_HEARTBEAT_TIMEOUT_MS) {
664
+ if (now - this.lastInboundFrameAt >= this.liveness.heartbeatTimeoutMs) {
701
665
  await this.closeForLiveness(
702
666
  ZLinkStreamCloseReasonCode.HeartbeatTimeout,
703
667
  'heartbeat_timeout',
@@ -705,7 +669,10 @@ export class ZLinkStreamSessionRuntime {
705
669
  );
706
670
  return;
707
671
  }
708
- await this.stream.writeControl(ZLINK_STREAM_HEARTBEAT_PING);
672
+ if (now - this.lastHeartbeatPingAt >= this.liveness.heartbeatIntervalMs) {
673
+ this.lastHeartbeatPingAt = now;
674
+ await this.stream.writeControl(ZLINK_STREAM_HEARTBEAT_PING);
675
+ }
709
676
  this.scheduleLivenessCheck();
710
677
  }
711
678
 
@@ -722,7 +689,9 @@ export class ZLinkStreamSessionRuntime {
722
689
  this.closeReason = closeReason;
723
690
  this.stopLivenessChecks();
724
691
  await this.stream.closeForReason(reason, diagnostic);
725
- await this.complete(undefined, true);
692
+ void this.serial
693
+ .executeFinal(async () => this.complete(undefined, true))
694
+ .catch((error) => this.options.onError?.(error));
726
695
  }
727
696
 
728
697
  private stopLivenessChecks(): void {
@@ -853,18 +822,6 @@ export class ZLinkStreamSessionRuntime {
853
822
  }
854
823
  }
855
824
 
856
- private enqueueInfrastructure(
857
- work: () => Promise<void>,
858
- onRejected?: () => void,
859
- onShutdownRejected?: () => void
860
- ): void {
861
- if (!this.serial.executeInfrastructure(work, {}, onRejected)) {
862
- // The serial lane only refuses new work after dispose() closed it.
863
- onShutdownRejected?.();
864
- onRejected?.();
865
- }
866
- }
867
-
868
825
  private enqueueApplication(
869
826
  work: () => Promise<void>,
870
827
  onRejected?: (error?: unknown) => void,
@@ -1,25 +0,0 @@
1
- import type { Message } from '../../contracts/Common/Message';
2
- export declare class ZLinkPendingSessionRequest {
3
- private readonly tracker;
4
- readonly requestSeq: bigint;
5
- private completed;
6
- private readonly timeout;
7
- private resolvePromise;
8
- private rejectPromise;
9
- readonly promise: Promise<Message>;
10
- constructor(tracker: ZLinkSessionRequestTracker, requestSeq: bigint, timeoutMs: number | undefined);
11
- complete(payload: Message): void;
12
- fail(error: unknown): void;
13
- cancel(): void;
14
- dispose(): void;
15
- private clearTimeout;
16
- }
17
- export declare class ZLinkSessionRequestTracker {
18
- private readonly pending;
19
- private nextRequestSeq;
20
- start(timeoutMs?: number): ZLinkPendingSessionRequest;
21
- complete(requestSeq: bigint, payload: Message): boolean;
22
- fail(requestSeq: bigint, error: unknown): boolean;
23
- remove(requestSeq: bigint): void;
24
- private next;
25
- }
@@ -1,101 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ZLinkSessionRequestTracker = exports.ZLinkPendingSessionRequest = void 0;
4
- const stream_wire_1 = require("@zlink-systems/stream-wire");
5
- const stream_message_utils_1 = require("./stream-message-utils");
6
- class ZLinkPendingSessionRequest {
7
- tracker;
8
- requestSeq;
9
- completed = false;
10
- timeout;
11
- resolvePromise;
12
- rejectPromise;
13
- promise;
14
- constructor(tracker, requestSeq, timeoutMs) {
15
- this.tracker = tracker;
16
- this.requestSeq = requestSeq;
17
- this.promise = new Promise((resolve, reject) => {
18
- this.resolvePromise = resolve;
19
- this.rejectPromise = reject;
20
- });
21
- if (timeoutMs !== undefined) {
22
- this.timeout = setTimeout(() => this.cancel(), timeoutMs);
23
- }
24
- }
25
- complete(payload) {
26
- if (this.completed) {
27
- return;
28
- }
29
- this.completed = true;
30
- this.clearTimeout();
31
- this.resolvePromise((0, stream_message_utils_1.copyMessage)(payload));
32
- }
33
- fail(error) {
34
- if (this.completed) {
35
- return;
36
- }
37
- this.completed = true;
38
- this.clearTimeout();
39
- this.rejectPromise(error);
40
- }
41
- cancel() {
42
- if (this.completed) {
43
- return;
44
- }
45
- this.completed = true;
46
- this.clearTimeout();
47
- this.tracker.remove(this.requestSeq);
48
- this.rejectPromise(new Error('Client stream request timed out.'));
49
- }
50
- dispose() {
51
- this.clearTimeout();
52
- this.tracker.remove(this.requestSeq);
53
- }
54
- clearTimeout() {
55
- if (this.timeout !== undefined) {
56
- clearTimeout(this.timeout);
57
- }
58
- }
59
- }
60
- exports.ZLinkPendingSessionRequest = ZLinkPendingSessionRequest;
61
- class ZLinkSessionRequestTracker {
62
- pending = new Map();
63
- nextRequestSeq = 0n;
64
- start(timeoutMs) {
65
- const requestSeq = this.next();
66
- const pending = new ZLinkPendingSessionRequest(this, requestSeq, timeoutMs);
67
- if (this.pending.has(requestSeq)) {
68
- throw new Error('Duplicate stream request sequence.');
69
- }
70
- this.pending.set(requestSeq, pending);
71
- return pending;
72
- }
73
- complete(requestSeq, payload) {
74
- const pending = this.pending.get(requestSeq);
75
- if (pending === undefined) {
76
- return false;
77
- }
78
- this.pending.delete(requestSeq);
79
- pending.complete(payload);
80
- return true;
81
- }
82
- fail(requestSeq, error) {
83
- const pending = this.pending.get(requestSeq);
84
- if (pending === undefined) {
85
- return false;
86
- }
87
- this.pending.delete(requestSeq);
88
- pending.fail(error);
89
- return true;
90
- }
91
- remove(requestSeq) {
92
- this.pending.delete(requestSeq);
93
- }
94
- next() {
95
- do {
96
- this.nextRequestSeq = (this.nextRequestSeq + 1n) & stream_wire_1.UINT64_MAX;
97
- } while (this.nextRequestSeq === 0n);
98
- return this.nextRequestSeq;
99
- }
100
- }
101
- exports.ZLinkSessionRequestTracker = ZLinkSessionRequestTracker;
@@ -1,110 +0,0 @@
1
- import { UINT64_MAX } from '@zlink-systems/stream-wire';
2
- import type { Message } from '../../contracts/Common/Message';
3
- import { copyMessage } from './stream-message-utils';
4
-
5
- export class ZLinkPendingSessionRequest {
6
- private completed = false;
7
- private readonly timeout: ReturnType<typeof setTimeout> | undefined;
8
- private resolvePromise!: (message: Message) => void;
9
- private rejectPromise!: (error: unknown) => void;
10
- readonly promise: Promise<Message>;
11
-
12
- constructor(
13
- private readonly tracker: ZLinkSessionRequestTracker,
14
- readonly requestSeq: bigint,
15
- timeoutMs: number | undefined
16
- ) {
17
- this.promise = new Promise<Message>((resolve, reject) => {
18
- this.resolvePromise = resolve;
19
- this.rejectPromise = reject;
20
- });
21
- if (timeoutMs !== undefined) {
22
- this.timeout = setTimeout(() => this.cancel(), timeoutMs);
23
- }
24
- }
25
-
26
- complete(payload: Message): void {
27
- if (this.completed) {
28
- return;
29
- }
30
- this.completed = true;
31
- this.clearTimeout();
32
- this.resolvePromise(copyMessage(payload));
33
- }
34
-
35
- fail(error: unknown): void {
36
- if (this.completed) {
37
- return;
38
- }
39
- this.completed = true;
40
- this.clearTimeout();
41
- this.rejectPromise(error);
42
- }
43
-
44
- cancel(): void {
45
- if (this.completed) {
46
- return;
47
- }
48
- this.completed = true;
49
- this.clearTimeout();
50
- this.tracker.remove(this.requestSeq);
51
- this.rejectPromise(new Error('Client stream request timed out.'));
52
- }
53
-
54
- dispose(): void {
55
- this.clearTimeout();
56
- this.tracker.remove(this.requestSeq);
57
- }
58
-
59
- private clearTimeout(): void {
60
- if (this.timeout !== undefined) {
61
- clearTimeout(this.timeout);
62
- }
63
- }
64
- }
65
-
66
- export class ZLinkSessionRequestTracker {
67
- private readonly pending = new Map<bigint, ZLinkPendingSessionRequest>();
68
- private nextRequestSeq = 0n;
69
-
70
- start(timeoutMs?: number): ZLinkPendingSessionRequest {
71
- const requestSeq = this.next();
72
- const pending = new ZLinkPendingSessionRequest(this, requestSeq, timeoutMs);
73
- if (this.pending.has(requestSeq)) {
74
- throw new Error('Duplicate stream request sequence.');
75
- }
76
- this.pending.set(requestSeq, pending);
77
- return pending;
78
- }
79
-
80
- complete(requestSeq: bigint, payload: Message): boolean {
81
- const pending = this.pending.get(requestSeq);
82
- if (pending === undefined) {
83
- return false;
84
- }
85
- this.pending.delete(requestSeq);
86
- pending.complete(payload);
87
- return true;
88
- }
89
-
90
- fail(requestSeq: bigint, error: unknown): boolean {
91
- const pending = this.pending.get(requestSeq);
92
- if (pending === undefined) {
93
- return false;
94
- }
95
- this.pending.delete(requestSeq);
96
- pending.fail(error);
97
- return true;
98
- }
99
-
100
- remove(requestSeq: bigint): void {
101
- this.pending.delete(requestSeq);
102
- }
103
-
104
- private next(): bigint {
105
- do {
106
- this.nextRequestSeq = (this.nextRequestSeq + 1n) & UINT64_MAX;
107
- } while (this.nextRequestSeq === 0n);
108
- return this.nextRequestSeq;
109
- }
110
- }