@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
- package/dist/runtime/actors/actor-context.js +4 -4
- package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
- package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
- package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +4 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +24 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
- package/dist/runtime/channels/channel-transports.js +3 -9
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
- package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
- package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
- package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
- package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
- package/dist/runtime/diagnostics/flow-context.js +2 -2
- package/dist/runtime/diagnostics/message-flow.js +11 -3
- package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +5 -0
- package/dist/runtime/foundation/service-stateful-runtime.js +52 -2
- package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
- package/dist/runtime/foundation/service-topology-registry.js +12 -4
- package/dist/runtime/host/index.js +7 -3
- package/dist/runtime/locations/in-memory-location-store.js +5 -0
- package/dist/runtime/locations/location-store-repository.js +3 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
- package/dist/runtime/spots/index.js +9 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
- package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
- package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.js +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
- package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
- package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
- package/dist/runtime/streams/bound-session-response-target.js +8 -1
- package/dist/runtime/streams/bound-session-service.d.ts +1 -0
- package/dist/runtime/streams/bound-session-service.js +6 -3
- package/dist/runtime/streams/index.js +8 -1
- package/dist/runtime/streams/managed-stream.d.ts +3 -0
- package/dist/runtime/streams/managed-stream.js +20 -1
- package/dist/runtime/streams/protocol.d.ts +5 -1
- package/dist/runtime/streams/protocol.js +41 -4
- package/dist/runtime/streams/session-actor-coordinator.js +3 -4
- package/dist/runtime/streams/session-context.d.ts +9 -2
- package/dist/runtime/streams/session-context.js +20 -4
- package/dist/runtime/streams/session-local-actors.d.ts +3 -1
- package/dist/runtime/streams/session-local-actors.js +17 -2
- package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
- package/dist/runtime/streams/stream-frame-factory.js +3 -2
- package/dist/runtime/streams/stream-session-runtime.js +42 -12
- package/package.json +3 -3
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
- package/src/contracts/Streams/IZLinkSession.ts +2 -1
- package/src/runtime/actors/actor-context.ts +4 -4
- package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
- package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
- package/src/runtime/backend/contracts/index.ts +5 -0
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +43 -11
- package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
- package/src/runtime/channels/channel-transports.ts +3 -9
- package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
- package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
- package/src/runtime/diagnostics/flow-context.ts +6 -4
- package/src/runtime/diagnostics/message-flow.ts +12 -3
- package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +71 -5
- package/src/runtime/foundation/service-topology-registry.ts +18 -5
- package/src/runtime/host/index.ts +11 -4
- package/src/runtime/locations/in-memory-location-store.ts +7 -0
- package/src/runtime/locations/location-store-repository.ts +4 -1
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
- package/src/runtime/spots/index.ts +22 -2
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
- package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
- package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
- package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
- package/src/runtime/spots/spot-timer.ts +6 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
- package/src/runtime/streams/bound-session-response-target.ts +10 -1
- package/src/runtime/streams/bound-session-service.ts +13 -3
- package/src/runtime/streams/index.ts +8 -1
- package/src/runtime/streams/managed-stream.ts +34 -12
- package/src/runtime/streams/protocol.ts +44 -5
- package/src/runtime/streams/session-actor-coordinator.ts +12 -4
- package/src/runtime/streams/session-context.ts +31 -4
- package/src/runtime/streams/session-local-actors.ts +19 -3
- package/src/runtime/streams/stream-frame-factory.ts +4 -2
- package/src/runtime/streams/stream-session-runtime.ts +58 -13
|
@@ -163,8 +163,23 @@ class DefaultZLinkSessionContext {
|
|
|
163
163
|
findBoundActor(actorId) {
|
|
164
164
|
return this.localActors.find(actorId);
|
|
165
165
|
}
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
actorForSlot(actorSlot) {
|
|
167
|
+
if (actorSlot === undefined)
|
|
168
|
+
return undefined;
|
|
169
|
+
return this.localActors.findBySlot(actorSlot);
|
|
170
|
+
}
|
|
171
|
+
get actorSlotControls() {
|
|
172
|
+
if (this.stream.enqueueActorBound === undefined ||
|
|
173
|
+
this.stream.enqueueActorUnbound === undefined) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
enqueueBound: (actorSlot, actorId) => this.stream.enqueueActorBound(actorSlot, actorId),
|
|
178
|
+
enqueueUnbound: (actorSlot) => this.stream.enqueueActorUnbound(actorSlot)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
bindLocal(actor, token, actorSlot) {
|
|
182
|
+
this.localActors.bind(actor, token, actorSlot);
|
|
168
183
|
}
|
|
169
184
|
unbindLocal(actorId, token) {
|
|
170
185
|
this.localActors.unbind(actorId, token);
|
|
@@ -313,11 +328,12 @@ class DefaultZLinkSessionActor {
|
|
|
313
328
|
}
|
|
314
329
|
exports.DefaultZLinkSessionActor = DefaultZLinkSessionActor;
|
|
315
330
|
const SESSION_DISPATCH_HEADERS = new WeakMap();
|
|
316
|
-
function createSessionDispatchContext(header) {
|
|
331
|
+
function createSessionDispatchContext(header, actor) {
|
|
317
332
|
const dispatch = {
|
|
318
333
|
packetName: header.name,
|
|
319
334
|
metadata: header.metadata,
|
|
320
|
-
canReply: header.requestSeq !== undefined
|
|
335
|
+
canReply: header.requestSeq !== undefined,
|
|
336
|
+
...(actor === undefined ? {} : { actor })
|
|
321
337
|
};
|
|
322
338
|
SESSION_DISPATCH_HEADERS.set(dispatch, header);
|
|
323
339
|
return dispatch;
|
|
@@ -3,8 +3,10 @@ export interface ZLinkSessionLocalActor {
|
|
|
3
3
|
}
|
|
4
4
|
export declare class ZLinkSessionLocalActorBindings<TActor extends ZLinkSessionLocalActor> {
|
|
5
5
|
private readonly actors;
|
|
6
|
+
private readonly actorIdsBySlot;
|
|
6
7
|
snapshot(): readonly TActor[];
|
|
7
8
|
find(actorId: string): TActor | undefined;
|
|
8
|
-
|
|
9
|
+
findBySlot(actorSlot: number): TActor | undefined;
|
|
10
|
+
bind(actor: TActor, token: string, actorSlot?: number): void;
|
|
9
11
|
unbind(actorId: string, token: string): void;
|
|
10
12
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ZLinkSessionLocalActorBindings = void 0;
|
|
4
4
|
class ZLinkSessionLocalActorBindings {
|
|
5
5
|
actors = new Map();
|
|
6
|
+
actorIdsBySlot = new Map();
|
|
6
7
|
snapshot() {
|
|
7
8
|
const snapshot = [];
|
|
8
9
|
for (const entry of this.actors.values()) {
|
|
@@ -13,13 +14,27 @@ class ZLinkSessionLocalActorBindings {
|
|
|
13
14
|
find(actorId) {
|
|
14
15
|
return this.actors.get(actorId)?.actor;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
this.
|
|
17
|
+
findBySlot(actorSlot) {
|
|
18
|
+
const actorId = this.actorIdsBySlot.get(actorSlot);
|
|
19
|
+
return actorId === undefined ? undefined : this.actors.get(actorId)?.actor;
|
|
20
|
+
}
|
|
21
|
+
bind(actor, token, actorSlot) {
|
|
22
|
+
const previous = this.actors.get(actor.actorId);
|
|
23
|
+
if (previous?.actorSlot !== undefined && previous.actorSlot !== actorSlot) {
|
|
24
|
+
this.actorIdsBySlot.delete(previous.actorSlot);
|
|
25
|
+
}
|
|
26
|
+
this.actors.set(actor.actorId, { actor, token, actorSlot });
|
|
27
|
+
if (actorSlot !== undefined) {
|
|
28
|
+
this.actorIdsBySlot.set(actorSlot, actor.actorId);
|
|
29
|
+
}
|
|
18
30
|
}
|
|
19
31
|
unbind(actorId, token) {
|
|
20
32
|
const current = this.actors.get(actorId);
|
|
21
33
|
if (current?.token === token) {
|
|
22
34
|
this.actors.delete(actorId);
|
|
35
|
+
if (current.actorSlot !== undefined) {
|
|
36
|
+
this.actorIdsBySlot.delete(current.actorSlot);
|
|
37
|
+
}
|
|
23
38
|
}
|
|
24
39
|
}
|
|
25
40
|
}
|
|
@@ -27,7 +27,7 @@ export declare class ZLinkStreamFrameMessageFactory {
|
|
|
27
27
|
constructor(options: ZLinkStreamFrameMessageFactoryOptions);
|
|
28
28
|
createTextMessage(payload: string): Message;
|
|
29
29
|
createBinaryMessage(payload: Uint8Array): Message;
|
|
30
|
-
createJsonFrameMessage(kind: ZLinkStreamMessageKind, packetName: string, metadata: ReadonlyMap<string, string>, compressed: boolean, requestSeq: bigint | undefined, payload: unknown, correlationId?: string): Message;
|
|
30
|
+
createJsonFrameMessage(kind: ZLinkStreamMessageKind, packetName: string, metadata: ReadonlyMap<string, string>, compressed: boolean, requestSeq: bigint | undefined, payload: unknown, correlationId?: string, actorSlot?: number): Message;
|
|
31
31
|
createJsonReplyFrameMessage(requestHeader: ZLinkStreamFrameHeader, kind: ZLinkStreamReplyMessageKind, metadata: ReadonlyMap<string, string>, compressed: boolean, payload: unknown): Message;
|
|
32
32
|
private createJsonFrameMessageWithHeader;
|
|
33
33
|
/**
|
|
@@ -26,7 +26,7 @@ class ZLinkStreamFrameMessageFactory {
|
|
|
26
26
|
}
|
|
27
27
|
return factory.createTextMessage((0, protocol_1.utf8Decode)(payload));
|
|
28
28
|
}
|
|
29
|
-
createJsonFrameMessage(kind, packetName, metadata, compressed, requestSeq, payload, correlationId) {
|
|
29
|
+
createJsonFrameMessage(kind, packetName, metadata, compressed, requestSeq, payload, correlationId, actorSlot) {
|
|
30
30
|
const flow = (this.options.flowCreationEnabled?.() ?? true)
|
|
31
31
|
? (0, flow_context_1.currentOrCreateFlow)('Application')
|
|
32
32
|
: undefined;
|
|
@@ -41,7 +41,8 @@ class ZLinkStreamFrameMessageFactory {
|
|
|
41
41
|
metadata,
|
|
42
42
|
correlationId,
|
|
43
43
|
flowId: flow?.flowId,
|
|
44
|
-
flowOrigin: flow?.flowOrigin
|
|
44
|
+
flowOrigin: flow?.flowOrigin,
|
|
45
|
+
actorSlot
|
|
45
46
|
}));
|
|
46
47
|
}
|
|
47
48
|
createJsonReplyFrameMessage(requestHeader, kind, metadata, compressed, payload) {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZLinkStreamSessionNodeRuntime = exports.ZLinkStreamSessionRuntime = void 0;
|
|
4
4
|
const framework_errors_internal_1 = require("../framework-errors-internal");
|
|
5
|
-
const contracts_1 = require("../../contracts");
|
|
6
5
|
const internal_event_contracts_1 = require("../diagnostics/internal-event-contracts");
|
|
7
6
|
const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
|
|
8
7
|
const abort_1 = require("../abort");
|
|
9
8
|
const channels_1 = require("../channels");
|
|
9
|
+
const bound_session_response_target_1 = require("./bound-session-response-target");
|
|
10
10
|
const diagnostics_1 = require("../diagnostics");
|
|
11
11
|
const payload_codec_1 = require("../messaging/payload-codec");
|
|
12
12
|
const protocol_1 = require("./protocol");
|
|
@@ -329,6 +329,40 @@ class ZLinkStreamSessionRuntime {
|
|
|
329
329
|
if (this.context.tryCompleteResponse(decodedHeader, dispatchPayload)) {
|
|
330
330
|
return;
|
|
331
331
|
}
|
|
332
|
+
const dispatchActor = this.context.actorForSlot(decodedHeader.actorSlot);
|
|
333
|
+
if (decodedHeader.actorSlot !== undefined && dispatchActor === undefined) {
|
|
334
|
+
const staleFlowEnabled = this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true;
|
|
335
|
+
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(decodedHeader.flowId, decodedHeader.flowOrigin, staleFlowEnabled, staleFlowEnabled ? this.stream.sessionId : undefined), async () => {
|
|
336
|
+
if (decodedHeader.kind === protocol_1.ZLinkStreamMessageKind.Request) {
|
|
337
|
+
const error = (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidOperation, 'Actor slot is not a current binding.');
|
|
338
|
+
this.options.dispatchErrors?.report({
|
|
339
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
|
|
340
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Request,
|
|
341
|
+
reason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget,
|
|
342
|
+
action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.ReplyError,
|
|
343
|
+
packetName: decodedHeader.name,
|
|
344
|
+
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
|
|
345
|
+
correlationId: decodedHeader.correlationId,
|
|
346
|
+
flowId: decodedHeader.flowId,
|
|
347
|
+
flowOrigin: decodedHeader.flowOrigin,
|
|
348
|
+
error
|
|
349
|
+
});
|
|
350
|
+
await this.replyDispatchError(decodedHeader, error);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
(0, diagnostics_1.flowIfEnabled)(this.options.dispatchErrors?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped)?.trace({
|
|
354
|
+
outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped,
|
|
355
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
|
|
356
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
357
|
+
packetName: decodedHeader.name,
|
|
358
|
+
correlationId: decodedHeader.correlationId,
|
|
359
|
+
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
|
|
360
|
+
errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
332
366
|
this.context.enterDispatch(decodedHeader);
|
|
333
367
|
enteredDispatch = true;
|
|
334
368
|
const session = await this.requireSession();
|
|
@@ -337,7 +371,8 @@ class ZLinkStreamSessionRuntime {
|
|
|
337
371
|
: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send;
|
|
338
372
|
const streamCorr = decodedHeader.correlationId;
|
|
339
373
|
const inboundHeader = decodedHeader;
|
|
340
|
-
|
|
374
|
+
const flowEnabled = this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true;
|
|
375
|
+
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(inboundHeader.flowId, inboundHeader.flowOrigin, flowEnabled, flowEnabled ? this.stream.sessionId : undefined), async () => {
|
|
341
376
|
(0, diagnostics_1.flowIfEnabled)(this.options.dispatchErrors?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Received)?.trace({
|
|
342
377
|
outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Received,
|
|
343
378
|
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
|
|
@@ -363,7 +398,7 @@ class ZLinkStreamSessionRuntime {
|
|
|
363
398
|
correlationId: streamCorr,
|
|
364
399
|
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId)
|
|
365
400
|
});
|
|
366
|
-
await session.onDispatch?.((0, session_context_1.createSessionDispatchContext)(inboundHeader), (0, payload_codec_1.wrapFrameworkPayloadMessage)(dispatchPayload, this.options.messageSerializers, (0, protocol_1.streamCodecContentType)(inboundHeader.codec), inboundHeader.name));
|
|
401
|
+
await session.onDispatch?.((0, session_context_1.createSessionDispatchContext)(inboundHeader, dispatchActor), (0, payload_codec_1.wrapFrameworkPayloadMessage)(dispatchPayload, this.options.messageSerializers, (0, protocol_1.streamCodecContentType)(inboundHeader.codec), inboundHeader.name));
|
|
367
402
|
if (streamKind === ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send) {
|
|
368
403
|
(0, diagnostics_1.flowIfEnabled)(this.options.dispatchErrors?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Completed)?.trace({
|
|
369
404
|
outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Completed,
|
|
@@ -478,14 +513,7 @@ class ZLinkStreamSessionRuntime {
|
|
|
478
513
|
// record (spec 26 §2.2) and confuse the peer's reply matching.
|
|
479
514
|
return;
|
|
480
515
|
}
|
|
481
|
-
const message = this.context.createJsonReplyFrameMessage(header, protocol_1.ZLinkStreamMessageKind.Error, new Map(), false,
|
|
482
|
-
code: error instanceof contracts_1.ZLinkFrameworkException
|
|
483
|
-
? contracts_1.ZLinkFrameworkErrorKind[error.kind]
|
|
484
|
-
: error instanceof Error
|
|
485
|
-
? error.constructor.name
|
|
486
|
-
: 'RemoteError',
|
|
487
|
-
message: error instanceof Error ? error.message : String(error)
|
|
488
|
-
});
|
|
516
|
+
const message = this.context.createJsonReplyFrameMessage(header, protocol_1.ZLinkStreamMessageKind.Error, new Map(), false, (0, bound_session_response_target_1.boundSessionErrorPayload)(error));
|
|
489
517
|
try {
|
|
490
518
|
if (!this.context.stream.writeRaw(message, ZLINK_SEND_DONT_WAIT)) {
|
|
491
519
|
throw new Error('Client stream error reply send failed.');
|
|
@@ -518,7 +546,8 @@ class ZLinkStreamSessionRuntime {
|
|
|
518
546
|
: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
519
547
|
packetName: packetName.length === 0 ? undefined : packetName,
|
|
520
548
|
correlationId,
|
|
521
|
-
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId)
|
|
549
|
+
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
|
|
550
|
+
streamSessionId: this.stream.sessionId
|
|
522
551
|
});
|
|
523
552
|
}
|
|
524
553
|
async complete(error, notifyDisconnected) {
|
|
@@ -627,6 +656,7 @@ class ZLinkStreamSessionRuntime {
|
|
|
627
656
|
packetName: header.name === '' ? undefined : header.name,
|
|
628
657
|
correlationId: header.correlationId,
|
|
629
658
|
sourceRid: this.context.routingId === undefined ? undefined : String(this.context.routingId),
|
|
659
|
+
streamSessionId: this.stream.sessionId,
|
|
630
660
|
flowId: flowEnabled ? header.flowId : undefined,
|
|
631
661
|
flowOrigin: flowEnabled ? header.flowOrigin : undefined,
|
|
632
662
|
errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.Shutdown
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zlink-systems/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@opentelemetry/api": "^1.9.1",
|
|
21
21
|
"@opentelemetry/api-logs": "^0.221.0",
|
|
22
|
-
"@zlink-systems/stream-wire": "0.
|
|
23
|
-
"@zlink-systems/zlink": "1.
|
|
22
|
+
"@zlink-systems/stream-wire": "0.24.0",
|
|
23
|
+
"@zlink-systems/zlink": "1.6.0"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -80,6 +80,7 @@ export interface ZLinkRuntimeMessageFlowEvent {
|
|
|
80
80
|
readonly instanceSpotType?: string;
|
|
81
81
|
readonly activationState?: 'activating' | 'ready' | 'closing';
|
|
82
82
|
readonly actorId?: string;
|
|
83
|
+
readonly streamSessionId?: string;
|
|
83
84
|
/** Internal service-wire command carried by a RouteMesh control record. */
|
|
84
85
|
readonly commandId?: number;
|
|
85
86
|
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
|
|
|
@@ -16,6 +16,7 @@ export interface ZLinkSessionDispatchContext {
|
|
|
16
16
|
readonly packetName: string;
|
|
17
17
|
readonly metadata: ReadonlyMap<string, string>;
|
|
18
18
|
readonly canReply: boolean;
|
|
19
|
+
readonly actor?: ZLinkSessionActor;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export interface ZLinkSessionFactory<TSession extends ZLinkSession = ZLinkSession> {
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import { createRandomOperationIdentity } from '../foundation/operation-identity';
|
|
40
40
|
import { deferActorJoin } from './actor-join-deferred-scope';
|
|
41
41
|
import { captureZLinkSpotSerialTurn } from '../execution';
|
|
42
|
+
import { dispatchErrorDetails } from '../diagnostics/dispatch-error-details';
|
|
42
43
|
|
|
43
44
|
export const ZLINK_ACTOR_JOIN_ENTRY_SPOT_RUNTIME = Symbol('zlink.actor.join-entry-spot-runtime');
|
|
44
45
|
|
|
@@ -219,7 +220,6 @@ class DefaultZLinkActorJoinSpotCall implements ZLinkActorJoinSpotCall {
|
|
|
219
220
|
};
|
|
220
221
|
try {
|
|
221
222
|
deferActorJoin({
|
|
222
|
-
requestBytes: requestMessage.data().byteLength,
|
|
223
223
|
prepare: async () => {
|
|
224
224
|
if (prepared) return;
|
|
225
225
|
prepared = true;
|
|
@@ -358,7 +358,6 @@ class DefaultZLinkActorJoinEntrySpotCall implements ZLinkActorJoinEntrySpotCall
|
|
|
358
358
|
};
|
|
359
359
|
try {
|
|
360
360
|
deferActorJoin({
|
|
361
|
-
requestBytes: requestMessage.data().byteLength,
|
|
362
361
|
prepare: async () => {
|
|
363
362
|
if (prepared) return;
|
|
364
363
|
prepared = true;
|
|
@@ -588,6 +587,7 @@ async function notifyJoinFailure(
|
|
|
588
587
|
// The completion carries only an error kind, so trace the cause on the
|
|
589
588
|
// message flow under the same flow id as the Join that produced it.
|
|
590
589
|
const tracePoint = coordinator?.messageFlow?.()?.begin(ZLinkMessageFlowOutcome.Error);
|
|
590
|
+
const errorDetails = tracePoint === undefined ? undefined : dispatchErrorDetails(error);
|
|
591
591
|
tracePoint?.trace({
|
|
592
592
|
outcome: ZLinkMessageFlowOutcome.Error,
|
|
593
593
|
surface: ZLinkDispatchErrorSurface.SpotActor,
|
|
@@ -596,8 +596,8 @@ async function notifyJoinFailure(
|
|
|
596
596
|
actorId: actor.context.actorId,
|
|
597
597
|
errorReason: ZLinkDispatchErrorReason.HandlerException,
|
|
598
598
|
errorAction: ZLinkDispatchErrorAction.ReplyError,
|
|
599
|
-
errorType:
|
|
600
|
-
errorMessage:
|
|
599
|
+
errorType: errorDetails?.errorType,
|
|
600
|
+
errorMessage: errorDetails?.errorMessage
|
|
601
601
|
});
|
|
602
602
|
await actor.onJoinCompleted?.({
|
|
603
603
|
status: 'failed',
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ZLinkFrameworkInternalErrorKind,
|
|
3
|
-
createInternalFrameworkException
|
|
4
|
-
} from '../framework-errors-internal';
|
|
5
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
2
|
import { ZLinkConfigurationException } from '../configuration';
|
|
7
3
|
|
|
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
|
-
|
|
12
4
|
interface DeferredActorJoin {
|
|
13
|
-
readonly requestBytes: number;
|
|
14
5
|
/** Starts target admission after the handler terminal, before reply admission. */
|
|
15
6
|
readonly prepare?: () => Promise<void> | void;
|
|
16
7
|
readonly execute: () => Promise<void>;
|
|
@@ -19,7 +10,6 @@ interface DeferredActorJoin {
|
|
|
19
10
|
|
|
20
11
|
class ActorJoinRegistrationScope {
|
|
21
12
|
private readonly intents: DeferredActorJoin[] = [];
|
|
22
|
-
private totalBytes = 0;
|
|
23
13
|
private open = true;
|
|
24
14
|
private prepared = false;
|
|
25
15
|
|
|
@@ -34,29 +24,7 @@ class ActorJoinRegistrationScope {
|
|
|
34
24
|
'Actor join defer requires an open Framework actor handler scope.'
|
|
35
25
|
);
|
|
36
26
|
}
|
|
37
|
-
if (this.intents.length >= MAX_DEFERRED_JOINS) {
|
|
38
|
-
discardWithoutWaiting(intent);
|
|
39
|
-
throw createInternalFrameworkException(
|
|
40
|
-
ZLinkFrameworkInternalErrorKind.InvalidConfiguration,
|
|
41
|
-
`An actor handler may defer at most ${MAX_DEFERRED_JOINS} joins.`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
if (intent.requestBytes > MAX_DEFERRED_JOIN_REQUEST_BYTES) {
|
|
45
|
-
discardWithoutWaiting(intent);
|
|
46
|
-
throw createInternalFrameworkException(
|
|
47
|
-
ZLinkFrameworkInternalErrorKind.InvalidConfiguration,
|
|
48
|
-
`A deferred actor join request may use at most ${MAX_DEFERRED_JOIN_REQUEST_BYTES} bytes.`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
if (this.totalBytes + intent.requestBytes > MAX_DEFERRED_JOIN_BYTES) {
|
|
52
|
-
discardWithoutWaiting(intent);
|
|
53
|
-
throw createInternalFrameworkException(
|
|
54
|
-
ZLinkFrameworkInternalErrorKind.InvalidConfiguration,
|
|
55
|
-
`Deferred actor join requests may use at most ${MAX_DEFERRED_JOIN_BYTES} bytes per handler.`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
27
|
this.intents.push(intent);
|
|
59
|
-
this.totalBytes += intent.requestBytes;
|
|
60
28
|
}
|
|
61
29
|
|
|
62
30
|
sealAfterHandlerTerminal(): void {
|
|
@@ -44,7 +44,6 @@ import {
|
|
|
44
44
|
const RETENTION_MS = 24 * 60 * 60 * 1_000;
|
|
45
45
|
const ROOT_VERSION = 2;
|
|
46
46
|
const ROOT_MAGIC = Buffer.from('ZLJR');
|
|
47
|
-
const MAX_ROOT_BYTES = 1024 * 1024;
|
|
48
47
|
const JOURNAL_INVENTORY_DOMAIN = Buffer.from('zlink-node-deferred-join-authority-v1\0', 'utf8');
|
|
49
48
|
|
|
50
49
|
export type ZLinkDeferredJoinDeliveryCursor = 'prepared' | 'committed' | 'delivered';
|
|
@@ -695,17 +694,14 @@ function encodeRoot(
|
|
|
695
694
|
'utf8'
|
|
696
695
|
)
|
|
697
696
|
]);
|
|
698
|
-
if (encoded.byteLength > MAX_ROOT_BYTES) {
|
|
699
|
-
throw new Error('Deferred Join completion root exceeds 1 MiB.');
|
|
700
|
-
}
|
|
701
697
|
return encoded;
|
|
702
698
|
}
|
|
703
699
|
|
|
704
700
|
function decodeRoot(
|
|
705
701
|
payload: Uint8Array
|
|
706
702
|
): Omit<ZLinkDeferredJoinAcceptedRoot, 'authority' | 'reference' | 'checksumCrc32c'> {
|
|
707
|
-
if (payload.byteLength === 0
|
|
708
|
-
throw new Error('Deferred Join completion root
|
|
703
|
+
if (payload.byteLength === 0) {
|
|
704
|
+
throw new Error('Deferred Join completion root is empty.');
|
|
709
705
|
}
|
|
710
706
|
const bytes = Buffer.from(payload);
|
|
711
707
|
const encoded = hasJournalRootMagic(bytes) ? bytes.subarray(ROOT_MAGIC.byteLength) : bytes;
|
|
@@ -60,6 +60,11 @@ export type ZLinkBackendObjectPlacement =
|
|
|
60
60
|
| { readonly kind: 'unavailable' };
|
|
61
61
|
|
|
62
62
|
export interface ZLinkBackendMeshNode {
|
|
63
|
+
completeClosedInstance?(spotId: string, objectGeneration: bigint): void;
|
|
64
|
+
setDispatchErrorReporter?(
|
|
65
|
+
reporter: import('../../channels/dispatch-error-reporter').ZLinkDispatchErrorReporter,
|
|
66
|
+
meshName: string
|
|
67
|
+
): void;
|
|
63
68
|
setRoutingId(routingId: unknown): void;
|
|
64
69
|
setBind(endpoint: string): void;
|
|
65
70
|
setAdvertiseHost(host: string): void;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { setImmediate as yieldToIO } from 'node:timers/promises';
|
|
3
|
+
import {
|
|
4
|
+
decodeStreamWireFrame,
|
|
5
|
+
decodeStreamWireHeader,
|
|
6
|
+
encodeStreamWireFrame,
|
|
7
|
+
encodeStreamWireHeader
|
|
8
|
+
} from '@zlink-systems/stream-wire';
|
|
3
9
|
import { disconnectStreamPeer } from './node-socket-backend-adapter';
|
|
4
10
|
import { ZLinkFrameworkException } from '../../../contracts';
|
|
5
11
|
import { internalFrameworkWireReply } from '../../framework-errors-internal';
|
|
@@ -20,6 +26,7 @@ import {
|
|
|
20
26
|
} from '../runtime-values';
|
|
21
27
|
import { ZLinkBufferMessage } from '../runtime-message';
|
|
22
28
|
import type { Message as FrameworkMessage } from '../../../contracts/Common/Message';
|
|
29
|
+
import type { ZLinkDispatchErrorReporter } from '../../channels/dispatch-error-reporter';
|
|
23
30
|
import type {
|
|
24
31
|
MeshOperationId,
|
|
25
32
|
MeshPeerEntry,
|
|
@@ -178,6 +185,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
178
185
|
private messageFollowHandler?: (
|
|
179
186
|
record: import('../../foundation/service-stateful-wire-codec').ServiceMessageFollowRecord
|
|
180
187
|
) => void;
|
|
188
|
+
private dispatchErrors?: ZLinkDispatchErrorReporter;
|
|
181
189
|
private readonly peerDisconnectedHandlers = new Set<(endpoint: string) => void>();
|
|
182
190
|
constructor(
|
|
183
191
|
private readonly meshName: string,
|
|
@@ -228,6 +236,11 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
228
236
|
this.stateful?.setMailboxDropHandler(handler);
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter): void {
|
|
240
|
+
this.dispatchErrors = reporter;
|
|
241
|
+
this.stateful?.setDispatchErrorReporter(reporter, this.meshName);
|
|
242
|
+
}
|
|
243
|
+
|
|
231
244
|
setProtocolErrorHandler(
|
|
232
245
|
handler: (record: {
|
|
233
246
|
readonly sourceNodeRid: string;
|
|
@@ -381,6 +394,9 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
381
394
|
descriptor.nodeRoutingId,
|
|
382
395
|
descriptor.lifecycleGeneration
|
|
383
396
|
);
|
|
397
|
+
if (this.dispatchErrors !== undefined) {
|
|
398
|
+
this.stateful.setDispatchErrorReporter(this.dispatchErrors, this.meshName);
|
|
399
|
+
}
|
|
384
400
|
if (this.mailboxRecordDropped !== undefined) {
|
|
385
401
|
this.stateful.setMailboxDropHandler(this.mailboxRecordDropped);
|
|
386
402
|
}
|
|
@@ -649,12 +665,7 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
649
665
|
channelName: string,
|
|
650
666
|
parts: MessageLike | readonly MessageLike[]
|
|
651
667
|
): Promise<SubmitResultValue> {
|
|
652
|
-
return
|
|
653
|
-
channelName,
|
|
654
|
-
encodeMultipartApplicationFrame(parts)
|
|
655
|
-
))
|
|
656
|
-
? SubmitResult.Ok
|
|
657
|
-
: SubmitResult.NotConnected;
|
|
668
|
+
return this.requireRuntime().sendToChannel(channelName, encodeMultipartApplicationFrame(parts));
|
|
658
669
|
}
|
|
659
670
|
|
|
660
671
|
requestToChannel(
|
|
@@ -667,9 +678,6 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
667
678
|
encodeMultipartApplicationFrame(parts),
|
|
668
679
|
options?.timeoutMs ?? 30_000
|
|
669
680
|
);
|
|
670
|
-
if (pending === undefined) {
|
|
671
|
-
return this.enqueueImmediateFailure(OperationKind.ChannelRequest, RequestResult.NotFound);
|
|
672
|
-
}
|
|
673
681
|
return this.observeCompletion(pending.id, OperationKind.ChannelRequest, pending.promise);
|
|
674
682
|
}
|
|
675
683
|
|
|
@@ -1054,6 +1062,10 @@ export class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
1054
1062
|
);
|
|
1055
1063
|
}
|
|
1056
1064
|
|
|
1065
|
+
completeClosedInstance(spotId: string, objectGeneration: bigint): void {
|
|
1066
|
+
this.stateful?.completeClosedInstance(spotId, objectGeneration);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1057
1069
|
rememberSpotRoute(
|
|
1058
1070
|
route: ServiceDirectSpotRouteFence,
|
|
1059
1071
|
expectedCurrentRoute?: ServiceDirectSpotRouteFence | null
|
|
@@ -1875,7 +1887,8 @@ class RawStreamSessionService implements StreamSessionService {
|
|
|
1875
1887
|
sessionKey,
|
|
1876
1888
|
actor,
|
|
1877
1889
|
timeoutMs,
|
|
1878
|
-
(targetSessionRid, payloadFrame) =>
|
|
1890
|
+
(targetSessionRid, payloadFrame) =>
|
|
1891
|
+
this.deliver(targetSessionRid, actor.actorId, payloadFrame),
|
|
1879
1892
|
onBindingReplaced,
|
|
1880
1893
|
serviceSessionBindingIngressPortIfRegistered(this),
|
|
1881
1894
|
actorAuthority
|
|
@@ -1913,12 +1926,22 @@ class RawStreamSessionService implements StreamSessionService {
|
|
|
1913
1926
|
)) as SubmitResultValue;
|
|
1914
1927
|
}
|
|
1915
1928
|
|
|
1916
|
-
private async deliver(
|
|
1929
|
+
private async deliver(
|
|
1930
|
+
sessionRid: string,
|
|
1931
|
+
actorId: string,
|
|
1932
|
+
payload: Uint8Array
|
|
1933
|
+
): Promise<boolean> {
|
|
1917
1934
|
const target = this.sessionTargets.get(sessionRid);
|
|
1918
1935
|
if (target === undefined) return false;
|
|
1919
1936
|
const operation = this.stream.send(target as unknown as BindingRoutingId);
|
|
1920
1937
|
const parts = decodeMultipartBuffers(decodeApplicationPayloadView(payload).payload);
|
|
1921
1938
|
if (parts.length === 0) return false;
|
|
1939
|
+
const actorSlot = await serviceSessionBindingIngressPortIfRegistered(this)?.actorSlot(
|
|
1940
|
+
actorId,
|
|
1941
|
+
sessionRid
|
|
1942
|
+
);
|
|
1943
|
+
if (actorSlot === undefined) return false;
|
|
1944
|
+
parts[0] = Buffer.from(streamFrameWithActorSlot(parts[0]!, actorSlot));
|
|
1922
1945
|
let submit = operation.message(parts[0]!);
|
|
1923
1946
|
for (let index = 1; index < parts.length; index++) {
|
|
1924
1947
|
submit = submit.message(parts[index]!);
|
|
@@ -1956,6 +1979,15 @@ class RawStreamSessionService implements StreamSessionService {
|
|
|
1956
1979
|
}
|
|
1957
1980
|
}
|
|
1958
1981
|
|
|
1982
|
+
function streamFrameWithActorSlot(frame: Uint8Array, actorSlot: number): Uint8Array {
|
|
1983
|
+
const decodedFrame = decodeStreamWireFrame(frame);
|
|
1984
|
+
const decodedHeader = decodeStreamWireHeader(decodedFrame.header);
|
|
1985
|
+
return encodeStreamWireFrame(
|
|
1986
|
+
encodeStreamWireHeader({ ...decodedHeader, actorSlot }),
|
|
1987
|
+
decodedFrame.payload
|
|
1988
|
+
);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1959
1991
|
class RawReadyBatch implements ReadyBatch {
|
|
1960
1992
|
readonly records: ReadyRecord[] = [];
|
|
1961
1993
|
private readonly claims: RawClaim[] = [];
|
|
@@ -110,7 +110,8 @@ export class ZLinkChannelDispatchPipeline {
|
|
|
110
110
|
const flow = createInboundFlow(
|
|
111
111
|
dispatch.fields.flowId,
|
|
112
112
|
dispatch.fields.flowOrigin,
|
|
113
|
-
this.options.dispatchErrors.flow.flowCreationEnabled()
|
|
113
|
+
this.options.dispatchErrors.flow.flowCreationEnabled(),
|
|
114
|
+
undefined
|
|
114
115
|
);
|
|
115
116
|
const invocation = await runWithFlow(flow, () =>
|
|
116
117
|
this.invoke(
|
|
@@ -161,7 +162,8 @@ export class ZLinkChannelDispatchPipeline {
|
|
|
161
162
|
const flow = createInboundFlow(
|
|
162
163
|
dispatch.fields.flowId,
|
|
163
164
|
dispatch.fields.flowOrigin,
|
|
164
|
-
this.options.dispatchErrors.flow.flowCreationEnabled()
|
|
165
|
+
this.options.dispatchErrors.flow.flowCreationEnabled(),
|
|
166
|
+
undefined
|
|
165
167
|
);
|
|
166
168
|
const invocation = await runWithFlow(flow, () =>
|
|
167
169
|
this.invoke(
|
|
@@ -1140,15 +1140,9 @@ export function meshRequestFailure(
|
|
|
1140
1140
|
const wireKind = canonical
|
|
1141
1141
|
? internalFrameworkErrorKindFromWireReply(result, nativeErrno)
|
|
1142
1142
|
: undefined;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
// not the protocol error a non-canonical pair would otherwise produce
|
|
1147
|
-
// (06-framework-api "no eligible select-one member"). A named target that
|
|
1148
|
-
// does not exist arrives with an errno and still ends as NotFound.
|
|
1149
|
-
const noEligibleMember = result === RequestResult.NotFound && nativeErrno === 0;
|
|
1150
|
-
const kind: ZLinkFrameworkInternalErrorKindType = noEligibleMember
|
|
1151
|
-
? ZLinkFrameworkInternalErrorKind.RouteNotConnected
|
|
1143
|
+
const localNoMember = result === RequestResult.NotFound && nativeErrno === 0;
|
|
1144
|
+
const kind: ZLinkFrameworkInternalErrorKindType = localNoMember
|
|
1145
|
+
? ZLinkFrameworkInternalErrorKind.RequestTargetNotFound
|
|
1152
1146
|
: !canonical
|
|
1153
1147
|
? ZLinkFrameworkInternalErrorKind.RequestProtocolError
|
|
1154
1148
|
: result === RequestResult.NotFound
|