@zlink-systems/framework 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
  3. package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
  4. package/dist/runtime/actors/actor-context.js +4 -4
  5. package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
  6. package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
  7. package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
  8. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  9. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +4 -0
  10. package/dist/runtime/backend/node/node-raw-mesh-backend.js +24 -8
  11. package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
  12. package/dist/runtime/channels/channel-transports.js +3 -9
  13. package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
  14. package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
  15. package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
  16. package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
  17. package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
  18. package/dist/runtime/diagnostics/flow-context.js +2 -2
  19. package/dist/runtime/diagnostics/message-flow.js +11 -3
  20. package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
  21. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
  22. package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
  23. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
  24. package/dist/runtime/foundation/service-stateful-runtime.d.ts +5 -0
  25. package/dist/runtime/foundation/service-stateful-runtime.js +52 -2
  26. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
  27. package/dist/runtime/foundation/service-topology-registry.js +12 -4
  28. package/dist/runtime/host/index.js +7 -3
  29. package/dist/runtime/locations/in-memory-location-store.js +5 -0
  30. package/dist/runtime/locations/location-store-repository.js +3 -1
  31. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
  32. package/dist/runtime/spots/index.js +9 -3
  33. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
  34. package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
  35. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  36. package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
  37. package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
  38. package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
  39. package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
  40. package/dist/runtime/spots/spot-timer.js +1 -1
  41. package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
  42. package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
  43. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
  44. package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
  45. package/dist/runtime/streams/bound-session-response-target.js +8 -1
  46. package/dist/runtime/streams/bound-session-service.d.ts +1 -0
  47. package/dist/runtime/streams/bound-session-service.js +6 -3
  48. package/dist/runtime/streams/index.js +8 -1
  49. package/dist/runtime/streams/managed-stream.d.ts +3 -0
  50. package/dist/runtime/streams/managed-stream.js +20 -1
  51. package/dist/runtime/streams/protocol.d.ts +5 -1
  52. package/dist/runtime/streams/protocol.js +41 -4
  53. package/dist/runtime/streams/session-actor-coordinator.js +3 -4
  54. package/dist/runtime/streams/session-context.d.ts +9 -2
  55. package/dist/runtime/streams/session-context.js +20 -4
  56. package/dist/runtime/streams/session-local-actors.d.ts +3 -1
  57. package/dist/runtime/streams/session-local-actors.js +17 -2
  58. package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
  59. package/dist/runtime/streams/stream-frame-factory.js +3 -2
  60. package/dist/runtime/streams/stream-session-runtime.js +42 -12
  61. package/package.json +3 -3
  62. package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
  63. package/src/contracts/Streams/IZLinkSession.ts +2 -1
  64. package/src/runtime/actors/actor-context.ts +4 -4
  65. package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
  66. package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
  67. package/src/runtime/backend/contracts/index.ts +5 -0
  68. package/src/runtime/backend/node/node-raw-mesh-backend.ts +43 -11
  69. package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
  70. package/src/runtime/channels/channel-transports.ts +3 -9
  71. package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
  72. package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
  73. package/src/runtime/diagnostics/flow-context.ts +6 -4
  74. package/src/runtime/diagnostics/message-flow.ts +12 -3
  75. package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
  76. package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
  77. package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
  78. package/src/runtime/foundation/service-stateful-runtime.ts +71 -5
  79. package/src/runtime/foundation/service-topology-registry.ts +18 -5
  80. package/src/runtime/host/index.ts +11 -4
  81. package/src/runtime/locations/in-memory-location-store.ts +7 -0
  82. package/src/runtime/locations/location-store-repository.ts +4 -1
  83. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
  84. package/src/runtime/spots/index.ts +22 -2
  85. package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
  86. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  87. package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
  88. package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
  89. package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
  90. package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
  91. package/src/runtime/spots/spot-timer.ts +6 -1
  92. package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
  93. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
  94. package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
  95. package/src/runtime/streams/bound-session-response-target.ts +10 -1
  96. package/src/runtime/streams/bound-session-service.ts +13 -3
  97. package/src/runtime/streams/index.ts +8 -1
  98. package/src/runtime/streams/managed-stream.ts +34 -12
  99. package/src/runtime/streams/protocol.ts +44 -5
  100. package/src/runtime/streams/session-actor-coordinator.ts +12 -4
  101. package/src/runtime/streams/session-context.ts +31 -4
  102. package/src/runtime/streams/session-local-actors.ts +19 -3
  103. package/src/runtime/streams/stream-frame-factory.ts +4 -2
  104. package/src/runtime/streams/stream-session-runtime.ts +58 -13
@@ -71,6 +71,7 @@ export interface ZLinkRuntimeMessageFlowEvent {
71
71
  readonly instanceSpotType?: string;
72
72
  readonly activationState?: 'activating' | 'ready' | 'closing';
73
73
  readonly actorId?: string;
74
+ readonly streamSessionId?: string;
74
75
  /** Internal service-wire command carried by a RouteMesh control record. */
75
76
  readonly commandId?: number;
76
77
  readonly messageSize?: number;
@@ -1,5 +1,5 @@
1
1
  import type { RoutingId, ZLinkMessage } from '../Common';
2
- import type { ZLinkSessionActors } from './IZLinkSessionActor';
2
+ import type { ZLinkSessionActor, ZLinkSessionActors } from './IZLinkSessionActor';
3
3
  import type { ZLinkStreamError } from './ZLinkStreamError';
4
4
  import type { ZLinkSessionHandlerRegistry } from './IZLinkSessionPacketHandler';
5
5
  export interface ZLinkSession {
@@ -14,6 +14,7 @@ export interface ZLinkSessionDispatchContext {
14
14
  readonly packetName: string;
15
15
  readonly metadata: ReadonlyMap<string, string>;
16
16
  readonly canReply: boolean;
17
+ readonly actor?: ZLinkSessionActor;
17
18
  }
18
19
  export interface ZLinkSessionFactory<TSession extends ZLinkSession = ZLinkSession> {
19
20
  create(context: ZLinkSessionContext): Promise<TSession>;
@@ -12,6 +12,7 @@ const actor_lifecycle_snapshot_1 = require("./actor-lifecycle-snapshot");
12
12
  const operation_identity_1 = require("../foundation/operation-identity");
13
13
  const actor_join_deferred_scope_1 = require("./actor-join-deferred-scope");
14
14
  const execution_1 = require("../execution");
15
+ const dispatch_error_details_1 = require("../diagnostics/dispatch-error-details");
15
16
  exports.ZLINK_ACTOR_JOIN_ENTRY_SPOT_RUNTIME = Symbol('zlink.actor.join-entry-spot-runtime');
16
17
  class DefaultZLinkActorContext {
17
18
  state;
@@ -154,7 +155,6 @@ class DefaultZLinkActorJoinSpotCall {
154
155
  };
155
156
  try {
156
157
  (0, actor_join_deferred_scope_1.deferActorJoin)({
157
- requestBytes: requestMessage.data().byteLength,
158
158
  prepare: async () => {
159
159
  if (prepared)
160
160
  return;
@@ -280,7 +280,6 @@ class DefaultZLinkActorJoinEntrySpotCall {
280
280
  };
281
281
  try {
282
282
  (0, actor_join_deferred_scope_1.deferActorJoin)({
283
- requestBytes: requestMessage.data().byteLength,
284
283
  prepare: async () => {
285
284
  if (prepared)
286
285
  return;
@@ -436,6 +435,7 @@ async function notifyJoinFailure(actor, operationId, error, coordinator) {
436
435
  // The completion carries only an error kind, so trace the cause on the
437
436
  // message flow under the same flow id as the Join that produced it.
438
437
  const tracePoint = coordinator?.messageFlow?.()?.begin(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error);
438
+ const errorDetails = tracePoint === undefined ? undefined : (0, dispatch_error_details_1.dispatchErrorDetails)(error);
439
439
  tracePoint?.trace({
440
440
  outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error,
441
441
  surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.SpotActor,
@@ -444,8 +444,8 @@ async function notifyJoinFailure(actor, operationId, error, coordinator) {
444
444
  actorId: actor.context.actorId,
445
445
  errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.HandlerException,
446
446
  errorAction: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.ReplyError,
447
- errorType: String(frameworkError.kind),
448
- errorMessage: String(error)
447
+ errorType: errorDetails?.errorType,
448
+ errorMessage: errorDetails?.errorMessage
449
449
  });
450
450
  await actor.onJoinCompleted?.({
451
451
  status: 'failed',
@@ -1,5 +1,4 @@
1
1
  interface DeferredActorJoin {
2
- readonly requestBytes: number;
3
2
  /** Starts target admission after the handler terminal, before reply admission. */
4
3
  readonly prepare?: () => Promise<void> | void;
5
4
  readonly execute: () => Promise<void>;
@@ -2,15 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deferActorJoin = deferActorJoin;
4
4
  exports.runActorHandlerWithDeferredJoins = runActorHandlerWithDeferredJoins;
5
- const framework_errors_internal_1 = require("../framework-errors-internal");
6
5
  const node_async_hooks_1 = require("node:async_hooks");
7
6
  const configuration_1 = require("../configuration");
8
- const MAX_DEFERRED_JOINS = 64;
9
- const MAX_DEFERRED_JOIN_REQUEST_BYTES = 1024 * 1024;
10
- const MAX_DEFERRED_JOIN_BYTES = 8 * 1024 * 1024;
11
7
  class ActorJoinRegistrationScope {
12
8
  intents = [];
13
- totalBytes = 0;
14
9
  open = true;
15
10
  prepared = false;
16
11
  // Ledger 2.3: a deferred join runs in registration order right after the
@@ -21,20 +16,7 @@ class ActorJoinRegistrationScope {
21
16
  discardWithoutWaiting(intent);
22
17
  throw new configuration_1.ZLinkConfigurationException('Actor join defer requires an open Framework actor handler scope.');
23
18
  }
24
- if (this.intents.length >= MAX_DEFERRED_JOINS) {
25
- discardWithoutWaiting(intent);
26
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidConfiguration, `An actor handler may defer at most ${MAX_DEFERRED_JOINS} joins.`);
27
- }
28
- if (intent.requestBytes > MAX_DEFERRED_JOIN_REQUEST_BYTES) {
29
- discardWithoutWaiting(intent);
30
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidConfiguration, `A deferred actor join request may use at most ${MAX_DEFERRED_JOIN_REQUEST_BYTES} bytes.`);
31
- }
32
- if (this.totalBytes + intent.requestBytes > MAX_DEFERRED_JOIN_BYTES) {
33
- discardWithoutWaiting(intent);
34
- throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidConfiguration, `Deferred actor join requests may use at most ${MAX_DEFERRED_JOIN_BYTES} bytes per handler.`);
35
- }
36
19
  this.intents.push(intent);
37
- this.totalBytes += intent.requestBytes;
38
20
  }
39
21
  sealAfterHandlerTerminal() {
40
22
  this.open = false;
@@ -16,7 +16,6 @@ const service_wire_pilot_codec_generated_1 = require("../protocol/service_wire_p
16
16
  const RETENTION_MS = 24 * 60 * 60 * 1_000;
17
17
  const ROOT_VERSION = 2;
18
18
  const ROOT_MAGIC = Buffer.from('ZLJR');
19
- const MAX_ROOT_BYTES = 1024 * 1024;
20
19
  const JOURNAL_INVENTORY_DOMAIN = Buffer.from('zlink-node-deferred-join-authority-v1\0', 'utf8');
21
20
  /** Identifies whether an authority slot points at a deferred Join journal root. */
22
21
  async function isDeferredJoinAcceptedRootPublication(relocation, reference, checksumCrc32c, expected, signal) {
@@ -487,14 +486,11 @@ function encodeRoot(value) {
487
486
  cursor: value.cursor
488
487
  }), 'utf8')
489
488
  ]);
490
- if (encoded.byteLength > MAX_ROOT_BYTES) {
491
- throw new Error('Deferred Join completion root exceeds 1 MiB.');
492
- }
493
489
  return encoded;
494
490
  }
495
491
  function decodeRoot(payload) {
496
- if (payload.byteLength === 0 || payload.byteLength > MAX_ROOT_BYTES) {
497
- throw new Error('Deferred Join completion root size is invalid.');
492
+ if (payload.byteLength === 0) {
493
+ throw new Error('Deferred Join completion root is empty.');
498
494
  }
499
495
  const bytes = Buffer.from(payload);
500
496
  const encoded = hasJournalRootMagic(bytes) ? bytes.subarray(ROOT_MAGIC.byteLength) : bytes;
@@ -32,6 +32,8 @@ export type ZLinkBackendObjectPlacement = {
32
32
  readonly kind: 'unavailable';
33
33
  };
34
34
  export interface ZLinkBackendMeshNode {
35
+ completeClosedInstance?(spotId: string, objectGeneration: bigint): void;
36
+ setDispatchErrorReporter?(reporter: import('../../channels/dispatch-error-reporter').ZLinkDispatchErrorReporter, meshName: string): void;
35
37
  setRoutingId(routingId: unknown): void;
36
38
  setBind(endpoint: string): void;
37
39
  setAdvertiseHost(host: string): void;
@@ -1,5 +1,6 @@
1
1
  import { SubmitResult, type RequestResult as RequestResultValue, type SubmitResult as SubmitResultValue } from '@zlink-systems/zlink';
2
2
  import { type ZLinkBackendMessageLike as MessageLike } from '../runtime-values';
3
+ import type { ZLinkDispatchErrorReporter } from '../../channels/dispatch-error-reporter';
3
4
  import type { MeshOperationId, MeshPeerEntry, MeshPublisher, ReadyBatch, ReadyRecord, ReceiveBatch, ServiceSpot, StreamSessionService } from '../../foundation/service-runtime-contracts';
4
5
  import { type RawServiceRequestResult } from '../../foundation/raw-service-mesh-runtime';
5
6
  import { type ServiceApplicationPayload } from '../../foundation/service-wire-m6a-codec';
@@ -52,6 +53,7 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
52
53
  private mailboxRecordDropped?;
53
54
  private protocolError?;
54
55
  private messageFollowHandler?;
56
+ private dispatchErrors?;
55
57
  private readonly peerDisconnectedHandlers;
56
58
  constructor(meshName: string, routingId: string | undefined, bindingPort: ZLinkRawBindingPort, applicationJobQueue?: ApplicationJobQueuePort | undefined, applicationJobReceiveFlowFailureSink?: ((error: unknown) => void) | undefined, peerAdmissionSealed?: (() => boolean) | undefined);
57
59
  configureObjectPlacement(options: {
@@ -66,6 +68,7 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
66
68
  readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
67
69
  readonly owner: string;
68
70
  }) => void): void;
71
+ setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter): void;
69
72
  setProtocolErrorHandler(handler: (record: {
70
73
  readonly sourceNodeRid: string;
71
74
  readonly request: boolean;
@@ -183,6 +186,7 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
183
186
  recoverPendingInstanceActivation(envelope: ServiceInstanceActivationRecoveryEnvelope, pending: ServicePendingInstanceActivation, expectedCurrentRoute?: ServiceInstanceRouteFence | null): Promise<boolean>;
184
187
  completeRecoveredInstanceActivation(target: ServiceInstanceActivationTarget, route: ServiceInstanceRouteFence, expectedCurrentRoute?: ServiceInstanceRouteFence | null): Promise<ServiceInstanceRouteFence | undefined>;
185
188
  forgetInstanceIntent(spotId: string, objectGeneration: bigint, authorityOwnerGeneration: bigint, storeVersion?: string): void;
189
+ completeClosedInstance(spotId: string, objectGeneration: bigint): void;
186
190
  rememberSpotRoute(route: ServiceDirectSpotRouteFence, expectedCurrentRoute?: ServiceDirectSpotRouteFence | null): void;
187
191
  entrySpotRouteFence(targetNodeRid: string, targetSpotId: string): ServiceDirectSpotRouteFence;
188
192
  sealSpotMessageFollowIngress(source: ServiceDirectSpotRouteFence): ServiceSpotMessageFollowSeal | undefined;
@@ -4,6 +4,7 @@ exports.ZLinkNodeRawMeshBackend = void 0;
4
4
  exports.genericOperationFailure = genericOperationFailure;
5
5
  const node_crypto_1 = require("node:crypto");
6
6
  const promises_1 = require("node:timers/promises");
7
+ const stream_wire_1 = require("@zlink-systems/stream-wire");
7
8
  const node_socket_backend_adapter_1 = require("./node-socket-backend-adapter");
8
9
  const contracts_1 = require("../../../contracts");
9
10
  const framework_errors_internal_1 = require("../../framework-errors-internal");
@@ -80,6 +81,7 @@ class ZLinkNodeRawMeshBackend {
80
81
  mailboxRecordDropped;
81
82
  protocolError;
82
83
  messageFollowHandler;
84
+ dispatchErrors;
83
85
  peerDisconnectedHandlers = new Set();
84
86
  constructor(meshName, routingId, bindingPort, applicationJobQueue, applicationJobReceiveFlowFailureSink, peerAdmissionSealed) {
85
87
  this.meshName = meshName;
@@ -108,6 +110,10 @@ class ZLinkNodeRawMeshBackend {
108
110
  this.mailboxRecordDropped = handler;
109
111
  this.stateful?.setMailboxDropHandler(handler);
110
112
  }
113
+ setDispatchErrorReporter(reporter) {
114
+ this.dispatchErrors = reporter;
115
+ this.stateful?.setDispatchErrorReporter(reporter, this.meshName);
116
+ }
111
117
  setProtocolErrorHandler(handler) {
112
118
  this.protocolError = handler;
113
119
  }
@@ -204,6 +210,9 @@ class ZLinkNodeRawMeshBackend {
204
210
  });
205
211
  runtime.start();
206
212
  this.stateful = new service_stateful_runtime_1.ServiceStatefulRuntime(runtime, descriptor.nodeRoutingId, descriptor.lifecycleGeneration);
213
+ if (this.dispatchErrors !== undefined) {
214
+ this.stateful.setDispatchErrorReporter(this.dispatchErrors, this.meshName);
215
+ }
207
216
  if (this.mailboxRecordDropped !== undefined) {
208
217
  this.stateful.setMailboxDropHandler(this.mailboxRecordDropped);
209
218
  }
@@ -375,15 +384,10 @@ class ZLinkNodeRawMeshBackend {
375
384
  return this.observeCompletion(pending.id, service_runtime_contracts_1.OperationKind.NodeRequest, pending.promise);
376
385
  }
377
386
  async sendToChannel(channelName, parts) {
378
- return (await this.requireRuntime().sendToChannel(channelName, encodeMultipartApplicationFrame(parts)))
379
- ? zlink_1.SubmitResult.Ok
380
- : zlink_1.SubmitResult.NotConnected;
387
+ return this.requireRuntime().sendToChannel(channelName, encodeMultipartApplicationFrame(parts));
381
388
  }
382
389
  requestToChannel(channelName, parts, options) {
383
390
  const pending = this.requireRuntime().requestToChannel(channelName, encodeMultipartApplicationFrame(parts), options?.timeoutMs ?? 30_000);
384
- if (pending === undefined) {
385
- return this.enqueueImmediateFailure(service_runtime_contracts_1.OperationKind.ChannelRequest, zlink_1.RequestResult.NotFound);
386
- }
387
391
  return this.observeCompletion(pending.id, service_runtime_contracts_1.OperationKind.ChannelRequest, pending.promise);
388
392
  }
389
393
  status() {
@@ -604,6 +608,9 @@ class ZLinkNodeRawMeshBackend {
604
608
  forgetInstanceIntent(spotId, objectGeneration, authorityOwnerGeneration, storeVersion) {
605
609
  this.requireStateful().forgetInstanceIntent(spotId, objectGeneration, authorityOwnerGeneration, storeVersion);
606
610
  }
611
+ completeClosedInstance(spotId, objectGeneration) {
612
+ this.stateful?.completeClosedInstance(spotId, objectGeneration);
613
+ }
607
614
  rememberSpotRoute(route, expectedCurrentRoute) {
608
615
  this.requireStateful().rememberSpotRoute(route, expectedCurrentRoute);
609
616
  }
@@ -1037,7 +1044,7 @@ class RawStreamSessionService {
1037
1044
  this.requireStarted();
1038
1045
  const sessionKey = String(sessionRid);
1039
1046
  this.sessionTargets.set(sessionKey, sessionRid);
1040
- return this.observe(service_runtime_contracts_1.OperationKind.StreamBind, this.stateful.bindSession(sessionKey, actor, timeoutMs, (targetSessionRid, payloadFrame) => this.deliver(targetSessionRid, payloadFrame), onBindingReplaced, (0, service_session_binding_ingress_port_1.serviceSessionBindingIngressPortIfRegistered)(this), actorAuthority));
1047
+ return this.observe(service_runtime_contracts_1.OperationKind.StreamBind, this.stateful.bindSession(sessionKey, actor, timeoutMs, (targetSessionRid, payloadFrame) => this.deliver(targetSessionRid, actor.actorId, payloadFrame), onBindingReplaced, (0, service_session_binding_ingress_port_1.serviceSessionBindingIngressPortIfRegistered)(this), actorAuthority));
1041
1048
  }
1042
1049
  unbindActor(sessionRid, actor, expectedBindingGeneration, timeoutMs = 30_000) {
1043
1050
  this.requireStarted();
@@ -1050,7 +1057,7 @@ class RawStreamSessionService {
1050
1057
  this.requireStarted();
1051
1058
  return (await this.stateful.sendSessionToActor(String(sessionRid), actor, encodeMultipart(parts)));
1052
1059
  }
1053
- async deliver(sessionRid, payload) {
1060
+ async deliver(sessionRid, actorId, payload) {
1054
1061
  const target = this.sessionTargets.get(sessionRid);
1055
1062
  if (target === undefined)
1056
1063
  return false;
@@ -1058,6 +1065,10 @@ class RawStreamSessionService {
1058
1065
  const parts = decodeMultipartBuffers((0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payload).payload);
1059
1066
  if (parts.length === 0)
1060
1067
  return false;
1068
+ const actorSlot = await (0, service_session_binding_ingress_port_1.serviceSessionBindingIngressPortIfRegistered)(this)?.actorSlot(actorId, sessionRid);
1069
+ if (actorSlot === undefined)
1070
+ return false;
1071
+ parts[0] = Buffer.from(streamFrameWithActorSlot(parts[0], actorSlot));
1061
1072
  let submit = operation.message(parts[0]);
1062
1073
  for (let index = 1; index < parts.length; index++) {
1063
1074
  submit = submit.message(parts[index]);
@@ -1095,6 +1106,11 @@ class RawStreamSessionService {
1095
1106
  }
1096
1107
  }
1097
1108
  }
1109
+ function streamFrameWithActorSlot(frame, actorSlot) {
1110
+ const decodedFrame = (0, stream_wire_1.decodeStreamWireFrame)(frame);
1111
+ const decodedHeader = (0, stream_wire_1.decodeStreamWireHeader)(decodedFrame.header);
1112
+ return (0, stream_wire_1.encodeStreamWireFrame)((0, stream_wire_1.encodeStreamWireHeader)({ ...decodedHeader, actorSlot }), decodedFrame.payload);
1113
+ }
1098
1114
  class RawReadyBatch {
1099
1115
  capacity;
1100
1116
  records = [];
@@ -29,7 +29,7 @@ class ZLinkChannelDispatchPipeline {
29
29
  return;
30
30
  }
31
31
  try {
32
- const flow = (0, flow_context_1.createInboundFlow)(dispatch.fields.flowId, dispatch.fields.flowOrigin, this.options.dispatchErrors.flow.flowCreationEnabled());
32
+ const flow = (0, flow_context_1.createInboundFlow)(dispatch.fields.flowId, dispatch.fields.flowOrigin, this.options.dispatchErrors.flow.flowCreationEnabled(), undefined);
33
33
  const invocation = await (0, flow_context_1.runWithFlow)(flow, () => this.invoke(dispatch.envelope, dispatch.codecs, dispatch.handler, dispatch.context, dispatch.fields, dispatch.signal));
34
34
  if (invocation.handlerInvoked) {
35
35
  this.trace(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Completed, dispatch.fields);
@@ -57,7 +57,7 @@ class ZLinkChannelDispatchPipeline {
57
57
  }
58
58
  let reply;
59
59
  try {
60
- const flow = (0, flow_context_1.createInboundFlow)(dispatch.fields.flowId, dispatch.fields.flowOrigin, this.options.dispatchErrors.flow.flowCreationEnabled());
60
+ const flow = (0, flow_context_1.createInboundFlow)(dispatch.fields.flowId, dispatch.fields.flowOrigin, this.options.dispatchErrors.flow.flowCreationEnabled(), undefined);
61
61
  const invocation = await (0, flow_context_1.runWithFlow)(flow, () => this.invoke(dispatch.envelope, dispatch.codecs, dispatch.handler, dispatch.context, dispatch.fields, dispatch.signal));
62
62
  if (!invocation.handlerInvoked) {
63
63
  throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestRejected, `A handler filter rejected '${this.options.channelName}:${dispatch.fields.packetName}'.`);
@@ -424,15 +424,9 @@ function meshRequestFailure(meshName, result, nativeErrno) {
424
424
  const wireKind = canonical
425
425
  ? (0, framework_errors_internal_1.internalFrameworkErrorKindFromWireReply)(result, nativeErrno)
426
426
  : undefined;
427
- // NotFound without an errno is the selection itself coming back empty: the
428
- // send path and its connection are there, and applying eligibility and drain
429
- // left no member to pick. The spec names that Unavailable, not NotFound, and
430
- // not the protocol error a non-canonical pair would otherwise produce
431
- // (06-framework-api "no eligible select-one member"). A named target that
432
- // does not exist arrives with an errno and still ends as NotFound.
433
- const noEligibleMember = result === runtime_values_1.RequestResult.NotFound && nativeErrno === 0;
434
- const kind = noEligibleMember
435
- ? framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RouteNotConnected
427
+ const localNoMember = result === runtime_values_1.RequestResult.NotFound && nativeErrno === 0;
428
+ const kind = localNoMember
429
+ ? framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestTargetNotFound
436
430
  : !canonical
437
431
  ? framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestProtocolError
438
432
  : result === runtime_values_1.RequestResult.NotFound
@@ -17,6 +17,7 @@ export declare class ZLinkDispatchErrorReporter {
17
17
  readonly flow: ZLinkMessageFlowTracer;
18
18
  constructor(_observerType: undefined, _providerResolver: unknown, errorSink: ZLinkDispatchErrorSink, ctx?: ZLinkDiagnosticsContext, metrics?: ZLinkRuntimeMetrics | undefined);
19
19
  report(event: ZLinkRuntimeDispatchFailure): void;
20
+ captureEnabled(): boolean;
20
21
  get reportedCount(): number;
21
22
  get providerFailureCount(): number;
22
23
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZLinkDispatchErrorReporter = void 0;
4
4
  const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
5
5
  const diagnostics_1 = require("../diagnostics");
6
+ const dispatch_error_details_1 = require("../diagnostics/dispatch-error-details");
6
7
  class ZLinkDispatchErrorReporter {
7
8
  metrics;
8
9
  reportedEvents = 0;
@@ -23,7 +24,6 @@ class ZLinkDispatchErrorReporter {
23
24
  }
24
25
  report(event) {
25
26
  const normalized = normalizeDispatchFailure(event);
26
- const tracePoint = this.flow.begin(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error);
27
27
  const dropReason = channelDropReason(normalized);
28
28
  if (dropReason !== undefined) {
29
29
  this.metrics?.count('zlink.mesh_node.messages.dropped', 1, {
@@ -32,9 +32,10 @@ class ZLinkDispatchErrorReporter {
32
32
  reason: dropReason
33
33
  });
34
34
  }
35
+ const tracePoint = this.flow.begin(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error);
35
36
  if (tracePoint === undefined)
36
37
  return;
37
- const errorInfo = dispatchErrorInfo(normalized);
38
+ const errorInfo = (0, dispatch_error_details_1.dispatchErrorDetails)(normalized.error);
38
39
  this.reportedEvents += 1;
39
40
  tracePoint.trace({
40
41
  outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error,
@@ -64,6 +65,9 @@ class ZLinkDispatchErrorReporter {
64
65
  errorCauseMessage: errorInfo.errorCauseMessage
65
66
  });
66
67
  }
68
+ captureEnabled() {
69
+ return this.flow.enabled(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Error);
70
+ }
67
71
  get reportedCount() {
68
72
  return this.reportedEvents;
69
73
  }
@@ -97,39 +101,3 @@ function channelDropReason(event) {
97
101
  return undefined;
98
102
  }
99
103
  }
100
- function dispatchErrorInfo(event) {
101
- if (event.errorType !== undefined || event.errorMessage !== undefined) {
102
- return {
103
- errorType: event.errorType,
104
- errorMessage: event.errorMessage,
105
- errorCauseType: event.errorCauseType,
106
- errorCauseMessage: event.errorCauseMessage
107
- };
108
- }
109
- if (event.error === undefined) {
110
- return {};
111
- }
112
- if (event.error instanceof Error) {
113
- const cause = deepestErrorCause(event.error);
114
- return {
115
- errorType: event.error.name,
116
- errorMessage: event.error.message,
117
- ...(cause === event.error
118
- ? {}
119
- : {
120
- errorCauseType: cause instanceof Error ? cause.name : typeof cause,
121
- errorCauseMessage: cause instanceof Error ? cause.message : String(cause)
122
- })
123
- };
124
- }
125
- return { errorType: typeof event.error, errorMessage: String(event.error) };
126
- }
127
- function deepestErrorCause(error) {
128
- let current = error;
129
- const seen = new Set();
130
- while (current instanceof Error && current.cause !== undefined && !seen.has(current)) {
131
- seen.add(current);
132
- current = current.cause;
133
- }
134
- return current;
135
- }
@@ -0,0 +1,6 @@
1
+ export declare function dispatchErrorDetails(error: unknown): {
2
+ readonly errorType?: string;
3
+ readonly errorMessage?: string;
4
+ readonly errorCauseType?: string;
5
+ readonly errorCauseMessage?: string;
6
+ };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dispatchErrorDetails = dispatchErrorDetails;
4
+ const ERROR_MESSAGE_MAX_LENGTH = 512;
5
+ const CREDENTIAL_PATTERNS = [
6
+ [/Authorization\s*:\s*(?:(?:Bearer|Basic)\s+)?[^\s,;]+/gi, 'Authorization: <redacted>'],
7
+ [/Bearer\s+[^\s,;]+/gi, 'Bearer <redacted>'],
8
+ [/password\s*=\s*[^\s,;]+/gi, 'password=<redacted>'],
9
+ [/token\s*=\s*[^\s,;]+/gi, 'token=<redacted>']
10
+ ];
11
+ function dispatchErrorDetails(error) {
12
+ if (error === undefined || error === null)
13
+ return {};
14
+ if (error instanceof Error) {
15
+ const cause = deepestErrorCause(error);
16
+ return {
17
+ errorType: error.name,
18
+ errorMessage: sanitizeErrorMessage(error.message),
19
+ ...(cause === error
20
+ ? {}
21
+ : {
22
+ errorCauseType: cause instanceof Error ? cause.name : typeof cause,
23
+ errorCauseMessage: sanitizeErrorMessage(cause instanceof Error ? cause.message : String(cause))
24
+ })
25
+ };
26
+ }
27
+ const errorType = typeof error === 'object' ? error.constructor.name : typeof error;
28
+ const message = typeof error === 'string' ||
29
+ typeof error === 'number' ||
30
+ typeof error === 'boolean' ||
31
+ typeof error === 'bigint'
32
+ ? `${error}`
33
+ : Object.prototype.toString.call(error);
34
+ return { errorType, errorMessage: sanitizeErrorMessage(message) };
35
+ }
36
+ function deepestErrorCause(error) {
37
+ let current = error;
38
+ const seen = new Set();
39
+ while (current instanceof Error && current.cause !== undefined && !seen.has(current)) {
40
+ seen.add(current);
41
+ current = current.cause;
42
+ }
43
+ return current;
44
+ }
45
+ function sanitizeErrorMessage(message) {
46
+ if (message.length === 0)
47
+ return '';
48
+ const lineEnd = message.search(/[\r\n]/u);
49
+ let sanitized = lineEnd < 0 ? message : message.slice(0, lineEnd);
50
+ for (const [pattern, replacement] of CREDENTIAL_PATTERNS) {
51
+ pattern.lastIndex = 0;
52
+ sanitized = sanitized.replace(pattern, replacement);
53
+ }
54
+ return sanitized.slice(0, ERROR_MESSAGE_MAX_LENGTH);
55
+ }
@@ -2,6 +2,7 @@ import type { ZLinkFlowOrigin } from '../../contracts';
2
2
  export interface ZLinkFlowContextValue {
3
3
  readonly flowId: string;
4
4
  readonly flowOrigin: ZLinkFlowOrigin;
5
+ readonly streamSessionId?: string;
5
6
  }
6
7
  export declare function currentFlowContext(): ZLinkFlowContextValue | undefined;
7
8
  export declare function currentOrCreateFlow(origin?: ZLinkFlowOrigin, createIfAbsent?: true): ZLinkFlowContextValue;
@@ -18,4 +19,4 @@ export declare function runWithFlow<T>(flow: ZLinkFlowContextValue | undefined,
18
19
  * observe the same ambient flow, so the source-side hop stays coherent.
19
20
  */
20
21
  export declare function runWithOutboundFlow<T>(enabled: boolean, callback: () => T): T;
21
- export declare function createInboundFlow(flowId?: string, flowOrigin?: ZLinkFlowOrigin, createIfAbsent?: boolean): ZLinkFlowContextValue | undefined;
22
+ export declare function createInboundFlow(flowId: string | undefined, flowOrigin: ZLinkFlowOrigin | undefined, createIfAbsent: boolean, streamSessionId: string | undefined): ZLinkFlowContextValue | undefined;
@@ -50,11 +50,11 @@ function runWithOutboundFlow(enabled, callback) {
50
50
  }
51
51
  return flowStorage.run({ flowId: createFlowId(), flowOrigin: 'Application' }, callback);
52
52
  }
53
- function createInboundFlow(flowId, flowOrigin, createIfAbsent = true) {
53
+ function createInboundFlow(flowId, flowOrigin, createIfAbsent, streamSessionId) {
54
54
  if (!createIfAbsent) {
55
55
  return undefined;
56
56
  }
57
- return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound' };
57
+ return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound', streamSessionId };
58
58
  }
59
59
  function createFlowId() {
60
60
  const bytes = (0, node_crypto_1.randomBytes)(16);
@@ -17,6 +17,7 @@ exports.DEFAULT_ZLINK_DIAGNOSTICS = {
17
17
  };
18
18
  const telemetryLogger = api_logs_1.logs.getLogger('@zlink-systems/framework');
19
19
  const telemetryTracer = api_1.trace.getTracer('@zlink-systems/framework');
20
+ const ERROR_MESSAGE_MAX_LENGTH = 512;
20
21
  let diagnosticsGeneration = 0n;
21
22
  function effectiveMessageFlow(ctx) {
22
23
  return ctx.liveMode.mode;
@@ -111,12 +112,13 @@ class ZLinkMessageFlowTracer {
111
112
  this.traceAcceptedAtMode(flowInput, effectiveMode, flowId, ambient?.flowOrigin);
112
113
  }
113
114
  traceAcceptedAtMode(flowInput, effectiveMode, acceptedFlowId, acceptedFlowOrigin) {
114
- const ambient = acceptedFlowId === undefined ? (0, flow_context_1.currentFlowContext)() : undefined;
115
+ const ambient = (0, flow_context_1.currentFlowContext)();
115
116
  const flowId = flowInput.flowId ?? acceptedFlowId ?? ambient?.flowId;
116
117
  const flowOrigin = flowInput.flowOrigin ?? acceptedFlowOrigin ?? ambient?.flowOrigin;
117
118
  const flow = {
118
119
  ...flowInput,
119
120
  ...(flowId === undefined ? {} : { flowId, flowOrigin }),
121
+ streamSessionId: flowInput.streamSessionId ?? ambient?.streamSessionId,
120
122
  effectiveMode
121
123
  };
122
124
  this.tracedEvents += 1;
@@ -237,11 +239,12 @@ function toTelemetryRecord(flow, messageSizeBytes, durationSeconds) {
237
239
  instanceSpotType: flow.instanceSpotType,
238
240
  activationState: flow.activationState,
239
241
  actorId: flow.actorId,
242
+ streamSessionId: flow.streamSessionId,
240
243
  commandId: flow.commandId,
241
244
  messageSizeBytes,
242
245
  durationSeconds,
243
246
  errorType: flow.errorType,
244
- errorMessage: boundedText(flow.errorMessage),
247
+ errorMessage: flow.errorMessage,
245
248
  errorCauseType: flow.errorCauseType,
246
249
  errorCauseMessage: boundedText(flow.errorCauseMessage)
247
250
  };
@@ -277,6 +280,7 @@ function structuredLogAttributes(record) {
277
280
  instance_spot_type: record.instanceSpotType,
278
281
  activation_state: record.activationState,
279
282
  actor_id: record.actorId,
283
+ stream_session_id: record.streamSessionId,
280
284
  correlation_id: record.correlationId,
281
285
  flow_id: record.flowId,
282
286
  flow_origin: record.flowOrigin,
@@ -311,11 +315,15 @@ function structuredLogBodyAttributes(record) {
311
315
  instance_type: record.instanceSpotType,
312
316
  activation_state: record.activationState,
313
317
  actor: record.actorId,
318
+ session: record.streamSessionId,
314
319
  corr: record.correlationId,
315
320
  flow: record.flowId,
316
321
  origin: record.flowOrigin,
317
322
  outcome: record.outcome,
318
323
  reason: record.reason,
324
+ action: record.action,
325
+ error_type: record.errorType,
326
+ error_message: record.errorMessage,
319
327
  size: record.messageSizeBytes
320
328
  });
321
329
  }
@@ -421,7 +429,7 @@ function hashFlowId(flowId) {
421
429
  return hash >>> 0;
422
430
  }
423
431
  function boundedText(value) {
424
- return value === undefined ? undefined : value.slice(0, 512);
432
+ return value === undefined ? undefined : value.slice(0, ERROR_MESSAGE_MAX_LENGTH);
425
433
  }
426
434
  function toSnakeCase(value) {
427
435
  return value.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
@@ -11,7 +11,6 @@ const service_stateful_wire_codec_1 = require("./service-stateful-wire-codec");
11
11
  const RECOVERY_PACKET_NAME = '__zlink.actor.routed_join.recovery';
12
12
  const RECOVERY_CONTENT_TYPE = 'application/x-zlink-actor-routed-join-recovery-v1';
13
13
  const MAXIMUM_METADATA_BYTES = 256 * 1024;
14
- const MAXIMUM_MESSAGE_BYTES = 1024 * 1024;
15
14
  const FRAMEWORK_METADATA_UPPER_BOUND_BYTES = 64 * 1024;
16
15
  const ACCEPTED_JOURNAL_UPPER_BOUND_BYTES = 16 * 1024 * 1024;
17
16
  const SNAPSHOT_APPLICATION_STATE_RESERVATION_BYTES = 64 * 1024 * 1024;
@@ -51,9 +50,6 @@ function encodeCanonicalActorJoinRecoverySavedWork(input) {
51
50
  const operationId = input.operationId;
52
51
  const reply = operationId === undefined ? Buffer.alloc(0) : Buffer.from(input.reply ?? []);
53
52
  const request = Buffer.from(input.request);
54
- if (request.byteLength > MAXIMUM_MESSAGE_BYTES || reply.byteLength > MAXIMUM_MESSAGE_BYTES) {
55
- throw new RangeError('Actor Join recovery message exceeds 1 MiB.');
56
- }
57
53
  const replyContentType = operationId === undefined
58
54
  ? null
59
55
  : requireText(input.replyContentType, 'Actor Join reply content type');
@@ -1,4 +1,5 @@
1
1
  import type { ZLinkRawBindingPort } from '../backend/raw-binding-port';
2
+ import { SubmitResult } from '../backend/runtime-values';
2
3
  import type { ApplicationJobQueuePort } from '../application-jobs/contracts';
3
4
  import { ApplicationIngressRecordOwner } from '../application-jobs/application-ingress-record-owner';
4
5
  import { type PendingOperation } from './operation-registry';
@@ -107,9 +108,10 @@ export declare class RawServiceMeshRuntime {
107
108
  replaceDiscoveredNotRequired(descriptors: readonly ServiceNodeDescriptor[]): void;
108
109
  isObjectClientNodeDirectTarget(nodeRoutingId: string): boolean;
109
110
  sendToNode(targetNodeRoutingId: string, payload: ServiceApplicationPayloadInput): Promise<boolean>;
110
- sendToChannel(channelName: string, payload: ServiceApplicationPayloadInput): Promise<boolean>;
111
+ sendToChannel(channelName: string, payload: ServiceApplicationPayloadInput): Promise<SubmitResult>;
111
112
  requestToNode(targetNodeRoutingId: string, payload: ServiceApplicationPayloadInput, timeoutMs: number): PendingOperation<RawServiceRequestResult>;
112
- requestToChannel(channelName: string, payload: ServiceApplicationPayloadInput, timeoutMs: number): PendingOperation<RawServiceRequestResult> | undefined;
113
+ requestToChannel(channelName: string, payload: ServiceApplicationPayloadInput, timeoutMs: number): PendingOperation<RawServiceRequestResult>;
114
+ private selectChannelTarget;
113
115
  private isLocalOrReadyPeer;
114
116
  setServiceIngress(handler: RawServiceIngressHandler): void;
115
117
  reserveLocalIngress(signal?: AbortSignal): Promise<ApplicationIngressRecordOwner>;