@zlink-systems/framework 0.22.0 → 0.23.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 (100) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
  3. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
  4. package/dist/runtime/actors/actor-context.js +4 -4
  5. package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
  6. package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
  7. package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
  8. package/dist/runtime/backend/contracts/index.d.ts +1 -0
  9. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +3 -0
  10. package/dist/runtime/backend/node/node-raw-mesh-backend.js +21 -8
  11. package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
  12. package/dist/runtime/channels/channel-transports.js +3 -9
  13. package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
  14. package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
  15. package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
  16. package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
  17. package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
  18. package/dist/runtime/diagnostics/flow-context.js +2 -2
  19. package/dist/runtime/diagnostics/message-flow.js +11 -3
  20. package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
  21. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
  22. package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
  23. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
  24. package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -0
  25. package/dist/runtime/foundation/service-stateful-runtime.js +46 -2
  26. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
  27. package/dist/runtime/foundation/service-topology-registry.js +12 -4
  28. package/dist/runtime/host/index.js +1 -1
  29. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
  30. package/dist/runtime/spots/index.js +1 -1
  31. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
  32. package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
  33. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  34. package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
  35. package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
  36. package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
  37. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
  38. package/dist/runtime/spots/spot-timer.js +1 -1
  39. package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
  40. package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
  41. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
  42. package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
  43. package/dist/runtime/streams/bound-session-response-target.js +8 -1
  44. package/dist/runtime/streams/bound-session-service.d.ts +1 -0
  45. package/dist/runtime/streams/bound-session-service.js +6 -3
  46. package/dist/runtime/streams/index.js +8 -1
  47. package/dist/runtime/streams/managed-stream.d.ts +3 -0
  48. package/dist/runtime/streams/managed-stream.js +20 -1
  49. package/dist/runtime/streams/protocol.d.ts +5 -1
  50. package/dist/runtime/streams/protocol.js +41 -4
  51. package/dist/runtime/streams/session-actor-coordinator.js +3 -4
  52. package/dist/runtime/streams/session-context.d.ts +9 -2
  53. package/dist/runtime/streams/session-context.js +20 -4
  54. package/dist/runtime/streams/session-local-actors.d.ts +3 -1
  55. package/dist/runtime/streams/session-local-actors.js +17 -2
  56. package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
  57. package/dist/runtime/streams/stream-frame-factory.js +3 -2
  58. package/dist/runtime/streams/stream-session-runtime.js +42 -12
  59. package/package.json +3 -3
  60. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
  61. package/src/contracts/Streams/IZLinkSession.ts +2 -1
  62. package/src/runtime/actors/actor-context.ts +4 -4
  63. package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
  64. package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
  65. package/src/runtime/backend/contracts/index.ts +4 -0
  66. package/src/runtime/backend/node/node-raw-mesh-backend.ts +39 -11
  67. package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
  68. package/src/runtime/channels/channel-transports.ts +3 -9
  69. package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
  70. package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
  71. package/src/runtime/diagnostics/flow-context.ts +6 -4
  72. package/src/runtime/diagnostics/message-flow.ts +12 -3
  73. package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
  74. package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
  75. package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
  76. package/src/runtime/foundation/service-stateful-runtime.ts +64 -5
  77. package/src/runtime/foundation/service-topology-registry.ts +18 -5
  78. package/src/runtime/host/index.ts +1 -1
  79. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
  80. package/src/runtime/spots/index.ts +2 -1
  81. package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
  82. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  83. package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
  84. package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
  85. package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
  86. package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
  87. package/src/runtime/spots/spot-timer.ts +6 -1
  88. package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
  89. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
  90. package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
  91. package/src/runtime/streams/bound-session-response-target.ts +10 -1
  92. package/src/runtime/streams/bound-session-service.ts +13 -3
  93. package/src/runtime/streams/index.ts +8 -1
  94. package/src/runtime/streams/managed-stream.ts +34 -12
  95. package/src/runtime/streams/protocol.ts +44 -5
  96. package/src/runtime/streams/session-actor-coordinator.ts +12 -4
  97. package/src/runtime/streams/session-context.ts +31 -4
  98. package/src/runtime/streams/session-local-actors.ts +19 -3
  99. package/src/runtime/streams/stream-frame-factory.ts +4 -2
  100. package/src/runtime/streams/stream-session-runtime.ts +58 -13
@@ -5,6 +5,7 @@ import type { ZLinkFlowOrigin } from '../../contracts';
5
5
  export interface ZLinkFlowContextValue {
6
6
  readonly flowId: string;
7
7
  readonly flowOrigin: ZLinkFlowOrigin;
8
+ readonly streamSessionId?: string;
8
9
  }
9
10
 
10
11
  const flowStorage = new AsyncLocalStorage<ZLinkFlowContextValue | undefined>();
@@ -71,14 +72,15 @@ export function runWithOutboundFlow<T>(enabled: boolean, callback: () => T): T {
71
72
  }
72
73
 
73
74
  export function createInboundFlow(
74
- flowId?: string,
75
- flowOrigin?: ZLinkFlowOrigin,
76
- createIfAbsent = true
75
+ flowId: string | undefined,
76
+ flowOrigin: ZLinkFlowOrigin | undefined,
77
+ createIfAbsent: boolean,
78
+ streamSessionId: string | undefined
77
79
  ): ZLinkFlowContextValue | undefined {
78
80
  if (!createIfAbsent) {
79
81
  return undefined;
80
82
  }
81
- return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound' };
83
+ return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound', streamSessionId };
82
84
  }
83
85
 
84
86
  function createFlowId(): string {
@@ -34,6 +34,7 @@ export const DEFAULT_ZLINK_DIAGNOSTICS: ZLinkDiagnosticsOptions = {
34
34
 
35
35
  const telemetryLogger = openTelemetryLogs.getLogger('@zlink-systems/framework');
36
36
  const telemetryTracer = openTelemetryTrace.getTracer('@zlink-systems/framework');
37
+ const ERROR_MESSAGE_MAX_LENGTH = 512;
37
38
  let diagnosticsGeneration = 0n;
38
39
 
39
40
  interface ZLinkTelemetryRecord {
@@ -68,6 +69,7 @@ interface ZLinkTelemetryRecord {
68
69
  readonly instanceSpotType?: string;
69
70
  readonly activationState?: string;
70
71
  readonly actorId?: string;
72
+ readonly streamSessionId?: string;
71
73
  readonly commandId?: number;
72
74
  readonly messageSizeBytes?: number;
73
75
  readonly durationSeconds?: number;
@@ -226,12 +228,13 @@ export class ZLinkMessageFlowTracer {
226
228
  acceptedFlowId?: string,
227
229
  acceptedFlowOrigin?: ZLinkFlowOrigin
228
230
  ): void {
229
- const ambient = acceptedFlowId === undefined ? currentFlowContext() : undefined;
231
+ const ambient = currentFlowContext();
230
232
  const flowId = flowInput.flowId ?? acceptedFlowId ?? ambient?.flowId;
231
233
  const flowOrigin = flowInput.flowOrigin ?? acceptedFlowOrigin ?? ambient?.flowOrigin;
232
234
  const flow: ZLinkRuntimeMessageFlowEvent = {
233
235
  ...flowInput,
234
236
  ...(flowId === undefined ? {} : { flowId, flowOrigin }),
237
+ streamSessionId: flowInput.streamSessionId ?? ambient?.streamSessionId,
235
238
  effectiveMode
236
239
  };
237
240
  this.tracedEvents += 1;
@@ -380,11 +383,12 @@ function toTelemetryRecord(
380
383
  instanceSpotType: flow.instanceSpotType,
381
384
  activationState: flow.activationState,
382
385
  actorId: flow.actorId,
386
+ streamSessionId: flow.streamSessionId,
383
387
  commandId: flow.commandId,
384
388
  messageSizeBytes,
385
389
  durationSeconds,
386
390
  errorType: flow.errorType,
387
- errorMessage: boundedText(flow.errorMessage),
391
+ errorMessage: flow.errorMessage,
388
392
  errorCauseType: flow.errorCauseType,
389
393
  errorCauseMessage: boundedText(flow.errorCauseMessage)
390
394
  };
@@ -421,6 +425,7 @@ function structuredLogAttributes(record: ZLinkTelemetryRecord): Attributes {
421
425
  instance_spot_type: record.instanceSpotType,
422
426
  activation_state: record.activationState,
423
427
  actor_id: record.actorId,
428
+ stream_session_id: record.streamSessionId,
424
429
  correlation_id: record.correlationId,
425
430
  flow_id: record.flowId,
426
431
  flow_origin: record.flowOrigin,
@@ -456,11 +461,15 @@ function structuredLogBodyAttributes(record: ZLinkTelemetryRecord): Attributes {
456
461
  instance_type: record.instanceSpotType,
457
462
  activation_state: record.activationState,
458
463
  actor: record.actorId,
464
+ session: record.streamSessionId,
459
465
  corr: record.correlationId,
460
466
  flow: record.flowId,
461
467
  origin: record.flowOrigin,
462
468
  outcome: record.outcome,
463
469
  reason: record.reason,
470
+ action: record.action,
471
+ error_type: record.errorType,
472
+ error_message: record.errorMessage,
464
473
  size: record.messageSizeBytes
465
474
  });
466
475
  }
@@ -571,7 +580,7 @@ function hashFlowId(flowId: string): number {
571
580
  }
572
581
 
573
582
  function boundedText(value: string | undefined): string | undefined {
574
- return value === undefined ? undefined : value.slice(0, 512);
583
+ return value === undefined ? undefined : value.slice(0, ERROR_MESSAGE_MAX_LENGTH);
575
584
  }
576
585
 
577
586
  function toSnakeCase(value: string): string {
@@ -10,7 +10,6 @@ import { decodeServiceWireFrozenRecord } from './service-stateful-wire-codec';
10
10
  const RECOVERY_PACKET_NAME = '__zlink.actor.routed_join.recovery';
11
11
  const RECOVERY_CONTENT_TYPE = 'application/x-zlink-actor-routed-join-recovery-v1';
12
12
  const MAXIMUM_METADATA_BYTES = 256 * 1024;
13
- const MAXIMUM_MESSAGE_BYTES = 1024 * 1024;
14
13
  const FRAMEWORK_METADATA_UPPER_BOUND_BYTES = 64 * 1024;
15
14
  const ACCEPTED_JOURNAL_UPPER_BOUND_BYTES = 16 * 1024 * 1024;
16
15
  const SNAPSHOT_APPLICATION_STATE_RESERVATION_BYTES = 64 * 1024 * 1024;
@@ -124,9 +123,6 @@ export function encodeCanonicalActorJoinRecoverySavedWork(
124
123
  const operationId = input.operationId;
125
124
  const reply = operationId === undefined ? Buffer.alloc(0) : Buffer.from(input.reply ?? []);
126
125
  const request = Buffer.from(input.request);
127
- if (request.byteLength > MAXIMUM_MESSAGE_BYTES || reply.byteLength > MAXIMUM_MESSAGE_BYTES) {
128
- throw new RangeError('Actor Join recovery message exceeds 1 MiB.');
129
- }
130
126
  const replyContentType =
131
127
  operationId === undefined
132
128
  ? null
@@ -7,7 +7,7 @@ import type {
7
7
  ZLinkRawReceivedRecord,
8
8
  ZLinkRawRouterPort
9
9
  } from '../backend/raw-binding-port';
10
- import { RequestResult } from '../backend/runtime-values';
10
+ import { RequestResult, SubmitResult } from '../backend/runtime-values';
11
11
  import type {
12
12
  ApplicationJobPermitPort,
13
13
  ApplicationJobQueuePort
@@ -63,6 +63,19 @@ export interface RawServiceRequestResult {
63
63
  readonly payload?: ServiceApplicationPayload;
64
64
  }
65
65
 
66
+ type RawServiceChannelTargetSelection =
67
+ | { readonly kind: 'selected'; readonly peer: AdmittedServicePeer }
68
+ | {
69
+ readonly kind: 'noMember';
70
+ readonly submitResult: typeof SubmitResult.NotFound;
71
+ readonly requestResult: RawServiceRequestResult;
72
+ }
73
+ | {
74
+ readonly kind: 'knownButNotReady';
75
+ readonly submitResult: typeof SubmitResult.NotConnected;
76
+ readonly requestResult: RawServiceRequestResult;
77
+ };
78
+
66
79
  /** An M6A application frame already owned by the Framework runtime. */
67
80
  type ServiceApplicationPayloadInput = ServiceApplicationPayload | Buffer;
68
81
 
@@ -463,11 +476,10 @@ export class RawServiceMeshRuntime {
463
476
  async sendToChannel(
464
477
  channelName: string,
465
478
  payload: ServiceApplicationPayloadInput
466
- ): Promise<boolean> {
467
- const selected = this.topology.selectChannel(channelName, (peer) =>
468
- this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId)
469
- );
470
- if (selected === undefined) return false;
479
+ ): Promise<SubmitResult> {
480
+ const selection = this.selectChannelTarget(channelName);
481
+ if (selection.kind !== 'selected') return selection.submitResult;
482
+ const selected = selection.peer;
471
483
  const applicationFrame = this.applicationFrame(payload);
472
484
  if (selected.descriptor.nodeRoutingId === this.descriptor.nodeRoutingId) {
473
485
  const applicationJobOwner = await this.reserveLocalIngress();
@@ -481,15 +493,17 @@ export class RawServiceMeshRuntime {
481
493
  applicationJob
482
494
  });
483
495
  if (!accepted) applicationJob.close();
484
- return accepted;
496
+ return accepted ? SubmitResult.Ok : SubmitResult.NotAdmitted;
485
497
  } finally {
486
498
  applicationJobOwner.close();
487
499
  }
488
500
  }
489
- return this.send(selected.descriptor.nodeRoutingId, [
501
+ return (await this.send(selected.descriptor.nodeRoutingId, [
490
502
  encodeChannelSendHeader(channelName),
491
503
  applicationFrame
492
- ]);
504
+ ]))
505
+ ? SubmitResult.Ok
506
+ : SubmitResult.NotConnected;
493
507
  }
494
508
 
495
509
  requestToNode(
@@ -504,13 +518,40 @@ export class RawServiceMeshRuntime {
504
518
  channelName: string,
505
519
  payload: ServiceApplicationPayloadInput,
506
520
  timeoutMs: number
507
- ): PendingOperation<RawServiceRequestResult> | undefined {
521
+ ): PendingOperation<RawServiceRequestResult> {
522
+ const selection = this.selectChannelTarget(channelName);
523
+ if (selection.kind !== 'selected') {
524
+ const pending = this.operations.register(timeoutMs);
525
+ this.operations.complete(pending.id, selection.requestResult);
526
+ return pending;
527
+ }
528
+ return this.requestToTarget(
529
+ selection.peer.descriptor.nodeRoutingId,
530
+ payload,
531
+ timeoutMs,
532
+ channelName
533
+ );
534
+ }
535
+
536
+ private selectChannelTarget(channelName: string): RawServiceChannelTargetSelection {
508
537
  const selected = this.topology.selectChannel(channelName, (peer) =>
509
538
  this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId)
510
539
  );
511
- return selected === undefined
512
- ? undefined
513
- : this.requestToTarget(selected.descriptor.nodeRoutingId, payload, timeoutMs, channelName);
540
+ if (selected !== undefined) return { kind: 'selected', peer: selected };
541
+ return this.topology.hasKnownChannelTarget(channelName)
542
+ ? {
543
+ kind: 'knownButNotReady',
544
+ submitResult: SubmitResult.NotConnected,
545
+ requestResult: { terminalResult: RequestResult.NotConnected, failureCode: 0 }
546
+ }
547
+ : {
548
+ kind: 'noMember',
549
+ submitResult: SubmitResult.NotFound,
550
+ requestResult: {
551
+ terminalResult: RequestResult.NotFound,
552
+ failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
553
+ }
554
+ };
514
555
  }
515
556
 
516
557
  private isLocalOrReadyPeer(nodeRoutingId: string): boolean {
@@ -19,6 +19,7 @@ export interface ServiceSessionBindingAdmissionClaim {
19
19
  export type ServiceSessionBindingAdmissionResult = 'passThrough' | 'retained' | 'rejected';
20
20
 
21
21
  export interface ServiceSessionBindingIngressPort {
22
+ actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
22
23
  retainOutbound(
23
24
  claim: ServiceSessionBindingAdmissionClaim,
24
25
  delivery: ServiceSessionBindingRetainedDelivery
@@ -9,6 +9,12 @@ import {
9
9
  translateWireReplyDecodeError
10
10
  } from '../framework-errors-internal';
11
11
  import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
12
+ import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
13
+ import {
14
+ ZLinkRuntimeDispatchErrorAction as ZLinkDispatchErrorAction,
15
+ ZLinkDispatchErrorSurface,
16
+ ZLinkDispatchMessageKind
17
+ } from '../../contracts/Dispatch/ZLinkDispatchOptions';
12
18
  import type {
13
19
  RawServiceIngressRecord,
14
20
  RawServiceMeshRuntime,
@@ -401,6 +407,8 @@ export class ServiceStatefulRuntime {
401
407
  readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
402
408
  readonly owner: string;
403
409
  }) => void;
410
+ private dispatchErrors?: ZLinkDispatchErrorReporter;
411
+ private dispatchErrorMeshName?: string;
404
412
  private readonly admittedUserSpotOperations = new Map<
405
413
  string,
406
414
  {
@@ -438,6 +446,11 @@ export class ServiceStatefulRuntime {
438
446
  this.mailboxDropHandler = handler;
439
447
  }
440
448
 
449
+ setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void {
450
+ this.dispatchErrors = reporter;
451
+ this.dispatchErrorMeshName = meshName;
452
+ }
453
+
441
454
  createSpot(
442
455
  routingId?: string,
443
456
  kind: 'user' | 'instance' = 'user',
@@ -1229,17 +1242,41 @@ export class ServiceStatefulRuntime {
1229
1242
  await this.enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload);
1230
1243
  const targets = this.raw.topology
1231
1244
  .peers()
1232
- .filter((peer) =>
1233
- peer.descriptor.channels.some(
1234
- (channel) => channel.name === channelName && channel.weight > 0
1235
- )
1245
+ .filter(
1246
+ (peer) =>
1247
+ peer.descriptor.channels.some(
1248
+ (channel) => channel.name === channelName && channel.weight > 0
1249
+ ) && this.raw.isPeerRouteReady(peer.descriptor.nodeRoutingId)
1236
1250
  );
1237
1251
  const header = encodeLogicalMulticastHeader(channelName, topic, sourceSpotId);
1238
1252
  const payloadFrame = encodeApplicationPayload(payload);
1253
+ const reporter = this.dispatchErrors;
1254
+ const captureFailures = reporter?.captureEnabled() === true;
1239
1255
  for (const target of targets) {
1240
1256
  // Remote admission ends at the source outbound transport queue. The
1241
1257
  // receiver's Spot queue and handler completion are not publish results.
1242
- await this.raw.sendService(target.descriptor.nodeRoutingId, [header, payloadFrame]);
1258
+ const accepted = await this.raw.sendService(target.descriptor.nodeRoutingId, [
1259
+ header,
1260
+ payloadFrame
1261
+ ]);
1262
+ if (!accepted) {
1263
+ if (captureFailures) {
1264
+ reporter.report({
1265
+ surface: ZLinkDispatchErrorSurface.SpotRoute,
1266
+ messageKind: ZLinkDispatchMessageKind.Send,
1267
+ reason: serviceSpotPublishFailureReason(
1268
+ undefined,
1269
+ this.raw.isPeerRouteReady(target.descriptor.nodeRoutingId),
1270
+ this.closed
1271
+ ),
1272
+ action: ZLinkDispatchErrorAction.Drop,
1273
+ meshName: this.dispatchErrorMeshName,
1274
+ channelName,
1275
+ topic,
1276
+ targetRid: target.descriptor.nodeRoutingId
1277
+ });
1278
+ }
1279
+ }
1243
1280
  }
1244
1281
  }
1245
1282
 
@@ -5251,6 +5288,28 @@ export class ServiceStatefulRuntime {
5251
5288
  }
5252
5289
  }
5253
5290
 
5291
+ function serviceSpotPublishFailureReason(
5292
+ result: number | undefined,
5293
+ routeReady: boolean,
5294
+ closed: boolean
5295
+ ): 'stale_target' | 'backpressure' | 'shutdown' {
5296
+ if (closed || result === SubmitResult.Terminated) return 'shutdown';
5297
+ switch (result) {
5298
+ case SubmitResult.Backpressured:
5299
+ return 'backpressure';
5300
+ case SubmitResult.NotAdmitted:
5301
+ case SubmitResult.NotConnected:
5302
+ case SubmitResult.NotFound:
5303
+ case SubmitResult.InvalidHandle:
5304
+ return 'stale_target';
5305
+ default:
5306
+ // RawServiceMeshRuntime exposes failed Core submissions as false. A
5307
+ // ready route therefore identifies queue admission failure; a route
5308
+ // that became unready after selection is a stale target.
5309
+ return routeReady ? 'backpressure' : 'stale_target';
5310
+ }
5311
+ }
5312
+
5254
5313
  function isEntrySpotFence(fence: ServiceDirectSpotRouteFence): boolean {
5255
5314
  return (
5256
5315
  fence.spot.spotId === fence.targetNodeRid &&
@@ -257,6 +257,13 @@ export class ServiceTopologyRegistry {
257
257
  return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
258
258
  }
259
259
 
260
+ hasKnownChannelTarget(channelName: string): boolean {
261
+ requireText(channelName, 'channelName');
262
+ return [this.local, ...this.knownByRid.values()].some(
263
+ (descriptor) => selectableChannel(descriptor, channelName) !== undefined
264
+ );
265
+ }
266
+
260
267
  private remember(descriptor: ServiceNodeDescriptor): void {
261
268
  const current = this.knownByRid.get(descriptor.nodeRoutingId);
262
269
  if (
@@ -285,7 +292,7 @@ export class ServiceTopologyRegistry {
285
292
  return [local, ...this.peersByRid.values()]
286
293
  .map((peer) => ({
287
294
  peer: clonePeer(peer),
288
- channel: findChannel(peer.descriptor, channelName)
295
+ channel: selectableChannel(peer.descriptor, channelName)
289
296
  }))
290
297
  .filter(
291
298
  (
@@ -293,10 +300,7 @@ export class ServiceTopologyRegistry {
293
300
  ): value is {
294
301
  peer: AdmittedServicePeer;
295
302
  channel: ServiceChannelDescriptor;
296
- } =>
297
- value.channel !== undefined &&
298
- value.peer.descriptor.state === 'serving' &&
299
- value.channel.weight > 0
303
+ } => value.channel !== undefined
300
304
  );
301
305
  },
302
306
  (value) => value.channel.weight,
@@ -519,6 +523,15 @@ function findChannel(
519
523
  return descriptor.channels.find((channel) => channel.name === channelName);
520
524
  }
521
525
 
526
+ function selectableChannel(
527
+ descriptor: ServiceNodeDescriptor,
528
+ channelName: string
529
+ ): ServiceChannelDescriptor | undefined {
530
+ if (descriptor.state !== 'serving') return undefined;
531
+ const channel = findChannel(descriptor, channelName);
532
+ return channel !== undefined && channel.weight > 0 ? channel : undefined;
533
+ }
534
+
522
535
  function requireText(value: string, field: string): void {
523
536
  if (value.length === 0 || value.includes('\0')) {
524
537
  throw new TypeError(`${field} must be non-empty text without NUL.`);
@@ -3047,7 +3047,7 @@ export class ZLinkFrameworkRuntimeHost
3047
3047
  const flow =
3048
3048
  current?.flowOrigin === 'Lifecycle'
3049
3049
  ? current
3050
- : createInboundFlow(undefined, 'Lifecycle', this.flowCreationEnabled());
3050
+ : createInboundFlow(undefined, 'Lifecycle', this.flowCreationEnabled(), undefined);
3051
3051
  return runWithFlow(flow, operation);
3052
3052
  }
3053
3053
 
@@ -6,14 +6,15 @@ export interface ServiceWireRouteFence { readonly id: string; readonly generatio
6
6
  function u64(out: number[], value: bigint): void { if (value < 0n || value > 0xffffffffffffffffn) throw new RangeError("u64"); for (let i=7;i>=0;i--) out.push(Number((value >> BigInt(i*8)) & 255n)); }
7
7
  function read64(bytes: Uint8Array, at: { value: number }): bigint { if (at.value + 8 > bytes.length) throw new RangeError("truncated u64"); let r=0n; for(let i=0;i<8;i++) r=(r<<8n)|BigInt(bytes[at.value++]); return r; }
8
8
  function u32(out: number[], value: number): void { if(!Number.isInteger(value)||value<0||value>0xffffffff)throw new RangeError("u32");out.push(value>>>24,(value>>>16)&255,(value>>>8)&255,value&255); }
9
+ function appendBytes(out: number[], bytes: Uint8Array | readonly number[]): void { for (const byte of bytes) out.push(byte); }
9
10
  function read32(bytes: Uint8Array, at: { value: number }): number { if(at.value+4>bytes.length)throw new RangeError("truncated u32");return ((bytes[at.value++]<<24)|(bytes[at.value++]<<16)|(bytes[at.value++]<<8)|bytes[at.value++])>>>0; }
10
- function text8(out: number[], value: string): void { const b=text.encode(value); if(b.length===0 || b.length>255 || b.includes(0)) throw new RangeError("text8"); out.push(b.length,...b); }
11
+ function text8(out: number[], value: string): void { const b=text.encode(value); if(b.length===0 || b.length>255 || b.includes(0)) throw new RangeError("text8"); out.push(b.length); appendBytes(out,b); }
11
12
  function readText8(bytes: Uint8Array, at: { value: number }): string { const n=bytes[at.value++]; if(n===0 || at.value+n>bytes.length) throw new RangeError("truncated text8"); const b=bytes.slice(at.value,at.value+n); at.value+=n; if(b.includes(0))throw new RangeError("text8 NUL"); return decodeText.decode(b); }
12
- function rid(out: number[], value: Uint8Array): void { if(value.length===0 || value.length>255) throw new RangeError("rid"); out.push(value.length,...value); }
13
+ function rid(out: number[], value: Uint8Array): void { if(value.length===0 || value.length>255) throw new RangeError("rid"); out.push(value.length); appendBytes(out,value); }
13
14
  function readRid(bytes: Uint8Array, at: { value: number }): Uint8Array { const n=bytes[at.value++]; if(n===0 || at.value+n>bytes.length) throw new RangeError("truncated rid"); const r=bytes.slice(at.value,at.value+n); at.value+=n; return r; }
14
15
  function fence(out: number[], value: ServiceWireRouteFence): void { if(value.generation===0n||value.targetNodeGeneration===0n||value.expectedAuthorityOwnerGeneration===0n||value.expectedOwnerLeaseGeneration===0n)throw new RangeError("invalid fence");text8(out,value.id); u64(out,value.generation); rid(out,value.targetNodeRid); u64(out,value.targetNodeGeneration); u64(out,value.expectedAuthorityOwnerGeneration); u64(out,value.expectedOwnerLeaseGeneration); }
15
16
  function readFence(bytes: Uint8Array, at: { value: number }): ServiceWireRouteFence { const value={ id:readText8(bytes,at), generation:read64(bytes,at), targetNodeRid:readRid(bytes,at), targetNodeGeneration:read64(bytes,at), expectedAuthorityOwnerGeneration:read64(bytes,at), expectedOwnerLeaseGeneration:read64(bytes,at) };if(value.generation===0n||value.targetNodeGeneration===0n||value.expectedAuthorityOwnerGeneration===0n||value.expectedOwnerLeaseGeneration===0n)throw new RangeError("invalid fence");return value; }
16
- function encodePayload(value: ApplicationPayloadEnvelopeV1): Uint8Array { const body:number[]=[];text8(body,value.packetName);text8(body,value.contentType);u32(body,value.payload.length);body.push(...value.payload);const out=[1];u32(out,body.length);out.push(...body);return Uint8Array.from(out); }
17
+ function encodePayload(value: ApplicationPayloadEnvelopeV1): Uint8Array { const body:number[]=[];text8(body,value.packetName);text8(body,value.contentType);u32(body,value.payload.length);appendBytes(body,value.payload);const out=[1];u32(out,body.length);appendBytes(out,body);return Uint8Array.from(out); }
17
18
  function decodePayload(bytes: Uint8Array): ApplicationPayloadEnvelopeV1 { const at={value:0};if(bytes[at.value++]!==1)throw new RangeError("payload version");const length=read32(bytes,at);if(length!==bytes.length-at.value)throw new RangeError("payload length");const end=bytes.length;const packetName=readText8(bytes,at);const contentType=readText8(bytes,at);const length2=read32(bytes,at);if(length2!==end-at.value)throw new RangeError("payload bytes");const payload=bytes.slice(at.value);at.value=end;if(at.value!==end)throw new RangeError("payload trailing");return {packetName,contentType,payload}; }
18
19
  export function encodeActorJoin28(value: ActorJoin28): readonly Uint8Array[] { const out=[90,77,1,28,0]; if(value.correlation===0n) throw new RangeError("correlation"); u64(out,value.correlation); fence(out,value.actor); out.push(value.entry?1:0); fence(out,value.targetSpot); return value.payload===undefined?[Uint8Array.from(out)]:[Uint8Array.from(out),encodePayload(value.payload)]; }
19
20
  export function decodeActorJoin28(frames: readonly Uint8Array[]): ActorJoin28 { if(frames.length<1||frames.length>2)throw new RangeError("actorJoin frames");const bytes=frames[0]!; const at={value:0}; if(bytes.length<5 || bytes[at.value++]!==90 || bytes[at.value++]!==77 || bytes[at.value++]!==1 || bytes[at.value++]!==28 || bytes[at.value++]!==0) throw new RangeError("actorJoin header"); const correlation=read64(bytes,at); const actor=readFence(bytes,at); const entry=bytes[at.value++]; const targetSpot=readFence(bytes,at); if(correlation===0n || entry>1 || at.value!==bytes.length) throw new RangeError("actorJoin body"); const payload=frames.length===2?decodePayload(frames[1]!):undefined; return payload===undefined?{ correlation, actor, entry:entry===1, targetSpot }:{ correlation, actor, entry:entry===1, targetSpot,payload }; }
@@ -22,16 +23,16 @@ export interface RelocationEnvelopeV1 { readonly relocationHigh: bigint; readonl
22
23
  export interface RelocationDataChunkV1 { readonly order:number;readonly data:Uint8Array; }
23
24
  export interface RelocationChunkEntryV1 { readonly order:number;readonly reference:string;readonly length:bigint;readonly checksumCrc32c:number; }
24
25
  export interface RelocationManifestV1 { readonly logicalFormatVersion:1;readonly totalLength:bigint;readonly totalChecksumCrc32c:number;readonly inventoryDigestSha256:Uint8Array;readonly chunks:readonly RelocationChunkEntryV1[]; }
25
- function bytes64(out:number[],v:Uint8Array):void{u64(out,BigInt(v.length));out.push(...v);}
26
- function body16(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);if(b.length>65535)throw new RangeError("body16");out.push(b.length>>>8,b.length&255,...b);}
27
- function body64(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);u64(out,BigInt(b.length));out.push(...b);}
26
+ function bytes64(out:number[],v:Uint8Array):void{u64(out,BigInt(v.length));appendBytes(out,v);}
27
+ function body16(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);if(b.length>65535)throw new RangeError("body16");out.push(b.length>>>8,b.length&255);appendBytes(out,b);}
28
+ function body64(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);u64(out,BigInt(b.length));appendBytes(out,b);}
28
29
  function sorted<T>(v:readonly T[],compare:(a:T,b:T)=>number,name:string):void{for(let i=1;i<v.length;i++)if(compare(v[i-1],v[i])>=0)throw new RangeError(name);}
29
30
  function read8(bytes:Uint8Array,at:{value:number}):number{if(at.value>=bytes.length)throw new RangeError("truncated u8");return bytes[at.value++]!;}
30
31
  function read16(bytes:Uint8Array,at:{value:number}):number{if(at.value+2>bytes.length)throw new RangeError("truncated u16");return (bytes[at.value++]!<<8)|bytes[at.value++]!;}
31
32
  function take(bytes:Uint8Array,at:{value:number},n:number,name:string):Uint8Array<ArrayBuffer>{if(!Number.isSafeInteger(n)||n<0||at.value+n>bytes.length)throw new RangeError("truncated "+name);const v=Uint8Array.from(bytes.subarray(at.value,at.value+n));at.value+=n;return v;}
32
33
  function nonzero64(bytes:Uint8Array,at:{value:number},name:string):bigint{const v=read64(bytes,at);if(v===0n)throw new RangeError(name);return v;}
33
34
  function readText16(bytes:Uint8Array,at:{value:number},minimum:number,maximum:number,name:string):string{const n=read16(bytes,at);if(n<minimum||n>maximum)throw new RangeError(name);const b=take(bytes,at,n,name);if(b.includes(0))throw new RangeError(name+" NUL");return decodeText.decode(b);}
34
- function writeText16(out:number[],value:string,minimum:number,maximum:number,name:string):void{const b=text.encode(value);if(b.length<minimum||b.length>maximum||b.includes(0))throw new RangeError(name);out.push(b.length>>>8,b.length&255,...b);}
35
+ function writeText16(out:number[],value:string,minimum:number,maximum:number,name:string):void{const b=text.encode(value);if(b.length<minimum||b.length>maximum||b.includes(0))throw new RangeError(name);out.push(b.length>>>8,b.length&255);appendBytes(out,b);}
35
36
  function boundedEnd(bytes:Uint8Array,at:{value:number},length:number,name:string):number{if(at.value+length>bytes.length)throw new RangeError("truncated "+name);return at.value+length;}
36
37
  function exactEnd(at:{value:number},end:number,name:string):void{if(at.value!==end)throw new RangeError(name+" trailing");}
37
38
  function skipRid(bytes:Uint8Array,at:{value:number}):void{const n=read8(bytes,at);if(n===0)throw new RangeError("rid minimumBytes");take(bytes,at,n,"rid");}
@@ -50,14 +51,14 @@ function skipFrozenRecord(bytes:Uint8Array,at:{value:number}):void{const kind=re
50
51
  function compareUtf8(a:string,b:string):number{const x=text.encode(a),y=text.encode(b),n=Math.min(x.length,y.length);for(let i=0;i<n;i++)if(x[i]!==y[i])return x[i]!-y[i]!;return x.length-y.length;}
51
52
  function writeRelocationObject(out:number[],value:RelocationObjectIdentity):void{const kind=value.objectKind==="actor"?1:value.objectKind==="userSpot"?2:3;out.push(kind);body16(out,b=>{if(value.objectKind==="actor"){text8(b,value.actorId);if(value.objectGeneration===0n||value.expectedAuthorityOwnerGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);u64(b,value.expectedAuthorityOwnerGeneration);}else if(value.objectKind==="userSpot"){text8(b,value.spotId);if(value.objectGeneration===0n||value.expectedAuthorityOwnerGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);u64(b,value.expectedAuthorityOwnerGeneration);}else{text8(b,value.instanceType);text8(b,value.spotId);if(value.objectGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);}});}
52
53
  function readRelocationObject(bytes:Uint8Array,at:{value:number}):RelocationObjectIdentity{const kind=read8(bytes,at),end=boundedEnd(bytes,at,read16(bytes,at),"relocation object");let value:RelocationObjectIdentity;if(kind===1)value={objectKind:"actor",actorId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation")};else if(kind===2)value={objectKind:"userSpot",spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation")};else if(kind===3)value={objectKind:"instanceSpot",instanceType:readText8(bytes,at),spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation")};else throw new RangeError("object kind");exactEnd(at,end,"relocation object");return value;}
53
- export function encodeRelocationEnvelopeV1(value:RelocationEnvelopeV1):Uint8Array{const out:number[]=[];if(value.relocationHigh===0n&&value.relocationLow===0n)throw new RangeError("relocation");u64(out,value.relocationHigh);u64(out,value.relocationLow);writeRelocationObject(out,value.object);if(value.applicationVersion<0n||value.applicationVersion>9223372036854775807n)throw new RangeError("application version");u64(out,value.applicationVersion);sorted(value.applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");if(value.applicationStates.length===0)throw new RangeError("applicationStates");u32(out,value.applicationStates.length);for(const s of value.applicationStates){if(s.participantId===0n)throw new RangeError("participant");u64(out,s.participantId);out.push(s.hasState?1:0);body64(out,b=>{if(s.hasState)bytes64(b,s.payload);else if(s.payload.length!==0)throw new RangeError("absent state payload");});}sorted(value.savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");u32(out,value.savedWork.length);for(const w of value.savedWork){if(w.participantId===0n||w.order===0n)throw new RangeError("saved work");const at={value:0};skipFrozenRecord(w.frozenRecord,at);exactEnd(at,w.frozenRecord.length,"frozen record");u64(out,w.participantId);u64(out,w.order);out.push(...w.frozenRecord);}sorted(value.timerRegistrations,(a,b)=>a.participantId===b.participantId?compareUtf8(a.name,b.name):a.participantId<b.participantId?-1:1,"timerRegistrations");u32(out,value.timerRegistrations.length);for(const t of value.timerRegistrations){if(t.participantId===0n||t.periodMilliseconds===0n||t.maxCatchUpTicks===0n||t.overrunPolicy<1||t.overrunPolicy>3)throw new RangeError("timer");u64(out,t.participantId);text8(out,t.name);text8(out,t.handlerType);u64(out,t.periodMilliseconds);out.push(t.overrunPolicy);u64(out,t.maxCatchUpTicks);out.push(t.stopOnUnhandledException?1:0);u64(out,t.lastCompletedDeliveryIndex);u64(out,t.lastCompletedScheduledIndex);u64(out,t.nextScheduledAtUnixMilliseconds);}sorted(value.pendingTimerTicks,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"pendingTimerTicks");u32(out,value.pendingTimerTicks.length);for(const p of value.pendingTimerTicks){if(p.participantId===0n||p.order===0n||p.deliveryIndex===0n||p.scheduledIndex===0n)throw new RangeError("pending timer");u64(out,p.participantId);u64(out,p.order);text8(out,p.timerName);u64(out,p.deliveryIndex);u64(out,p.scheduledIndex);u64(out,p.scheduledAtUnixMilliseconds);u64(out,p.skippedTicks);}if(out.length>274877906944)throw new RangeError("relocation logical bytes");return Uint8Array.from(out);}
54
+ export function encodeRelocationEnvelopeV1(value:RelocationEnvelopeV1):Uint8Array{const out:number[]=[];if(value.relocationHigh===0n&&value.relocationLow===0n)throw new RangeError("relocation");u64(out,value.relocationHigh);u64(out,value.relocationLow);writeRelocationObject(out,value.object);if(value.applicationVersion<0n||value.applicationVersion>9223372036854775807n)throw new RangeError("application version");u64(out,value.applicationVersion);sorted(value.applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");if(value.applicationStates.length===0)throw new RangeError("applicationStates");u32(out,value.applicationStates.length);for(const s of value.applicationStates){if(s.participantId===0n)throw new RangeError("participant");u64(out,s.participantId);out.push(s.hasState?1:0);body64(out,b=>{if(s.hasState)bytes64(b,s.payload);else if(s.payload.length!==0)throw new RangeError("absent state payload");});}sorted(value.savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");u32(out,value.savedWork.length);for(const w of value.savedWork){if(w.participantId===0n||w.order===0n)throw new RangeError("saved work");const at={value:0};skipFrozenRecord(w.frozenRecord,at);exactEnd(at,w.frozenRecord.length,"frozen record");u64(out,w.participantId);u64(out,w.order);appendBytes(out,w.frozenRecord);}sorted(value.timerRegistrations,(a,b)=>a.participantId===b.participantId?compareUtf8(a.name,b.name):a.participantId<b.participantId?-1:1,"timerRegistrations");u32(out,value.timerRegistrations.length);for(const t of value.timerRegistrations){if(t.participantId===0n||t.periodMilliseconds===0n||t.maxCatchUpTicks===0n||t.overrunPolicy<1||t.overrunPolicy>3)throw new RangeError("timer");u64(out,t.participantId);text8(out,t.name);text8(out,t.handlerType);u64(out,t.periodMilliseconds);out.push(t.overrunPolicy);u64(out,t.maxCatchUpTicks);out.push(t.stopOnUnhandledException?1:0);u64(out,t.lastCompletedDeliveryIndex);u64(out,t.lastCompletedScheduledIndex);u64(out,t.nextScheduledAtUnixMilliseconds);}sorted(value.pendingTimerTicks,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"pendingTimerTicks");u32(out,value.pendingTimerTicks.length);for(const p of value.pendingTimerTicks){if(p.participantId===0n||p.order===0n||p.deliveryIndex===0n||p.scheduledIndex===0n)throw new RangeError("pending timer");u64(out,p.participantId);u64(out,p.order);text8(out,p.timerName);u64(out,p.deliveryIndex);u64(out,p.scheduledIndex);u64(out,p.scheduledAtUnixMilliseconds);u64(out,p.skippedTicks);}if(out.length>274877906944)throw new RangeError("relocation logical bytes");return Uint8Array.from(out);}
54
55
  export function decodeRelocationEnvelopeV1(chunks:readonly Uint8Array[]):RelocationEnvelopeV1{const n=chunks.reduce((s,c)=>s+c.length,0);if(n>274877906944)throw new RangeError("relocation logical bytes");const bytes=new Uint8Array(n);let offset=0;for(const c of chunks){bytes.set(c,offset);offset+=c.length;}const at={value:0},relocationHigh=read64(bytes,at),relocationLow=read64(bytes,at);if(relocationHigh===0n&&relocationLow===0n)throw new RangeError("relocation");const object=readRelocationObject(bytes,at),applicationVersion=read64(bytes,at);if(applicationVersion>9223372036854775807n)throw new RangeError("application version");const applicationStates:RelocationEnvelopeV1["applicationStates"][number][]=[];const stateCount=read32(bytes,at);if(stateCount===0)throw new RangeError("applicationStates");for(let i=0,count=stateCount;i<count;i++){const participantId=nonzero64(bytes,at,"participant"),has=read8(bytes,at),end=boundedEnd(bytes,at,Number(read64(bytes,at)),"application state");if(has>1)throw new RangeError("application state");let payload=new Uint8Array();if(has===1)payload=take(bytes,at,Number(read64(bytes,at)),"state payload");exactEnd(at,end,"application state");applicationStates.push({participantId,hasState:has===1,payload});}const participants=new Set(applicationStates.map((s)=>s.participantId));const savedWork:RelocationEnvelopeV1["savedWork"][number][]=[];for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("saved work participant");const order=nonzero64(bytes,at,"order"),start=at.value;skipFrozenRecord(bytes,at);savedWork.push({participantId,order,frozenRecord:bytes.slice(start,at.value)});}const timerRegistrations:RelocationEnvelopeV1["timerRegistrations"][number][]=[],registeredTimerNames=new Map<bigint,Set<string>>();for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("timer participant");const name=readText8(bytes,at),handlerType=readText8(bytes,at),periodMilliseconds=nonzero64(bytes,at,"period"),overrunPolicy=read8(bytes,at);if(overrunPolicy<1||overrunPolicy>3)throw new RangeError("overrun policy");const maxCatchUpTicks=nonzero64(bytes,at,"catch-up"),stop=read8(bytes,at);if(stop>1)throw new RangeError("stop flag");let names=registeredTimerNames.get(participantId);if(names===undefined){names=new Set<string>();registeredTimerNames.set(participantId,names);}names.add(name);timerRegistrations.push({participantId,name,handlerType,periodMilliseconds,overrunPolicy:overrunPolicy as 1|2|3,maxCatchUpTicks,stopOnUnhandledException:stop===1,lastCompletedDeliveryIndex:read64(bytes,at),lastCompletedScheduledIndex:read64(bytes,at),nextScheduledAtUnixMilliseconds:read64(bytes,at)});}const pendingTimerTicks:RelocationEnvelopeV1["pendingTimerTicks"][number][]=[];for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("pending timer participant");const order=nonzero64(bytes,at,"order"),timerName=readText8(bytes,at);if(registeredTimerNames.get(participantId)?.has(timerName)!==true)throw new RangeError("pending timer registration");pendingTimerTicks.push({participantId,order,timerName,deliveryIndex:nonzero64(bytes,at,"delivery"),scheduledIndex:nonzero64(bytes,at,"scheduled"),scheduledAtUnixMilliseconds:read64(bytes,at),skippedTicks:read64(bytes,at)});}exactEnd(at,bytes.length,"relocation envelope");sorted(applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");sorted(savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");sorted(timerRegistrations,(a,b)=>a.participantId===b.participantId?compareUtf8(a.name,b.name):a.participantId<b.participantId?-1:1,"timerRegistrations");sorted(pendingTimerTicks,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"pendingTimerTicks");return{relocationHigh,relocationLow,object,applicationVersion,applicationStates,savedWork,timerRegistrations,pendingTimerTicks};}
55
56
  function crc32c(bytes:Uint8Array):number{let crc=0xffffffff;for(const octet of bytes){crc^=octet;for(let bit=0;bit<8;bit++)crc=(crc>>>1)^((crc&1)!==0?0x82f63b78:0);}return(~crc)>>>0;}
56
- function encodeDurable(magic:readonly number[],body:readonly number[],maximum:number):Uint8Array{const out=[...magic,1,0,0];u32(out,body.length);out.push(...body);if(out.length+4>maximum)throw new RangeError("durable format bytes");u32(out,crc32c(Uint8Array.from(out)));return Uint8Array.from(out);}
57
+ function encodeDurable(magic:readonly number[],body:readonly number[],maximum:number):Uint8Array{const out=[...magic,1,0,0];u32(out,body.length);appendBytes(out,body);if(out.length+4>maximum)throw new RangeError("durable format bytes");u32(out,crc32c(Uint8Array.from(out)));return Uint8Array.from(out);}
57
58
  function decodeDurable(bytes:Uint8Array,magic:readonly number[],maximum:number):Uint8Array{if(bytes.length>maximum||bytes.length<15||magic.some((v,i)=>bytes[i]!==v)||bytes[4]!==1||bytes[5]!==0||bytes[6]!==0)throw new RangeError("durable header");const at={value:7},length=read32(bytes,at),end=boundedEnd(bytes,at,length,"durable body");if(end+4!==bytes.length)throw new RangeError("durable length");const body=bytes.slice(at.value,end);at.value=end;const expected=read32(bytes,at);if(crc32c(bytes.slice(0,end))!==expected)throw new RangeError("durable checksum");return body;}
58
- export function encodeRelocationDataChunkV1(value:RelocationDataChunkV1):Uint8Array{const body:number[]=[];u32(body,value.order);if(value.data.length>67108864)throw new RangeError("chunk data");u32(body,value.data.length);body.push(...value.data);return encodeDurable([90,76,84,67],body,67108886);}
59
+ export function encodeRelocationDataChunkV1(value:RelocationDataChunkV1):Uint8Array{const body:number[]=[];u32(body,value.order);if(value.data.length>67108864)throw new RangeError("chunk data");u32(body,value.data.length);appendBytes(body,value.data);return encodeDurable([90,76,84,67],body,67108886);}
59
60
  export function decodeRelocationDataChunkV1(bytes:Uint8Array):RelocationDataChunkV1{const body=decodeDurable(bytes,[90,76,84,67],67108886),at={value:0},order=read32(body,at),data=take(body,at,read32(body,at),"chunk data");exactEnd(at,body.length,"chunk");return{order,data};}
60
- export function encodeRelocationManifestV1(value:RelocationManifestV1):Uint8Array{if(Number(value.logicalFormatVersion)!==1||value.totalLength>274877906944n||value.inventoryDigestSha256.length!==32||value.chunks.length>4096)throw new RangeError("manifest");sorted(value.chunks,(a,b)=>a.order-b.order,"manifest chunks");const body:number[]=[1];u64(body,value.totalLength);u32(body,value.totalChecksumCrc32c);body.push(32,...value.inventoryDigestSha256);u32(body,value.chunks.length);for(const chunk of value.chunks){if(chunk.length===0n)throw new RangeError("chunk length");u32(body,chunk.order);writeText16(body,chunk.reference,1,4096,"chunk reference");u64(body,chunk.length);u32(body,chunk.checksumCrc32c);}return encodeDurable([90,76,84,77],body,33554432);}
61
+ export function encodeRelocationManifestV1(value:RelocationManifestV1):Uint8Array{if(Number(value.logicalFormatVersion)!==1||value.totalLength>274877906944n||value.inventoryDigestSha256.length!==32||value.chunks.length>4096)throw new RangeError("manifest");sorted(value.chunks,(a,b)=>a.order-b.order,"manifest chunks");const body:number[]=[1];u64(body,value.totalLength);u32(body,value.totalChecksumCrc32c);body.push(32);appendBytes(body,value.inventoryDigestSha256);u32(body,value.chunks.length);for(const chunk of value.chunks){if(chunk.length===0n)throw new RangeError("chunk length");u32(body,chunk.order);writeText16(body,chunk.reference,1,4096,"chunk reference");u64(body,chunk.length);u32(body,chunk.checksumCrc32c);}return encodeDurable([90,76,84,77],body,33554432);}
61
62
  export function decodeRelocationManifestV1(bytes:Uint8Array):RelocationManifestV1{const body=decodeDurable(bytes,[90,76,84,77],33554432),at={value:0};if(read8(body,at)!==1)throw new RangeError("logical format version");const totalLength=read64(body,at);if(totalLength>274877906944n)throw new RangeError("logical length");const totalChecksumCrc32c=read32(body,at),digestLength=read8(body,at);if(digestLength!==32)throw new RangeError("inventory digest minimumBytes");const inventoryDigestSha256=take(body,at,digestLength,"inventory digest"),count=read32(body,at);if(count>4096)throw new RangeError("chunk count");const chunks:RelocationChunkEntryV1[]=[];for(let i=0;i<count;i++)chunks.push({order:read32(body,at),reference:readText16(body,at,1,4096,"chunk reference"),length:nonzero64(body,at,"chunk length"),checksumCrc32c:read32(body,at)});exactEnd(at,body.length,"manifest");sorted(chunks,(a,b)=>a.order-b.order,"manifest chunks");return{logicalFormatVersion:1,totalLength,totalChecksumCrc32c,inventoryDigestSha256,chunks};}
62
63
  export interface LivenessProbe5 { readonly probeId: bigint; }
63
64
  export function encodeLivenessProbe5(value: LivenessProbe5): Uint8Array { const out=[90,77,1,5,0]; if(value.probeId===0n) throw new RangeError("probeId"); u64(out,value.probeId); return Uint8Array.from(out); }
@@ -144,11 +145,11 @@ export function encodeRelocationFailed53(v:RelocationFailed53):Uint8Array{const
144
145
  export function decodeRelocationFailed53(bytes:Uint8Array):RelocationFailed53{const at=b3ReadHeader(bytes,53,"relocationFailed"),v=b3ReadReadyBase(bytes,at),failureCode=read32(bytes,at);if(!b3FailureResults.has(failureCode))throw new RangeError("failure code");b3Finish(at,bytes,"relocationFailed");return{...v,failureCode};}
145
146
  function b3WriteSourceBase(o:number[],v:RelocationData31|RelocationCutover34|RelocationState52):void{b3WriteId(o,v.relocation,"relocation");if(v.targetAttemptGeneration===0n)throw new RangeError("target attempt");u64(o,v.targetAttemptGeneration);b3WriteCoordinator(o,v.coordinator);b3WriteRole(o,v.senderRole);if(v.senderRole!=="source")throw new RangeError("sender role");writeRelocationObject(o,v.object);}
146
147
  function b3ReadSourceBase(bytes:Uint8Array,at:{value:number}){const relocation=b3ReadId(bytes,at,"relocation"),targetAttemptGeneration=nonzero64(bytes,at,"target attempt"),coordinator=b3ReadCoordinator(bytes,at),senderRole=b3ReadRole(bytes,at);if(senderRole!=="source")throw new RangeError("sender role");return{relocation,targetAttemptGeneration,coordinator,senderRole,object:readRelocationObject(bytes,at)};}
147
- export function encodeRelocationData31(v:RelocationData31):Uint8Array{const o=b3Header(31);b3WriteSourceBase(o,v);const at={value:0};skipFrozenRecord(v.record,at);exactEnd(at,v.record.length,"frozen record");o.push(...v.record);return Uint8Array.from(o);}
148
+ export function encodeRelocationData31(v:RelocationData31):Uint8Array{const o=b3Header(31);b3WriteSourceBase(o,v);const at={value:0};skipFrozenRecord(v.record,at);exactEnd(at,v.record.length,"frozen record");appendBytes(o,v.record);return Uint8Array.from(o);}
148
149
  export function decodeRelocationData31(bytes:Uint8Array):RelocationData31{const at=b3ReadHeader(bytes,31,"relocationData"),v=b3ReadSourceBase(bytes,at),start=at.value;skipFrozenRecord(bytes,at);const record=bytes.slice(start,at.value);b3Finish(at,bytes,"relocationData");return{...v,record};}
149
150
  export function encodeRelocationCutover34(v:RelocationCutover34):Uint8Array{const o=b3Header(34);b3WriteSourceBase(o,v);if(v.boundaryRecordCount<0n||v.boundaryRecordCount>9223372036854775807n)throw new RangeError("boundary count");u64(o,v.boundaryRecordCount);u32(o,v.boundaryChecksumCrc32c);return Uint8Array.from(o);}
150
151
  export function decodeRelocationCutover34(bytes:Uint8Array):RelocationCutover34{const at=b3ReadHeader(bytes,34,"relocationCutover"),v=b3ReadSourceBase(bytes,at),boundaryRecordCount=read64(bytes,at);if(boundaryRecordCount>9223372036854775807n)throw new RangeError("boundary count");const boundaryChecksumCrc32c=read32(bytes,at);b3Finish(at,bytes,"relocationCutover");return{...v,boundaryRecordCount,boundaryChecksumCrc32c};}
151
- export function encodeRelocationState52(v:RelocationState52):Uint8Array{const o=b3Header(52);b3WriteSourceBase(o,v);if(v.chunkData.length>67108864)throw new RangeError("chunk data");u32(o,v.chunkOrdinal);u32(o,v.chunkData.length);o.push(...v.chunkData);return Uint8Array.from(o);}
152
+ export function encodeRelocationState52(v:RelocationState52):Uint8Array{const o=b3Header(52);b3WriteSourceBase(o,v);if(v.chunkData.length>67108864)throw new RangeError("chunk data");u32(o,v.chunkOrdinal);u32(o,v.chunkData.length);appendBytes(o,v.chunkData);return Uint8Array.from(o);}
152
153
  export function decodeRelocationState52(bytes:Uint8Array):RelocationState52{const at=b3ReadHeader(bytes,52,"relocationState"),v=b3ReadSourceBase(bytes,at),chunkOrdinal=read32(bytes,at),length=read32(bytes,at);if(length>67108864)throw new RangeError("chunk data");const chunkData=take(bytes,at,length,"chunk data");b3Finish(at,bytes,"relocationState");return{...v,chunkOrdinal,chunkData};}
153
154
  export function encodeRelocationPrepare40(v:RelocationPrepare40):Uint8Array{const o=b3Header(40);b3WriteId(o,v.relocation,"relocation");if(v.targetAttemptGeneration===0n||v.sourceNodeGeneration===0n)throw new RangeError("prepare");u64(o,v.targetAttemptGeneration);b3WriteCoordinator(o,v.coordinator);b3WriteTarget(o,v.target);b3WriteRole(o,v.initiatorRole);if(v.initiatorRole!=="source")throw new RangeError("initiator role");writeRelocationObject(o,v.object);rid(o,v.sourceNodeRid);u64(o,v.sourceNodeGeneration);if(v.payloadTotalLength<0n||v.payloadTotalLength>274877906944n||v.payloadChunkCount>4096||v.applicationVersion<0n||v.applicationVersion>9223372036854775807n)throw new RangeError("prepare bounds");u64(o,v.payloadTotalLength);u32(o,v.payloadChunkCount);u32(o,v.payloadChecksumCrc32c);u64(o,v.applicationVersion);return Uint8Array.from(o);}
154
155
  export function decodeRelocationPrepare40(bytes:Uint8Array):RelocationPrepare40{const at=b3ReadHeader(bytes,40,"relocationPrepare"),relocation=b3ReadId(bytes,at,"relocation"),targetAttemptGeneration=nonzero64(bytes,at,"target attempt"),coordinator=b3ReadCoordinator(bytes,at),target=b3ReadTarget(bytes,at),initiatorRole=b3ReadRole(bytes,at);if(initiatorRole!=="source")throw new RangeError("initiator role");const object=readRelocationObject(bytes,at),sourceNodeRid=readRid(bytes,at),sourceNodeGeneration=nonzero64(bytes,at,"source node"),payloadTotalLength=read64(bytes,at),payloadChunkCount=read32(bytes,at),payloadChecksumCrc32c=read32(bytes,at),applicationVersion=read64(bytes,at);if(payloadTotalLength>274877906944n||payloadChunkCount>4096||applicationVersion>9223372036854775807n)throw new RangeError("prepare bounds");b3Finish(at,bytes,"relocationPrepare");return{relocation,targetAttemptGeneration,coordinator,target,initiatorRole,object,sourceNodeRid,sourceNodeGeneration,payloadTotalLength,payloadChunkCount,payloadChecksumCrc32c,applicationVersion};}
@@ -173,7 +174,7 @@ function b4ReadHeader(bytes:Uint8Array,id:number,name:string):{value:number}{con
173
174
  function b4Finish(at:{value:number},bytes:Uint8Array,name:string):void{if(at.value!==bytes.length)throw new RangeError(name+" trailing");}
174
175
  function b4Reservation(out:number[],v:ServiceWireObjectReservationFence):void{text8(out,v.reservationId);writeText16(out,v.expectedStoreVersion,1,4096,"store version");if(v.objectGeneration===0n||v.authorityOwnerGeneration===0n||v.targetNodeGeneration===0n||v.targetOwnerLeaseGeneration===0n||!Number.isInteger(v.pendingCapacityDelta)||v.pendingCapacityDelta<1||v.pendingCapacityDelta>0xffffffff)throw new RangeError("reservation");u64(out,v.objectGeneration);u64(out,v.authorityOwnerGeneration);rid(out,v.targetNodeRid);u64(out,v.targetNodeGeneration);text8(out,v.targetOwnerId);u64(out,v.targetOwnerLeaseGeneration);u32(out,v.pendingCapacityDelta);}
175
176
  function b4ReadReservation(bytes:Uint8Array,at:{value:number}):ServiceWireObjectReservationFence{return{reservationId:readText8(bytes,at),expectedStoreVersion:readText16(bytes,at,1,4096,"store version"),objectGeneration:nonzero64(bytes,at,"object generation"),authorityOwnerGeneration:nonzero64(bytes,at,"authority generation"),targetNodeRid:readRid(bytes,at),targetNodeGeneration:nonzero64(bytes,at,"target node generation"),targetOwnerId:readText8(bytes,at),targetOwnerLeaseGeneration:nonzero64(bytes,at,"target lease generation"),pendingCapacityDelta:(()=>{const v=read32(bytes,at);if(v===0)throw new RangeError("pending capacity");return v;})()};}
176
- function b4Close(out:number[],v:ServiceWireUserSpotCloseFenceV1):void{const body:number[]=[];text8(body,v.spotId);if(v.objectGeneration===0n||v.targetNodeGeneration===0n||v.expectedAuthorityOwnerGeneration===0n)throw new RangeError("close fence");u64(body,v.objectGeneration);rid(body,v.targetNodeRid);u64(body,v.targetNodeGeneration);u64(body,v.expectedAuthorityOwnerGeneration);writeText16(body,v.expectedStoreVersion,1,4096,"store version");out.push(1);if(body.length>65535)throw new RangeError("close fence");out.push(body.length>>>8,body.length&255,...body);}
177
+ function b4Close(out:number[],v:ServiceWireUserSpotCloseFenceV1):void{const body:number[]=[];text8(body,v.spotId);if(v.objectGeneration===0n||v.targetNodeGeneration===0n||v.expectedAuthorityOwnerGeneration===0n)throw new RangeError("close fence");u64(body,v.objectGeneration);rid(body,v.targetNodeRid);u64(body,v.targetNodeGeneration);u64(body,v.expectedAuthorityOwnerGeneration);writeText16(body,v.expectedStoreVersion,1,4096,"store version");out.push(1);if(body.length>65535)throw new RangeError("close fence");out.push(body.length>>>8,body.length&255);appendBytes(out,body);}
177
178
  function b4ReadClose(bytes:Uint8Array,at:{value:number}):ServiceWireUserSpotCloseFenceV1{if(read8(bytes,at)!==1)throw new RangeError("close fence version");const end=boundedEnd(bytes,at,read16(bytes,at),"close fence");const value={spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),targetNodeRid:readRid(bytes,at),targetNodeGeneration:nonzero64(bytes,at,"target node generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation"),expectedStoreVersion:readText16(bytes,at,1,4096,"store version")};exactEnd(at,end,"close fence");return value;}
178
179
  function b4CommandBase(out:number[],v:{correlation:bigint;operation:ServiceWireOperationId;sourceNodeRid:Uint8Array;sourceNodeGeneration:bigint}):void{if(v.correlation===0n||v.sourceNodeGeneration===0n)throw new RangeError("command base");u64(out,v.correlation);b3WriteId(out,v.operation,"operation");rid(out,v.sourceNodeRid);u64(out,v.sourceNodeGeneration);}
179
180
  function b4ReadCommandBase(bytes:Uint8Array,at:{value:number}):{correlation:bigint;operation:ServiceWireOperationId;sourceNodeRid:Uint8Array;sourceNodeGeneration:bigint}{return{correlation:nonzero64(bytes,at,"correlation"),operation:b3ReadId(bytes,at,"operation"),sourceNodeRid:readRid(bytes,at),sourceNodeGeneration:nonzero64(bytes,at,"source node generation")};}
@@ -183,7 +184,7 @@ export function encodeUserSpotClose48(v:UserSpotClose48):Uint8Array{const out=b4
183
184
  export function decodeUserSpotClose48(bytes:Uint8Array):UserSpotClose48{const at=b4ReadHeader(bytes,48,"userSpotClose"),base=b4ReadCommandBase(bytes,at),target=b4ReadClose(bytes,at),deadlineUnixMs=nonzero64(bytes,at,"deadline");b4Finish(at,bytes,"userSpotClose");return{...base,target,deadlineUnixMs};}
184
185
  export function encodeActorCreate49(v:ActorCreate49):Uint8Array{const out=b4Header(49);b4CommandBase(out,v);text8(out,v.actorId);text8(out,v.stableType);b4Reservation(out,v.reservation);if(v.deadlineUnixMs===0n)throw new RangeError("deadline");u64(out,v.deadlineUnixMs);return Uint8Array.from(out);}
185
186
  export function decodeActorCreate49(bytes:Uint8Array):ActorCreate49{const at=b4ReadHeader(bytes,49,"actorCreate"),base=b4ReadCommandBase(bytes,at),actorId=readText8(bytes,at),stableType=readText8(bytes,at),reservation=b4ReadReservation(bytes,at),deadlineUnixMs=nonzero64(bytes,at,"deadline");b4Finish(at,bytes,"actorCreate");return{...base,actorId,stableType,reservation,deadlineUnixMs};}
186
- function b4ZljrPayload(v:ZljrRecordV1):Uint8Array{if(v.metadata.length>262144||v.request.length>1048576||v.reply.length>1048576)throw new RangeError("ZLJR length");const out=[90,76,74,82,1];u32(out,v.metadata.length);u32(out,v.request.length);u32(out,v.reply.length);out.push(...v.metadata,...v.request,...v.reply);return Uint8Array.from(out);}
187
- function b4ReadZljrPayload(bytes:Uint8Array):{metadata:Uint8Array;request:Uint8Array;reply:Uint8Array}{const at={value:0};if(read32(bytes,at)!==0x5a4c4a52||read8(bytes,at)!==1)throw new RangeError("ZLJR header");const metadataLength=read32(bytes,at),requestLength=read32(bytes,at),replyLength=read32(bytes,at);if(metadataLength>262144||requestLength>1048576||replyLength>1048576)throw new RangeError("ZLJR length");const metadata=take(bytes,at,metadataLength,"ZLJR metadata"),request=take(bytes,at,requestLength,"ZLJR request"),reply=take(bytes,at,replyLength,"ZLJR reply");b4Finish(at,bytes,"ZLJR");return{metadata,request,reply};}
188
- export function encodeZljrRecordV1(v:ZljrRecordV1):Uint8Array{if(v.source.nodeGeneration===0n||v.source.ownerLeaseGeneration===0n)throw new RangeError("ZLJR record");const source:number[]=[];rid(source,v.source.nodeRid);u64(source,v.source.nodeGeneration);text8(source,v.source.ownerId);u64(source,v.source.ownerLeaseGeneration);if(source.length>65535)throw new RangeError("ZLJR source");const payload=b4ZljrPayload(v),body:number[]=[];text8(body,"__zlink.actor.routed_join.recovery");text8(body,"application/x-zlink-actor-routed-join-recovery-v1");u32(body,payload.length);body.push(...payload);const out=[1,1,source.length>>>8,source.length&255,...source,0];u64(out,v.operation.high);u64(out,v.operation.low);u32(out,0);out.push(0,0,1);u32(out,body.length);out.push(...body);return Uint8Array.from(out);}
187
+ function b4ZljrPayload(v:ZljrRecordV1):Uint8Array{if(v.metadata.length>262144)throw new RangeError("ZLJR length");const out=[90,76,74,82,1];u32(out,v.metadata.length);u32(out,v.request.length);u32(out,v.reply.length);appendBytes(out,v.metadata);appendBytes(out,v.request);appendBytes(out,v.reply);return Uint8Array.from(out);}
188
+ function b4ReadZljrPayload(bytes:Uint8Array):{metadata:Uint8Array;request:Uint8Array;reply:Uint8Array}{const at={value:0};if(read32(bytes,at)!==0x5a4c4a52||read8(bytes,at)!==1)throw new RangeError("ZLJR header");const metadataLength=read32(bytes,at),requestLength=read32(bytes,at),replyLength=read32(bytes,at);if(metadataLength>262144)throw new RangeError("ZLJR length");const metadata=take(bytes,at,metadataLength,"ZLJR metadata"),request=take(bytes,at,requestLength,"ZLJR request"),reply=take(bytes,at,replyLength,"ZLJR reply");b4Finish(at,bytes,"ZLJR");return{metadata,request,reply};}
189
+ export function encodeZljrRecordV1(v:ZljrRecordV1):Uint8Array{if(v.source.nodeGeneration===0n||v.source.ownerLeaseGeneration===0n)throw new RangeError("ZLJR record");const source:number[]=[];rid(source,v.source.nodeRid);u64(source,v.source.nodeGeneration);text8(source,v.source.ownerId);u64(source,v.source.ownerLeaseGeneration);if(source.length>65535)throw new RangeError("ZLJR source");const payload=b4ZljrPayload(v),body:number[]=[];text8(body,"__zlink.actor.routed_join.recovery");text8(body,"application/x-zlink-actor-routed-join-recovery-v1");u32(body,payload.length);appendBytes(body,payload);const out=[1,1,source.length>>>8,source.length&255,...source,0];u64(out,v.operation.high);u64(out,v.operation.low);u32(out,0);out.push(0,0,1);u32(out,body.length);appendBytes(out,body);return Uint8Array.from(out);}
189
190
  export function decodeZljrRecordV1(bytes:Uint8Array):ZljrRecordV1{const at={value:0};if(read8(bytes,at)!==1||read8(bytes,at)!==1)throw new RangeError("ZLJR frozen record");const sourceEnd=boundedEnd(bytes,at,read16(bytes,at),"ZLJR source"),nodeRid=readRid(bytes,at),nodeGeneration=nonzero64(bytes,at,"source node generation"),ownerId=readText8(bytes,at),ownerLeaseGeneration=nonzero64(bytes,at,"source owner lease");exactEnd(at,sourceEnd,"ZLJR source");if(read8(bytes,at)!==0)throw new RangeError("ZLJR metadata flag");const operation={high:read64(bytes,at),low:read64(bytes,at)};if(read32(bytes,at)!==0||read16(bytes,at)!==0||read8(bytes,at)!==1)throw new RangeError("ZLJR frozen record");const payloadEnd=boundedEnd(bytes,at,read32(bytes,at),"ZLJR payload"),packetName=readText8(bytes,at),contentType=readText8(bytes,at),payloadLength=read32(bytes,at);if(packetName!=="__zlink.actor.routed_join.recovery"||contentType!=="application/x-zlink-actor-routed-join-recovery-v1"||payloadLength!==payloadEnd-at.value)throw new RangeError("ZLJR payload");const payload=take(bytes,at,payloadLength,"ZLJR payload");exactEnd(at,payloadEnd,"ZLJR payload");b4Finish(at,bytes,"ZLJR record");return{source:{nodeRid,nodeGeneration,ownerId,ownerLeaseGeneration},operation,...b4ReadZljrPayload(payload)};}
@@ -1600,7 +1600,8 @@ export class DefaultZLinkSpotManager {
1600
1600
  const inboundFlow = createInboundFlow(
1601
1601
  envelope.header.flowId,
1602
1602
  envelope.header.flowOrigin,
1603
- this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true
1603
+ this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
1604
+ undefined
1604
1605
  );
1605
1606
  await runWithFlow(inboundFlow, async () => {
1606
1607
  const request = record.operationKind === OperationKind.InstanceSpotRequest;
@@ -3,7 +3,13 @@ import {
3
3
  createInternalFrameworkException,
4
4
  internalFrameworkErrorKind
5
5
  } from '../framework-errors-internal';
6
- import type { ActorRef, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
6
+ import type {
7
+ ActorRef,
8
+ RoutingId,
9
+ ZLinkActor,
10
+ ZLinkMessageSerializer,
11
+ ZLinkSpot
12
+ } from '../../contracts';
7
13
  import type { Message } from '../../contracts/Common/Message';
8
14
  import { ZLinkFrameworkException, zlinkMessageMetadata } from '../../contracts';
9
15
  import {
@@ -14,7 +20,8 @@ import {
14
20
  ZLinkDispatchMessageKind
15
21
  } from '../../contracts/Dispatch/ZLinkDispatchOptions';
16
22
  import { flowIfEnabled } from '../diagnostics';
17
- import { createInboundFlow, runWithFlow } from '../diagnostics/flow-context';
23
+ import { createInboundFlow, currentFlowContext, runWithFlow } from '../diagnostics/flow-context';
24
+ import { streamSessionIdFromRoutingId } from '../streams/managed-stream';
18
25
  import type { ZLinkRemoteBoundSessionTarget } from '../actors';
19
26
  import {
20
27
  actorMessageFollowContext,
@@ -58,6 +65,7 @@ export interface ZLinkActorPacketDelivery {
58
65
  readonly fallbackActorRef?: ActorRef;
59
66
  readonly requestTerminal?: ZLinkActorRequestTerminal;
60
67
  readonly messageFollowOrigin?: ZLinkMessageFollowOrigin;
68
+ readonly sourceSessionRid?: RoutingId;
61
69
  }
62
70
 
63
71
  /**
@@ -176,7 +184,16 @@ export class ZLinkSpotActorPacketDispatch {
176
184
  throw error;
177
185
  }
178
186
  return runWithFlow(
179
- createInboundFlow(header.flowId, header.flowOrigin, flowEnabled),
187
+ createInboundFlow(
188
+ header.flowId,
189
+ header.flowOrigin,
190
+ flowEnabled,
191
+ !flowEnabled
192
+ ? undefined
193
+ : delivery.sourceSessionRid === undefined
194
+ ? currentFlowContext()?.streamSessionId
195
+ : streamSessionIdFromRoutingId(delivery.sourceSessionRid)
196
+ ),
180
197
  async () => {
181
198
  const messageKind =
182
199
  header.kind === ZLinkStreamMessageKind.Request