@actuallab/rpc 12.3.85 → 12.4.6
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/index.cjs +103 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -26
- package/dist/index.d.ts +16 -26
- package/dist/index.js +103 -171
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -995,7 +995,7 @@ interface RpcStreamRef {
|
|
|
995
995
|
readonly hostId: string;
|
|
996
996
|
readonly localId: number;
|
|
997
997
|
readonly ackPeriod: number;
|
|
998
|
-
readonly
|
|
998
|
+
readonly bufferSize: number;
|
|
999
999
|
readonly allowReconnect: boolean;
|
|
1000
1000
|
readonly isRealTime: boolean;
|
|
1001
1001
|
}
|
|
@@ -1009,13 +1009,13 @@ type RpcStreamSource<T> = AsyncIterable<T> | ((abortSignal: AbortSignal) => Asyn
|
|
|
1009
1009
|
/** Configuration options for creating a local (origin-side) RpcStream. */
|
|
1010
1010
|
interface RpcStreamOptions<T> {
|
|
1011
1011
|
ackPeriod?: number;
|
|
1012
|
-
|
|
1012
|
+
bufferSize?: number;
|
|
1013
1013
|
allowReconnect?: boolean;
|
|
1014
1014
|
isRealTime?: boolean;
|
|
1015
1015
|
canSkipTo?: (item: T) => boolean;
|
|
1016
1016
|
}
|
|
1017
1017
|
/**
|
|
1018
|
-
* Parses a stream reference string of the form "{hostId},{localId},{ackPeriod},{
|
|
1018
|
+
* Parses a stream reference string of the form "{hostId},{localId},{ackPeriod},{bufferSize}".
|
|
1019
1019
|
* Returns null if the value is not a valid stream reference.
|
|
1020
1020
|
*/
|
|
1021
1021
|
declare function parseStreamRef(value: unknown): RpcStreamRef | null;
|
|
@@ -1047,7 +1047,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1047
1047
|
readonly isRealTime: boolean;
|
|
1048
1048
|
readonly canSkipTo: (item: T) => boolean;
|
|
1049
1049
|
readonly ackPeriod: number;
|
|
1050
|
-
readonly
|
|
1050
|
+
readonly bufferSize: number;
|
|
1051
1051
|
readonly localSource?: RpcStreamSource<T>;
|
|
1052
1052
|
readonly peer: RpcPeer;
|
|
1053
1053
|
private _sender?;
|
|
@@ -1078,7 +1078,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1078
1078
|
* stream reference.
|
|
1079
1079
|
*
|
|
1080
1080
|
* For binary peers the ref is a MessagePack-compatible object
|
|
1081
|
-
* (`{ SerializedId, AckPeriod,
|
|
1081
|
+
* (`{ SerializedId, AckPeriod, BufferSize, AllowReconnect }`);
|
|
1082
1082
|
* for text peers it is a comma-separated string.
|
|
1083
1083
|
*
|
|
1084
1084
|
* Can only be called once per stream.
|
|
@@ -1104,7 +1104,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1104
1104
|
* Recursively walks a deserialized value and replaces any stream ref strings
|
|
1105
1105
|
* with live RpcStream<unknown> instances registered on the given peer.
|
|
1106
1106
|
* This handles nested stream refs inside complex return types (e.g. Table<T>
|
|
1107
|
-
* containing RpcStream<Row<T>> fields serialized as "{hostId},{localId},{ackPeriod},{
|
|
1107
|
+
* containing RpcStream<Row<T>> fields serialized as "{hostId},{localId},{ackPeriod},{bufferSize}").
|
|
1108
1108
|
*/
|
|
1109
1109
|
declare function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown;
|
|
1110
1110
|
|
|
@@ -1123,13 +1123,12 @@ declare function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown;
|
|
|
1123
1123
|
* The pump is ACK-driven: the main loop blocks on `_whenAckReady` until a
|
|
1124
1124
|
* client ACK is queued, processes it (updating `_lastAckedIndex`, rewinding
|
|
1125
1125
|
* `_index` on mustReset, trimming the replay buffer), then sends items up
|
|
1126
|
-
* to `ack.nextIndex +
|
|
1126
|
+
* to `ack.nextIndex + bufferSize`. While the peer is disconnected no ACKs
|
|
1127
1127
|
* arrive, so the source is never pulled — the sender naturally pauses
|
|
1128
1128
|
* instead of spinning.
|
|
1129
1129
|
*
|
|
1130
1130
|
* Direct port of .NET `RpcSharedStream<T>` at
|
|
1131
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs
|
|
1132
|
-
* separate non-real-time / real-time pump variants.
|
|
1131
|
+
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1133
1132
|
*/
|
|
1134
1133
|
declare class RpcStreamSender<T> implements IRpcObject {
|
|
1135
1134
|
private static readonly _emptyGuid;
|
|
@@ -1140,7 +1139,7 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1140
1139
|
readonly canSkipTo: (item: T) => boolean;
|
|
1141
1140
|
readonly peer: RpcPeer;
|
|
1142
1141
|
readonly ackPeriod: number;
|
|
1143
|
-
readonly
|
|
1142
|
+
readonly bufferSize: number;
|
|
1144
1143
|
/**
|
|
1145
1144
|
* If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`
|
|
1146
1145
|
* and may honor the AbortSignal to exit gracefully. `disconnect()` first aborts
|
|
@@ -1161,7 +1160,7 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1161
1160
|
private _iterator;
|
|
1162
1161
|
private _acks;
|
|
1163
1162
|
private _whenAckReady;
|
|
1164
|
-
constructor(peer: RpcPeer, ackPeriod?: number,
|
|
1163
|
+
constructor(peer: RpcPeer, ackPeriod?: number, bufferSize?: number, allowReconnect?: boolean, isRealTime?: boolean, canSkipTo?: (item: T) => boolean, sourceUsesAbortSignal?: boolean);
|
|
1165
1164
|
/** AbortSignal that is aborted when the sender is disconnected. */
|
|
1166
1165
|
get abortSignal(): AbortSignal;
|
|
1167
1166
|
/** Returns the stream reference string for the $sys.Ok response. */
|
|
@@ -1192,25 +1191,16 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1192
1191
|
/**
|
|
1193
1192
|
* Consume an AsyncIterable and send all items to the client.
|
|
1194
1193
|
*
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:105-108.
|
|
1194
|
+
* Matches .NET `RpcSharedStream<T>.OnRun` at
|
|
1195
|
+
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1198
1196
|
*/
|
|
1199
1197
|
writeFrom(source: AsyncIterable<T>): Promise<void>;
|
|
1200
1198
|
/**
|
|
1201
|
-
* Non-real-time
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:110-217.
|
|
1205
|
-
*/
|
|
1206
|
-
private _runNonRealTime;
|
|
1207
|
-
/**
|
|
1208
|
-
* Real-time pump — drops stale items to stay close to the source's head.
|
|
1209
|
-
*
|
|
1210
|
-
* Direct port of .NET `OnRunRealTime` at
|
|
1211
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:219-420.
|
|
1199
|
+
* ACK-driven pump. Non-real-time streams apply normal backpressure at
|
|
1200
|
+
* `bufferSize`; real-time streams may compact the already-buffered unsent
|
|
1201
|
+
* suffix to the latest buffered `canSkipTo` item after the consumer ACKs.
|
|
1212
1202
|
*/
|
|
1213
|
-
private
|
|
1203
|
+
private _run;
|
|
1214
1204
|
/** Drain all queued ACKs, returning the most recent one (or null if none). */
|
|
1215
1205
|
private _tryProcessAcks;
|
|
1216
1206
|
/** Wait for the ACK queue to have at least one entry (or `_ended`). */
|
package/dist/index.d.ts
CHANGED
|
@@ -995,7 +995,7 @@ interface RpcStreamRef {
|
|
|
995
995
|
readonly hostId: string;
|
|
996
996
|
readonly localId: number;
|
|
997
997
|
readonly ackPeriod: number;
|
|
998
|
-
readonly
|
|
998
|
+
readonly bufferSize: number;
|
|
999
999
|
readonly allowReconnect: boolean;
|
|
1000
1000
|
readonly isRealTime: boolean;
|
|
1001
1001
|
}
|
|
@@ -1009,13 +1009,13 @@ type RpcStreamSource<T> = AsyncIterable<T> | ((abortSignal: AbortSignal) => Asyn
|
|
|
1009
1009
|
/** Configuration options for creating a local (origin-side) RpcStream. */
|
|
1010
1010
|
interface RpcStreamOptions<T> {
|
|
1011
1011
|
ackPeriod?: number;
|
|
1012
|
-
|
|
1012
|
+
bufferSize?: number;
|
|
1013
1013
|
allowReconnect?: boolean;
|
|
1014
1014
|
isRealTime?: boolean;
|
|
1015
1015
|
canSkipTo?: (item: T) => boolean;
|
|
1016
1016
|
}
|
|
1017
1017
|
/**
|
|
1018
|
-
* Parses a stream reference string of the form "{hostId},{localId},{ackPeriod},{
|
|
1018
|
+
* Parses a stream reference string of the form "{hostId},{localId},{ackPeriod},{bufferSize}".
|
|
1019
1019
|
* Returns null if the value is not a valid stream reference.
|
|
1020
1020
|
*/
|
|
1021
1021
|
declare function parseStreamRef(value: unknown): RpcStreamRef | null;
|
|
@@ -1047,7 +1047,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1047
1047
|
readonly isRealTime: boolean;
|
|
1048
1048
|
readonly canSkipTo: (item: T) => boolean;
|
|
1049
1049
|
readonly ackPeriod: number;
|
|
1050
|
-
readonly
|
|
1050
|
+
readonly bufferSize: number;
|
|
1051
1051
|
readonly localSource?: RpcStreamSource<T>;
|
|
1052
1052
|
readonly peer: RpcPeer;
|
|
1053
1053
|
private _sender?;
|
|
@@ -1078,7 +1078,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1078
1078
|
* stream reference.
|
|
1079
1079
|
*
|
|
1080
1080
|
* For binary peers the ref is a MessagePack-compatible object
|
|
1081
|
-
* (`{ SerializedId, AckPeriod,
|
|
1081
|
+
* (`{ SerializedId, AckPeriod, BufferSize, AllowReconnect }`);
|
|
1082
1082
|
* for text peers it is a comma-separated string.
|
|
1083
1083
|
*
|
|
1084
1084
|
* Can only be called once per stream.
|
|
@@ -1104,7 +1104,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
1104
1104
|
* Recursively walks a deserialized value and replaces any stream ref strings
|
|
1105
1105
|
* with live RpcStream<unknown> instances registered on the given peer.
|
|
1106
1106
|
* This handles nested stream refs inside complex return types (e.g. Table<T>
|
|
1107
|
-
* containing RpcStream<Row<T>> fields serialized as "{hostId},{localId},{ackPeriod},{
|
|
1107
|
+
* containing RpcStream<Row<T>> fields serialized as "{hostId},{localId},{ackPeriod},{bufferSize}").
|
|
1108
1108
|
*/
|
|
1109
1109
|
declare function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown;
|
|
1110
1110
|
|
|
@@ -1123,13 +1123,12 @@ declare function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown;
|
|
|
1123
1123
|
* The pump is ACK-driven: the main loop blocks on `_whenAckReady` until a
|
|
1124
1124
|
* client ACK is queued, processes it (updating `_lastAckedIndex`, rewinding
|
|
1125
1125
|
* `_index` on mustReset, trimming the replay buffer), then sends items up
|
|
1126
|
-
* to `ack.nextIndex +
|
|
1126
|
+
* to `ack.nextIndex + bufferSize`. While the peer is disconnected no ACKs
|
|
1127
1127
|
* arrive, so the source is never pulled — the sender naturally pauses
|
|
1128
1128
|
* instead of spinning.
|
|
1129
1129
|
*
|
|
1130
1130
|
* Direct port of .NET `RpcSharedStream<T>` at
|
|
1131
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs
|
|
1132
|
-
* separate non-real-time / real-time pump variants.
|
|
1131
|
+
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1133
1132
|
*/
|
|
1134
1133
|
declare class RpcStreamSender<T> implements IRpcObject {
|
|
1135
1134
|
private static readonly _emptyGuid;
|
|
@@ -1140,7 +1139,7 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1140
1139
|
readonly canSkipTo: (item: T) => boolean;
|
|
1141
1140
|
readonly peer: RpcPeer;
|
|
1142
1141
|
readonly ackPeriod: number;
|
|
1143
|
-
readonly
|
|
1142
|
+
readonly bufferSize: number;
|
|
1144
1143
|
/**
|
|
1145
1144
|
* If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`
|
|
1146
1145
|
* and may honor the AbortSignal to exit gracefully. `disconnect()` first aborts
|
|
@@ -1161,7 +1160,7 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1161
1160
|
private _iterator;
|
|
1162
1161
|
private _acks;
|
|
1163
1162
|
private _whenAckReady;
|
|
1164
|
-
constructor(peer: RpcPeer, ackPeriod?: number,
|
|
1163
|
+
constructor(peer: RpcPeer, ackPeriod?: number, bufferSize?: number, allowReconnect?: boolean, isRealTime?: boolean, canSkipTo?: (item: T) => boolean, sourceUsesAbortSignal?: boolean);
|
|
1165
1164
|
/** AbortSignal that is aborted when the sender is disconnected. */
|
|
1166
1165
|
get abortSignal(): AbortSignal;
|
|
1167
1166
|
/** Returns the stream reference string for the $sys.Ok response. */
|
|
@@ -1192,25 +1191,16 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
1192
1191
|
/**
|
|
1193
1192
|
* Consume an AsyncIterable and send all items to the client.
|
|
1194
1193
|
*
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:105-108.
|
|
1194
|
+
* Matches .NET `RpcSharedStream<T>.OnRun` at
|
|
1195
|
+
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1198
1196
|
*/
|
|
1199
1197
|
writeFrom(source: AsyncIterable<T>): Promise<void>;
|
|
1200
1198
|
/**
|
|
1201
|
-
* Non-real-time
|
|
1202
|
-
*
|
|
1203
|
-
*
|
|
1204
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:110-217.
|
|
1205
|
-
*/
|
|
1206
|
-
private _runNonRealTime;
|
|
1207
|
-
/**
|
|
1208
|
-
* Real-time pump — drops stale items to stay close to the source's head.
|
|
1209
|
-
*
|
|
1210
|
-
* Direct port of .NET `OnRunRealTime` at
|
|
1211
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:219-420.
|
|
1199
|
+
* ACK-driven pump. Non-real-time streams apply normal backpressure at
|
|
1200
|
+
* `bufferSize`; real-time streams may compact the already-buffered unsent
|
|
1201
|
+
* suffix to the latest buffered `canSkipTo` item after the consumer ACKs.
|
|
1212
1202
|
*/
|
|
1213
|
-
private
|
|
1203
|
+
private _run;
|
|
1214
1204
|
/** Drain all queued ACKs, returning the most recent one (or null if none). */
|
|
1215
1205
|
private _tryProcessAcks;
|
|
1216
1206
|
/** Wait for the ACK queue to have at least one entry (or `_ended`). */
|
package/dist/index.js
CHANGED
|
@@ -870,7 +870,7 @@ var RpcObjectKind = { Local: 0, Remote: 1 };
|
|
|
870
870
|
import { PromiseSource as PromiseSource3, RingBuffer } from "@actuallab/core";
|
|
871
871
|
var { warnLog: warnLog3 } = getLogs("RpcSharedStream");
|
|
872
872
|
var DEFAULT_ACK_PERIOD = 256;
|
|
873
|
-
var
|
|
873
|
+
var DEFAULT_BUFFER_SIZE = 128;
|
|
874
874
|
var RpcStreamSender = class _RpcStreamSender {
|
|
875
875
|
static _emptyGuid = "00000000-0000-0000-0000-000000000000";
|
|
876
876
|
id;
|
|
@@ -880,7 +880,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
880
880
|
canSkipTo;
|
|
881
881
|
peer;
|
|
882
882
|
ackPeriod;
|
|
883
|
-
|
|
883
|
+
bufferSize;
|
|
884
884
|
/**
|
|
885
885
|
* If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`
|
|
886
886
|
* and may honor the AbortSignal to exit gracefully. `disconnect()` first aborts
|
|
@@ -903,7 +903,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
903
903
|
// -- ACK queue (analog of .NET's Channel<(long, bool)>) --
|
|
904
904
|
_acks = [];
|
|
905
905
|
_whenAckReady = null;
|
|
906
|
-
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD,
|
|
906
|
+
constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, bufferSize = DEFAULT_BUFFER_SIZE, allowReconnect = true, isRealTime = false, canSkipTo = () => true, sourceUsesAbortSignal = false) {
|
|
907
907
|
const localId = peer.sharedObjects.nextId();
|
|
908
908
|
this.id = { hostId: peer.hub.hubId, localId };
|
|
909
909
|
this.allowReconnect = allowReconnect;
|
|
@@ -911,7 +911,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
911
911
|
this.canSkipTo = canSkipTo;
|
|
912
912
|
this.peer = peer;
|
|
913
913
|
this.ackPeriod = ackPeriod;
|
|
914
|
-
this.
|
|
914
|
+
this.bufferSize = bufferSize;
|
|
915
915
|
this.sourceUsesAbortSignal = sourceUsesAbortSignal;
|
|
916
916
|
}
|
|
917
917
|
/** AbortSignal that is aborted when the sender is disconnected. */
|
|
@@ -920,7 +920,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
920
920
|
}
|
|
921
921
|
/** Returns the stream reference string for the $sys.Ok response. */
|
|
922
922
|
toRef() {
|
|
923
|
-
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.
|
|
923
|
+
return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.bufferSize},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
|
|
924
924
|
}
|
|
925
925
|
/**
|
|
926
926
|
* Called by system call handler when $sys.Ack is received from the client.
|
|
@@ -997,9 +997,8 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
997
997
|
/**
|
|
998
998
|
* Consume an AsyncIterable and send all items to the client.
|
|
999
999
|
*
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1002
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:105-108.
|
|
1000
|
+
* Matches .NET `RpcSharedStream<T>.OnRun` at
|
|
1001
|
+
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.
|
|
1003
1002
|
*/
|
|
1004
1003
|
async writeFrom(source) {
|
|
1005
1004
|
await this._started.promise;
|
|
@@ -1008,11 +1007,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1008
1007
|
this._iterator = iterator;
|
|
1009
1008
|
const state = { iteratorDone: false };
|
|
1010
1009
|
try {
|
|
1011
|
-
|
|
1012
|
-
await this._runRealTime(iterator, state);
|
|
1013
|
-
} else {
|
|
1014
|
-
await this._runNonRealTime(iterator, state);
|
|
1015
|
-
}
|
|
1010
|
+
await this._run(iterator, state);
|
|
1016
1011
|
} finally {
|
|
1017
1012
|
if (state.iteratorDone && this._iterator === iterator) {
|
|
1018
1013
|
this._iterator = null;
|
|
@@ -1020,90 +1015,59 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1020
1015
|
}
|
|
1021
1016
|
}
|
|
1022
1017
|
/**
|
|
1023
|
-
* Non-real-time
|
|
1024
|
-
*
|
|
1025
|
-
*
|
|
1026
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:110-217.
|
|
1018
|
+
* ACK-driven pump. Non-real-time streams apply normal backpressure at
|
|
1019
|
+
* `bufferSize`; real-time streams may compact the already-buffered unsent
|
|
1020
|
+
* suffix to the latest buffered `canSkipTo` item after the consumer ACKs.
|
|
1027
1021
|
*/
|
|
1028
1022
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition -- _ended changes across awaits */
|
|
1029
|
-
async
|
|
1030
|
-
const buffer = new RingBuffer(this.
|
|
1023
|
+
async _run(iterator, state) {
|
|
1024
|
+
const buffer = new RingBuffer(this.bufferSize + 1);
|
|
1025
|
+
const isRealTime = this.isRealTime;
|
|
1031
1026
|
let bufferStart = 0;
|
|
1032
1027
|
let isFullyBuffered = false;
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
if (
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
while (this._nextIndex < maxIndex) {
|
|
1058
|
-
if (this._ended) return;
|
|
1059
|
-
if (bufferIndex < buffer.count) {
|
|
1060
|
-
const item = buffer.get(bufferIndex++);
|
|
1061
|
-
if (item.kind === "value") {
|
|
1062
|
-
this.sendItem(item.value);
|
|
1063
|
-
} else if (item.kind === "end") {
|
|
1064
|
-
if (!this._ended) this.sendEnd();
|
|
1065
|
-
return;
|
|
1066
|
-
} else {
|
|
1067
|
-
if (!this._ended) this.sendEnd(item.error);
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
continue;
|
|
1071
|
-
}
|
|
1072
|
-
if (isFullyBuffered) break;
|
|
1073
|
-
if (!buffer.hasRemainingCapacity) break;
|
|
1074
|
-
let pulled;
|
|
1075
|
-
try {
|
|
1076
|
-
const r = await iterator.next();
|
|
1077
|
-
if (this._ended) return;
|
|
1078
|
-
if (r.done) {
|
|
1079
|
-
pulled = _endItem;
|
|
1080
|
-
isFullyBuffered = true;
|
|
1081
|
-
state.iteratorDone = true;
|
|
1082
|
-
} else {
|
|
1083
|
-
pulled = { kind: "value", value: r.value };
|
|
1084
|
-
}
|
|
1085
|
-
} catch (e) {
|
|
1086
|
-
state.iteratorDone = true;
|
|
1028
|
+
let whenMovedNext = null;
|
|
1029
|
+
const pending = /* @__PURE__ */ Symbol("pending");
|
|
1030
|
+
const readNext = async () => {
|
|
1031
|
+
const next = whenMovedNext ?? iterator.next();
|
|
1032
|
+
whenMovedNext = null;
|
|
1033
|
+
return await next;
|
|
1034
|
+
};
|
|
1035
|
+
const tryReadReady = async () => {
|
|
1036
|
+
whenMovedNext ??= iterator.next();
|
|
1037
|
+
return await Promise.race([
|
|
1038
|
+
whenMovedNext,
|
|
1039
|
+
Promise.resolve(pending)
|
|
1040
|
+
]);
|
|
1041
|
+
};
|
|
1042
|
+
const bufferNext = async (onlyIfReady, prefetchNext) => {
|
|
1043
|
+
let r;
|
|
1044
|
+
try {
|
|
1045
|
+
r = onlyIfReady ? await tryReadReady() : await readNext();
|
|
1046
|
+
if (r === pending)
|
|
1047
|
+
return false;
|
|
1048
|
+
whenMovedNext = null;
|
|
1049
|
+
if (this._ended) return false;
|
|
1050
|
+
if (r.done) {
|
|
1051
|
+
buffer.pushTail(_endItem);
|
|
1087
1052
|
isFullyBuffered = true;
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1053
|
+
state.iteratorDone = true;
|
|
1054
|
+
} else {
|
|
1055
|
+
buffer.pushTail({ kind: "value", value: r.value });
|
|
1056
|
+
if (prefetchNext)
|
|
1057
|
+
whenMovedNext = iterator.next();
|
|
1092
1058
|
}
|
|
1093
|
-
|
|
1059
|
+
return true;
|
|
1060
|
+
} catch (e) {
|
|
1061
|
+
whenMovedNext = null;
|
|
1062
|
+
state.iteratorDone = true;
|
|
1063
|
+
isFullyBuffered = true;
|
|
1064
|
+
buffer.pushTail({
|
|
1065
|
+
kind: "error",
|
|
1066
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
1067
|
+
});
|
|
1068
|
+
return true;
|
|
1094
1069
|
}
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Real-time pump — drops stale items to stay close to the source's head.
|
|
1099
|
-
*
|
|
1100
|
-
* Direct port of .NET `OnRunRealTime` at
|
|
1101
|
-
* src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:219-420.
|
|
1102
|
-
*/
|
|
1103
|
-
async _runRealTime(iterator, state) {
|
|
1104
|
-
const buffer = new RingBuffer(this.ackAdvance + 1);
|
|
1105
|
-
let bufferStart = 0;
|
|
1106
|
-
let isFullyBuffered = false;
|
|
1070
|
+
};
|
|
1107
1071
|
while (true) {
|
|
1108
1072
|
let ack = this._tryProcessAcks();
|
|
1109
1073
|
if (!ack) {
|
|
@@ -1116,7 +1080,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1116
1080
|
}
|
|
1117
1081
|
}
|
|
1118
1082
|
this._lastAckedIndex = ack.nextIndex;
|
|
1119
|
-
if (ack.mustReset && !isFullyBuffered) {
|
|
1083
|
+
if (isRealTime && ack.mustReset && !isFullyBuffered) {
|
|
1120
1084
|
buffer.clear();
|
|
1121
1085
|
bufferStart = this._nextIndex;
|
|
1122
1086
|
while (true) {
|
|
@@ -1124,7 +1088,7 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1124
1088
|
let item;
|
|
1125
1089
|
let accepted = false;
|
|
1126
1090
|
try {
|
|
1127
|
-
const r = await
|
|
1091
|
+
const r = await readNext();
|
|
1128
1092
|
if (this._ended) return;
|
|
1129
1093
|
if (r.done) {
|
|
1130
1094
|
item = _endItem;
|
|
@@ -1159,11 +1123,22 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1159
1123
|
return;
|
|
1160
1124
|
}
|
|
1161
1125
|
let bufferIndex = this._nextIndex - bufferStart;
|
|
1162
|
-
const maxIndex = ack.nextIndex + this.
|
|
1126
|
+
const maxIndex = ack.nextIndex + this.bufferSize;
|
|
1163
1127
|
while (this._nextIndex < maxIndex) {
|
|
1164
1128
|
if (this._ended) return;
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1129
|
+
if (isRealTime) {
|
|
1130
|
+
while (buffer.hasRemainingCapacity && !isFullyBuffered && this._acks.length === 0) {
|
|
1131
|
+
if (!await bufferNext(true, true))
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
if (bufferIndex >= buffer.count && !isFullyBuffered && buffer.hasRemainingCapacity && this._acks.length === 0) {
|
|
1135
|
+
await bufferNext(false, true);
|
|
1136
|
+
}
|
|
1137
|
+
if (ack.nextIndex > 0)
|
|
1138
|
+
bufferIndex = _compactBufferedUnsentSuffix(buffer, bufferIndex, this.canSkipTo);
|
|
1139
|
+
if (this._acks.length > 0) break;
|
|
1140
|
+
}
|
|
1141
|
+
while (this._nextIndex < maxIndex && bufferIndex < buffer.count) {
|
|
1167
1142
|
const item = buffer.get(bufferIndex++);
|
|
1168
1143
|
if (item.kind === "value") {
|
|
1169
1144
|
this.sendItem(item.value);
|
|
@@ -1174,73 +1149,13 @@ var RpcStreamSender = class _RpcStreamSender {
|
|
|
1174
1149
|
if (!this._ended) this.sendEnd(item.error);
|
|
1175
1150
|
return;
|
|
1176
1151
|
}
|
|
1177
|
-
continue;
|
|
1178
1152
|
}
|
|
1153
|
+
if (this._nextIndex >= maxIndex)
|
|
1154
|
+
break;
|
|
1179
1155
|
if (isFullyBuffered) break;
|
|
1180
1156
|
if (!buffer.hasRemainingCapacity) break;
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
const r = await iterator.next();
|
|
1184
|
-
if (this._ended) return;
|
|
1185
|
-
if (r.done) {
|
|
1186
|
-
pulled = _endItem;
|
|
1187
|
-
isFullyBuffered = true;
|
|
1188
|
-
state.iteratorDone = true;
|
|
1189
|
-
} else {
|
|
1190
|
-
pulled = { kind: "value", value: r.value };
|
|
1191
|
-
}
|
|
1192
|
-
} catch (e) {
|
|
1193
|
-
state.iteratorDone = true;
|
|
1194
|
-
isFullyBuffered = true;
|
|
1195
|
-
pulled = {
|
|
1196
|
-
kind: "error",
|
|
1197
|
-
error: e instanceof Error ? e : new Error(String(e))
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
buffer.pushTail(pulled);
|
|
1201
|
-
}
|
|
1202
|
-
if (this._nextIndex >= maxIndex && !isFullyBuffered && !this._ended) {
|
|
1203
|
-
let latestValue;
|
|
1204
|
-
let haveLatest = false;
|
|
1205
|
-
let exitReason = "ack";
|
|
1206
|
-
let exitError = null;
|
|
1207
|
-
drain: while (!this._ended) {
|
|
1208
|
-
if (this._acks.length > 0) {
|
|
1209
|
-
exitReason = "ack";
|
|
1210
|
-
break drain;
|
|
1211
|
-
}
|
|
1212
|
-
let r;
|
|
1213
|
-
try {
|
|
1214
|
-
r = await iterator.next();
|
|
1215
|
-
} catch (e) {
|
|
1216
|
-
state.iteratorDone = true;
|
|
1217
|
-
isFullyBuffered = true;
|
|
1218
|
-
exitReason = "error";
|
|
1219
|
-
exitError = e instanceof Error ? e : new Error(String(e));
|
|
1220
|
-
break drain;
|
|
1221
|
-
}
|
|
1222
|
-
if (this._ended) return;
|
|
1223
|
-
if (r.done) {
|
|
1224
|
-
state.iteratorDone = true;
|
|
1225
|
-
isFullyBuffered = true;
|
|
1226
|
-
exitReason = "end";
|
|
1227
|
-
break drain;
|
|
1228
|
-
}
|
|
1229
|
-
if (this.canSkipTo(r.value)) {
|
|
1230
|
-
latestValue = r.value;
|
|
1231
|
-
haveLatest = true;
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
if (exitReason === "end" || exitReason === "error") {
|
|
1235
|
-
if (haveLatest && !this._ended) this.sendItem(latestValue);
|
|
1236
|
-
if (!this._ended) this.sendEnd(exitError);
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
if (haveLatest) {
|
|
1240
|
-
const wasFull = buffer.isFull;
|
|
1241
|
-
buffer.pushTailAndMoveHeadIfFull({ kind: "value", value: latestValue });
|
|
1242
|
-
if (wasFull) bufferStart++;
|
|
1243
|
-
}
|
|
1157
|
+
if (!isRealTime)
|
|
1158
|
+
await bufferNext(false, false);
|
|
1244
1159
|
}
|
|
1245
1160
|
}
|
|
1246
1161
|
}
|
|
@@ -1307,11 +1222,28 @@ var _endItem = { kind: "end" };
|
|
|
1307
1222
|
function _clamp(value, min, max) {
|
|
1308
1223
|
return value < min ? min : value > max ? max : value;
|
|
1309
1224
|
}
|
|
1225
|
+
function _compactBufferedUnsentSuffix(buffer, firstUnsentIndex, canSkipTo) {
|
|
1226
|
+
let skipToIndex = -1;
|
|
1227
|
+
for (let i = firstUnsentIndex; i < buffer.count; i++) {
|
|
1228
|
+
const item = buffer.get(i);
|
|
1229
|
+
if (item.kind === "value" && canSkipTo(item.value))
|
|
1230
|
+
skipToIndex = i;
|
|
1231
|
+
}
|
|
1232
|
+
if (skipToIndex <= firstUnsentIndex)
|
|
1233
|
+
return firstUnsentIndex;
|
|
1234
|
+
const items = buffer.toArray();
|
|
1235
|
+
buffer.clear();
|
|
1236
|
+
for (let i = 0; i < firstUnsentIndex; i++)
|
|
1237
|
+
buffer.pushTail(items[i]);
|
|
1238
|
+
for (let i = skipToIndex; i < items.length; i++)
|
|
1239
|
+
buffer.pushTail(items[i]);
|
|
1240
|
+
return firstUnsentIndex;
|
|
1241
|
+
}
|
|
1310
1242
|
|
|
1311
1243
|
// src/rpc-stream.ts
|
|
1312
1244
|
var { warnLog: warnLog4 } = getLogs("RpcStream");
|
|
1313
1245
|
var DEFAULT_ACK_PERIOD2 = 30;
|
|
1314
|
-
var
|
|
1246
|
+
var DEFAULT_BUFFER_SIZE2 = 61;
|
|
1315
1247
|
function parseStreamRef(value) {
|
|
1316
1248
|
if (typeof value === "string") {
|
|
1317
1249
|
const parts = value.split(",");
|
|
@@ -1319,12 +1251,12 @@ function parseStreamRef(value) {
|
|
|
1319
1251
|
const hostId = parts[0];
|
|
1320
1252
|
const localId = parseInt(parts[1], 10);
|
|
1321
1253
|
const ackPeriod = parseInt(parts[2], 10);
|
|
1322
|
-
const
|
|
1323
|
-
if (isNaN(localId) || isNaN(ackPeriod) || isNaN(
|
|
1254
|
+
const bufferSize = parseInt(parts[3], 10);
|
|
1255
|
+
if (isNaN(localId) || isNaN(ackPeriod) || isNaN(bufferSize))
|
|
1324
1256
|
return null;
|
|
1325
1257
|
const allowReconnect = parts.length < 5 || parts[4] !== "0";
|
|
1326
1258
|
const isRealTime = parts.length >= 6 && parts[5] === "1";
|
|
1327
|
-
return { hostId, localId, ackPeriod,
|
|
1259
|
+
return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
|
|
1328
1260
|
}
|
|
1329
1261
|
if (typeof value === "object" && value !== null) {
|
|
1330
1262
|
const obj = value;
|
|
@@ -1335,10 +1267,10 @@ function parseStreamRef(value) {
|
|
|
1335
1267
|
const hostId = String(rawHostId);
|
|
1336
1268
|
const localId = Number(serializedId[1]);
|
|
1337
1269
|
const ackPeriod = Number(obj.AckPeriod ?? 256);
|
|
1338
|
-
const
|
|
1270
|
+
const bufferSize = Number(obj.BufferSize ?? 128);
|
|
1339
1271
|
const allowReconnect = obj.AllowReconnect !== false;
|
|
1340
1272
|
const isRealTime = obj.IsRealTime === true;
|
|
1341
|
-
return { hostId, localId, ackPeriod,
|
|
1273
|
+
return { hostId, localId, ackPeriod, bufferSize, allowReconnect, isRealTime };
|
|
1342
1274
|
}
|
|
1343
1275
|
return null;
|
|
1344
1276
|
}
|
|
@@ -1358,7 +1290,7 @@ var RpcStream = class _RpcStream {
|
|
|
1358
1290
|
isRealTime;
|
|
1359
1291
|
canSkipTo;
|
|
1360
1292
|
ackPeriod;
|
|
1361
|
-
|
|
1293
|
+
bufferSize;
|
|
1362
1294
|
// Local (origin) mode
|
|
1363
1295
|
localSource;
|
|
1364
1296
|
// Remote (target) mode
|
|
@@ -1388,7 +1320,7 @@ var RpcStream = class _RpcStream {
|
|
|
1388
1320
|
this.isRealTime = options.isRealTime ?? false;
|
|
1389
1321
|
this.canSkipTo = options.canSkipTo ?? (() => true);
|
|
1390
1322
|
this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD2;
|
|
1391
|
-
this.
|
|
1323
|
+
this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE2;
|
|
1392
1324
|
} else {
|
|
1393
1325
|
const ref = sourceOrRef;
|
|
1394
1326
|
const peer = optionsOrPeer;
|
|
@@ -1399,7 +1331,7 @@ var RpcStream = class _RpcStream {
|
|
|
1399
1331
|
this.isRealTime = ref.isRealTime;
|
|
1400
1332
|
this.canSkipTo = () => true;
|
|
1401
1333
|
this.ackPeriod = ref.ackPeriod;
|
|
1402
|
-
this.
|
|
1334
|
+
this.bufferSize = ref.bufferSize;
|
|
1403
1335
|
}
|
|
1404
1336
|
}
|
|
1405
1337
|
/**
|
|
@@ -1417,7 +1349,7 @@ var RpcStream = class _RpcStream {
|
|
|
1417
1349
|
* stream reference.
|
|
1418
1350
|
*
|
|
1419
1351
|
* For binary peers the ref is a MessagePack-compatible object
|
|
1420
|
-
* (`{ SerializedId, AckPeriod,
|
|
1352
|
+
* (`{ SerializedId, AckPeriod, BufferSize, AllowReconnect }`);
|
|
1421
1353
|
* for text peers it is a comma-separated string.
|
|
1422
1354
|
*
|
|
1423
1355
|
* Can only be called once per stream.
|
|
@@ -1434,7 +1366,7 @@ var RpcStream = class _RpcStream {
|
|
|
1434
1366
|
const sender = new RpcStreamSender(
|
|
1435
1367
|
peer,
|
|
1436
1368
|
this.ackPeriod,
|
|
1437
|
-
this.
|
|
1369
|
+
this.bufferSize,
|
|
1438
1370
|
this.allowReconnect,
|
|
1439
1371
|
this.isRealTime,
|
|
1440
1372
|
this.canSkipTo,
|
|
@@ -1447,7 +1379,7 @@ var RpcStream = class _RpcStream {
|
|
|
1447
1379
|
this._toRefValue = peer.serializationFormat.isBinary ? {
|
|
1448
1380
|
SerializedId: [sender.id.hostId, sender.id.localId],
|
|
1449
1381
|
AckPeriod: sender.ackPeriod,
|
|
1450
|
-
|
|
1382
|
+
BufferSize: sender.bufferSize,
|
|
1451
1383
|
AllowReconnect: sender.allowReconnect
|
|
1452
1384
|
} : sender.toRef();
|
|
1453
1385
|
return this._toRefValue;
|