@zlink-systems/stream-connector 0.24.0 → 0.26.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.
@@ -19,8 +19,9 @@ export declare class ZlinkStreamSendBuilder implements ZlinkStreamSendCall {
19
19
  export declare class ZlinkStreamRequestBuilder implements ZlinkStreamRequestCall {
20
20
  private readonly connector;
21
21
  private readonly payload;
22
+ private readonly enqueueCallback;
22
23
  private readonly state;
23
- constructor(connector: ZlinkStreamConnectorSubmitter, name: string | undefined, payload: ZlinkStreamEncodedPayload, actorSlot?: number, validateActor?: () => void);
24
+ constructor(connector: ZlinkStreamConnectorSubmitter, name: string | undefined, payload: ZlinkStreamEncodedPayload, enqueueCallback: (callback: () => void) => void, actorSlot?: number, validateActor?: () => void);
24
25
  packetName(name: string): this;
25
26
  metadata(key: string, value: string): this;
26
27
  metadata(metadata: ZlinkStreamMetadata): this;
@@ -31,6 +31,11 @@ export declare class BrowserWebSocketConnection implements ZlinkStreamConnection
31
31
  constructor(socket: BrowserWebSocket);
32
32
  write(frame: Uint8Array, signal?: AbortSignal): Promise<void>;
33
33
  read(signal?: AbortSignal): Promise<Uint8Array | undefined>;
34
+ /**
35
+ * Spec stream-connector 32 §7: closing does not wait for the peer to read or
36
+ * answer. `WebSocket.close` sends the close frame after the data already
37
+ * handed to `send`, so a frame whose write completed is not torn.
38
+ */
34
39
  close(signal?: AbortSignal): Promise<void>;
35
40
  private readonly onMessage;
36
41
  private readonly onClose;
@@ -40,6 +40,5 @@ export declare class ZlinkStreamActors {
40
40
  private bind;
41
41
  private unbind;
42
42
  private queue;
43
- private invoke;
44
43
  }
45
44
  export {};
@@ -71,7 +71,7 @@ export declare class DefaultZlinkStreamConnector implements ZlinkStreamConnector
71
71
  private encodePayload;
72
72
  private decodePayload;
73
73
  private decodeWaitPayload;
74
- sendEncoded(kind: ZlinkStreamMessageKind, name: string, payload: ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata, compress: boolean, requestSeq: bigint | undefined, signal?: AbortSignal, correlationId?: string, actorSlot?: number): Promise<void>;
74
+ sendEncoded(kind: ZlinkStreamMessageKind, name: string, payload: ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata, compress: boolean, requestSeq: bigint | undefined, signal?: AbortSignal, correlationId?: string, actorSlot?: number, expiry?: Promise<unknown>, onAccepted?: () => void): Promise<void>;
75
75
  /**
76
76
  * Per-connector monotonic correlation id (hex). The client generates it on each request
77
77
  * and the server echoes it back on the reply.
@@ -1,13 +1,28 @@
1
1
  import type { Disposable, ZlinkStreamConnectionStateChanged, ZlinkStreamError } from '../Contracts';
2
2
  export declare class ZlinkStreamConnectorEvents {
3
+ private readonly enqueueCallback;
4
+ /**
5
+ * @param enqueueCallback Queues a callback that calls the number of handlers
6
+ * `callbacks` reports for the handlers registered at that moment.
7
+ */
8
+ constructor(enqueueCallback: (callback: () => void, callbacks: () => number) => void);
3
9
  private readonly errorHandlers;
4
10
  private readonly disconnectedHandlers;
5
11
  private readonly stateHandlers;
6
12
  onError(handler: (error: ZlinkStreamError, signal?: AbortSignal) => Promise<void> | void): Disposable;
7
13
  onDisconnected(handler: (signal?: AbortSignal) => Promise<void> | void): Disposable;
8
14
  onStateChanged(handler: (change: ZlinkStreamConnectionStateChanged, signal?: AbortSignal) => Promise<void> | void): Disposable;
9
- publishError(error: ZlinkStreamError, signal?: AbortSignal): Promise<void>;
10
- publishDisconnected(signal?: AbortSignal): Promise<void>;
11
- publishStateChanged(change: ZlinkStreamConnectionStateChanged, signal?: AbortSignal): Promise<void>;
15
+ publishError(error: ZlinkStreamError, signal?: AbortSignal): void;
16
+ publishDisconnected(signal?: AbortSignal): void;
17
+ publishStateChanged(change: ZlinkStreamConnectionStateChanged, signal?: AbortSignal): void;
18
+ /**
19
+ * Spec stream-connector 32 §7: the connector runs a registered callback and
20
+ * does not wait for it to finish. A thrown or rejected failure reaches the
21
+ * error handlers as `UserCallbackFailed`.
22
+ */
23
+ runUserCallback(run: () => Promise<void> | void, failureMessage: string, signal?: AbortSignal): void;
12
24
  private publish;
25
+ private reportFailure;
26
+ private reportToRemaining;
27
+ private invoke;
13
28
  }
@@ -4,7 +4,7 @@ import type { ZlinkStreamCloseReason } from '../Contracts';
4
4
  import type { ZlinkStreamConnectorEvents } from './ZlinkStreamConnectorEvents';
5
5
  import type { ZlinkStreamFrameSender } from './ZlinkStreamFrameSender';
6
6
  import type { ZlinkStreamPendingRequests } from './ZlinkStreamPendingRequests';
7
- import type { ZlinkStreamReceiveDispatcher } from './ZlinkStreamReceiveDispatcher';
7
+ import { type ZlinkStreamReceiveDispatcher } from './ZlinkStreamReceiveDispatcher';
8
8
  import type { ZlinkStreamReceivedMessages } from './ZlinkStreamReceivedMessages';
9
9
  import type { ZlinkStreamActors } from './ZlinkStreamActors';
10
10
  export declare class ZlinkStreamConnectorLifecycle {
@@ -27,6 +27,7 @@ export declare class ZlinkStreamConnectorLifecycle {
27
27
  private lastInboundAt;
28
28
  private closeTask;
29
29
  private connectTask;
30
+ private connectAbort;
30
31
  private disconnectTask;
31
32
  private closeRequested;
32
33
  private disconnectedPublished;
@@ -37,7 +38,20 @@ export declare class ZlinkStreamConnectorLifecycle {
37
38
  get state(): ZlinkStreamConnectionState;
38
39
  get closeReason(): ZlinkStreamCloseReason | undefined;
39
40
  connect(signal?: AbortSignal): Promise<void>;
41
+ /**
42
+ * Spec stream-connector 32 §7: close does not wait for a connect attempt or a
43
+ * reconnect delay to run its course. The attempts listen to one controller
44
+ * the lifecycle owns; close aborts it, and the caller's signal is forwarded
45
+ * to it.
46
+ */
40
47
  private connectOnce;
48
+ /**
49
+ * Spec stream-connector 32 §7: a `close` called outside the registered
50
+ * handlers and callbacks returns once the close work has ended. One called
51
+ * from inside them returns right after starting it; the result goes to the
52
+ * outside caller, so a callback never waits for the close of the path that
53
+ * is running it.
54
+ */
41
55
  close(signal?: AbortSignal): Promise<void>;
42
56
  serverClosing(reason: ZlinkStreamCloseReason): Promise<void>;
43
57
  private closeOnce;
@@ -64,9 +78,21 @@ export declare class ZlinkStreamConnectorLifecycle {
64
78
  private releaseReceiveLoopSettled;
65
79
  private shouldContinueReceiveLoop;
66
80
  private runHeartbeatTick;
81
+ /**
82
+ * @param reason Given only where the reason is not read from `error`: a
83
+ * server `session-closing` and the heartbeat timeout. Every other ending
84
+ * takes it from {@link closeReasonFor}.
85
+ */
67
86
  private disconnectForTransportFailure;
68
87
  private isCurrentConnection;
69
- /** Transport teardown only — no application callback runs from here. */
88
+ /**
89
+ * Ends the current connection, for close and for transport loss alike. No
90
+ * application callback runs from here. Spec stream-connector 32 §7 and §9:
91
+ * every operation the ending connection fails (the frames it has not
92
+ * written, the one it is writing and the pending requests) fails with
93
+ * `Disconnected`, whatever ended it; the cause stays in the close reason.
94
+ * The transport is closed without waiting for the frames.
95
+ */
70
96
  private tearDownConnection;
71
97
  /**
72
98
  * Runs once the teardown promise has settled, so a handler reached from here
@@ -78,35 +104,13 @@ export declare class ZlinkStreamConnectorLifecycle {
78
104
  */
79
105
  private announceDisconnect;
80
106
  private shouldReconnect;
81
- private claimDisconnectedPublish;
82
- private publishDisconnectedOnce;
83
107
  /**
84
- * Spec stream-connector 32 §7: the connector runs the registered handler, it
85
- * does not wait for it to finish. `close` returns once its own work is done —
86
- * the drain, the transport close, the pending requests — having run the
87
- * disconnect handler but without looking at whether that handler has ended.
88
- *
89
- * Starting the publish without awaiting it still runs every handler right
90
- * here: an `async` function body runs synchronously up to its first `await`,
91
- * and `publishDisconnected` reaches each handler before that point. So the
92
- * handler has run by the time `close` returns, which is what the spec asks
93
- * for, while a handler that calls `close` no longer waits for the very
94
- * `closeTask` it is running inside. Java and C++ hand the handler to a queue
95
- * and return the same way.
96
- *
97
- * `claimDisconnectedPublish` is taken by the same call and before any await,
98
- * so deferring the completion never turns the one notification into two.
99
- *
100
- * Nothing awaits the promise, so a rejection would reach the process as an
101
- * unhandled rejection and kill it. Handler failures are already contained
102
- * where they were before this call stopped waiting — the `Promise.allSettled`
103
- * in `publishDisconnected` — and this `catch` covers what is left rather than
104
- * reporting the same failure a second time on the error surface.
108
+ * Spec stream-connector 32 §6 and §7: one disconnect notification per
109
+ * lifecycle event, handed to the dispatch queue. The connector does not wait
110
+ * for the handler; `Manual` runs it when the application pumps dispatch.
111
+ * The flag is tested and set synchronously, so concurrent callers for the
112
+ * same event claim it once.
105
113
  */
106
- private publishDisconnectedWithoutWaiting;
107
- private queueDisconnected;
108
- private queueState;
114
+ private publishDisconnected;
109
115
  private setState;
110
- private transitionState;
111
- private publishStateChange;
112
116
  }
@@ -1,11 +1,27 @@
1
- import type { ZlinkStreamConnection, ZlinkStreamEncodedPayload, ZlinkStreamMetadata, ZlinkStreamMessageKind } from '../Contracts';
1
+ import type { ZlinkStreamConnection, ZlinkStreamEncodedPayload, ZlinkStreamError, ZlinkStreamMetadata, ZlinkStreamMessageKind } from '../Contracts';
2
2
  import type { ZlinkStreamFrameProtocol } from './Protocol/ZlinkStreamFrameProtocol';
3
3
  export declare class ZlinkStreamFrameSender {
4
4
  private readonly protocol;
5
- private readonly pendingWrites;
5
+ private readonly queues;
6
6
  constructor(protocol: ZlinkStreamFrameProtocol);
7
- send(connection: ZlinkStreamConnection, kind: ZlinkStreamMessageKind, name: string, payload: ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata, compress: boolean, requestSeq: bigint | undefined, signal?: AbortSignal, correlationId?: string, actorSlot?: number): Promise<void>;
8
- sendControl(connection: ZlinkStreamConnection, name: string, signal?: AbortSignal): Promise<void>;
9
- drain(signal?: AbortSignal): Promise<void>;
7
+ send(connection: ZlinkStreamConnection, kind: ZlinkStreamMessageKind, name: string, payload: ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata, compress: boolean, requestSeq: bigint | undefined, signal?: AbortSignal, correlationId?: string, actorSlot?: number, expiry?: Promise<unknown>, onAccepted?: () => void): Promise<void>;
8
+ /**
9
+ * Writes a control frame (heartbeat ping or pong). A control frame belongs to
10
+ * no operation: its write failure ends the connection through the write
11
+ * queue like any other write (spec stream-connector 32 §9), and a connection
12
+ * that has already ended does not write it. The promise settles when the
13
+ * write has ended either way.
14
+ */
15
+ sendControl(connection: ZlinkStreamConnection, name: string): Promise<void>;
16
+ /**
17
+ * Gives a connection that has just been established its own write queue.
18
+ * `writeFailed` ends that connection when one of its transport writes fails.
19
+ */
20
+ open(connection: ZlinkStreamConnection, writeFailed: (error: ZlinkStreamError) => void): void;
21
+ /**
22
+ * Ends the write queue of a connection that has ended, by close and by
23
+ * transport loss alike. Every operation still in it fails with `error`.
24
+ */
25
+ failUnwritten(connection: ZlinkStreamConnection, error: unknown): void;
10
26
  private write;
11
27
  }
@@ -2,13 +2,13 @@ import { ZlinkStreamEncodedPayload, ZlinkStreamError, ZlinkStreamMessage, ZlinkS
2
2
  export interface PendingZlinkStreamRequest {
3
3
  readonly requestSeq: bigint;
4
4
  readonly promise: Promise<ZlinkStreamMessage<ZlinkStreamEncodedPayload>>;
5
+ startTimeout(): void;
5
6
  }
6
7
  export declare class ZlinkStreamPendingRequests {
7
8
  private nextRequestSeq;
8
9
  private readonly active;
9
- get count(): number;
10
10
  create(packetName: string, timeoutMs: number): PendingZlinkStreamRequest;
11
- resolve(requestSeq: bigint, value: ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata): boolean;
11
+ resolve(requestSeq: bigint, value: () => ZlinkStreamEncodedPayload, metadata: ZlinkStreamMetadata): boolean;
12
12
  reject(requestSeq: bigint, error: ZlinkStreamError): boolean;
13
13
  cancel(requestSeq: bigint): void;
14
14
  failAll(error: ZlinkStreamError): void;
@@ -1,4 +1,4 @@
1
- import type { ZlinkStreamConnection } from '../Contracts';
1
+ import type { ZlinkStreamConnection, ZlinkStreamError } from '../Contracts';
2
2
  import type { ZlinkStreamFrameProtocol } from './Protocol/ZlinkStreamFrameProtocol';
3
3
  import type { ZlinkStreamConnectorEvents } from './ZlinkStreamConnectorEvents';
4
4
  import type { ZlinkStreamFrameSender } from './ZlinkStreamFrameSender';
@@ -6,6 +6,18 @@ import type { ZlinkStreamPendingRequests } from './ZlinkStreamPendingRequests';
6
6
  import type { ZlinkStreamReceivedMessages } from './ZlinkStreamReceivedMessages';
7
7
  import type { ZlinkStreamCloseReason } from '../Contracts';
8
8
  import { type ZlinkStreamActors } from './ZlinkStreamActors';
9
+ /**
10
+ * Spec stream-connector 32 §9: the one mapping from the error that ended a
11
+ * connection to its close reason. A frame or header the connector cannot
12
+ * decode and a payload over the receive limit are protocol violations; every
13
+ * other failure — a transport read or write — is a transport error.
14
+ */
15
+ export declare function closeReasonFor(error: ZlinkStreamError): ZlinkStreamCloseReason;
16
+ /** A receive failure that ends the connection; {@link closeReasonFor} names its reason. */
17
+ export declare class ZlinkStreamConnectionEnd extends Error {
18
+ readonly error: ZlinkStreamError;
19
+ constructor(error: ZlinkStreamError);
20
+ }
9
21
  export interface ZlinkStreamReceiveResult {
10
22
  readonly available: boolean;
11
23
  readonly inbound: boolean;
@@ -28,11 +40,15 @@ export declare class ZlinkStreamReceiveDispatcher {
28
40
  * the recheck the rest of a dead connection's batch lands on the new
29
41
  * connection's state, and the §10 arrival counts, which start at 0 when a
30
42
  * connection is established, take the stale frames on top.
31
- * @param connectionForSend Resolves the connection a reply belongs on at the
32
- * moment it is written. The captured `connection` is the one the batch was
33
- * read from, which a reconnect may already have replaced.
34
43
  */
35
- readAndDispatch(connection: ZlinkStreamConnection | undefined, signal?: AbortSignal, isCurrent?: () => boolean, connectionForSend?: () => ZlinkStreamConnection): Promise<ZlinkStreamReceiveResult>;
44
+ readAndDispatch(connection: ZlinkStreamConnection | undefined, signal?: AbortSignal, isCurrent?: () => boolean): Promise<ZlinkStreamReceiveResult>;
45
+ /**
46
+ * Spec stream-connector 32 §4.7 and §9: a decompressed payload over the
47
+ * receive limit is `FrameTooLarge` and ends the connection as a protocol
48
+ * error, like a wire payload over the limit. A payload that does not
49
+ * decompress fails only its packet.
50
+ */
51
+ private decodePayload;
36
52
  private dispatch;
37
53
  private dispatchControl;
38
54
  }
@@ -13,9 +13,9 @@ export declare class ZlinkStreamReceivedMessages {
13
13
  private readonly handlers;
14
14
  private readonly observers;
15
15
  private readonly queue;
16
+ private readonly deliverable;
16
17
  private queueHead;
17
18
  private queuedCount;
18
- private drainTask;
19
19
  private draining;
20
20
  private readonly receivedCounts;
21
21
  /**
@@ -25,7 +25,11 @@ export declare class ZlinkStreamReceivedMessages {
25
25
  * queue in both modes, so they never depend on this flag.
26
26
  */
27
27
  constructor(events: ZlinkStreamConnectorEvents, deliverOnArrival: boolean);
28
- on(name: string, handler: EncodedMessageHandler): Disposable;
28
+ /**
29
+ * @param actor The Actor handle that registers the handler, if any. Its
30
+ * handler receives only that Actor's packets.
31
+ */
32
+ on(name: string, handler: EncodedMessageHandler, actor?: object): Disposable;
29
33
  /**
30
34
  * Registers a wait surface over the receive queue. Spec stream-connector 32
31
35
  * §7: these are not registered callbacks — they observe and consume the
@@ -74,25 +78,42 @@ export declare class ZlinkStreamReceivedMessages {
74
78
  */
75
79
  connectionEnded(): void;
76
80
  enqueue(message: ZlinkStreamMessage<ZlinkStreamEncodedPayload>, signal?: AbortSignal): void;
77
- enqueueCallback(callback: () => Promise<void> | void): void;
81
+ /**
82
+ * @param callbacks The number of handler calls `callback` makes when it runs
83
+ * with the handlers registered at that moment; a callback that runs a set
84
+ * of handlers reads the set's size. One callback by default.
85
+ */
86
+ enqueueCallback(callback: () => Promise<void> | void, callbacks?: () => number): void;
78
87
  /**
79
88
  * Runs the registered handlers the receive path left queued. `Manual` calls
80
- * this from `dispatch`; `Immediate` has already drained on arrival.
89
+ * this from `dispatch`; `Immediate` drains on arrival.
81
90
  *
82
- * A handler that calls `dispatch` arrives back here from inside the drain it
83
- * was started by. `scheduleDrain` would find `drainTask` already set and
84
- * return, and the await below would then be the drain waiting on itself —
85
- * a deadlock with neither timeout nor error. The drain loop already takes
86
- * every message a handler exists for, so there is nothing a second drain
87
- * would deliver and returning is the whole of the correct behaviour.
91
+ * The drain is synchronous: the connector does not wait for a handler
92
+ * (spec stream-connector 32 §7), so nothing inside it awaits. A handler that
93
+ * calls `dispatch` arrives back here from inside the drain it was started by;
94
+ * the running loop already takes every deliverable entry, so returning is the
95
+ * whole of the correct behaviour.
88
96
  */
89
- pump(): Promise<void>;
97
+ pump(): void;
98
+ /**
99
+ * True while a registered callback runs from {@link pump}: the execution
100
+ * context spec stream-connector 32 §7 calls "inside a handler".
101
+ */
102
+ get dispatching(): boolean;
103
+ get pendingCallbacks(): number;
90
104
  private offerQueued;
91
- private scheduleDrain;
92
- private drain;
93
105
  private removeAt;
94
106
  private findDeliverableIndex;
95
- private hasQueuedMessage;
107
+ private indexDeliverable;
108
+ private isDeliverable;
109
+ /**
110
+ * Spec stream-connector 32 §7 and §10: the handlers that receive `message`
111
+ * are the connector handlers for its name and the handle handlers of the
112
+ * Actor it carries. A packet no handler receives stays in the queue for the
113
+ * wait surfaces. This is the one place that decides it.
114
+ */
115
+ private receiversOf;
116
+ private removeDeliverable;
96
117
  private advanceHead;
97
118
  private compactQueue;
98
119
  }