@actuallab/rpc 12.1.100 → 12.2.21
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 +1603 -170
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +348 -25
- package/dist/index.d.ts +348 -25
- package/dist/index.js +1576 -169
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Encoder, Decoder } from '@msgpack/msgpack';
|
|
1
2
|
import { EventHandlerSet, PromiseSource, RetryDelayer } from '@actuallab/core';
|
|
2
3
|
|
|
3
4
|
/** Identifies the type of an RPC call on the wire. */
|
|
@@ -30,6 +31,29 @@ declare const ENVELOPE_DELIMITER = "\n";
|
|
|
30
31
|
declare const ARG_DELIMITER = "\u001F";
|
|
31
32
|
declare const FRAME_DELIMITER = "\n\u001E";
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Registry mapping RPC method wire names to/from their 4-byte XXH3 hashes.
|
|
36
|
+
* Hashes match .NET's RpcMethodRef.ComputeHashCode.
|
|
37
|
+
* Hashes are computed lazily on first access and cached.
|
|
38
|
+
*/
|
|
39
|
+
declare class RpcMethodRegistry {
|
|
40
|
+
private _nameToHash;
|
|
41
|
+
private _hashToName;
|
|
42
|
+
/** Register a method name and compute its hash. */
|
|
43
|
+
register(methodName: string): void;
|
|
44
|
+
/** Register all wire method names from a service definition. */
|
|
45
|
+
registerService(serviceName: string, methods: ReadonlyMap<string, {
|
|
46
|
+
name: string;
|
|
47
|
+
wireArgCount: number;
|
|
48
|
+
}>): void;
|
|
49
|
+
/** Get the hash for a method name. Computes on demand if not registered. */
|
|
50
|
+
getHash(methodName: string): number;
|
|
51
|
+
/** Get the method name for a hash. Returns undefined if not registered. */
|
|
52
|
+
getName(hash: number): string | undefined;
|
|
53
|
+
/** Get the hash for a method name, throwing if not registered. */
|
|
54
|
+
requireHash(methodName: string): number;
|
|
55
|
+
}
|
|
56
|
+
|
|
33
57
|
/** Serializes an RpcMessage + args into the json5 wire format. */
|
|
34
58
|
declare function serializeMessage(message: RpcMessage, args?: unknown[]): string;
|
|
35
59
|
/** Serializes multiple messages into a single WebSocket frame. */
|
|
@@ -41,11 +65,189 @@ declare function deserializeMessage(raw: string): {
|
|
|
41
65
|
message: RpcMessage;
|
|
42
66
|
args: unknown[];
|
|
43
67
|
};
|
|
68
|
+
declare const defaultBinaryEncoder: Encoder<undefined>;
|
|
69
|
+
declare const defaultBinaryDecoder: Decoder<undefined>;
|
|
70
|
+
/** Exposed so connection code can instantiate a matching Encoder
|
|
71
|
+
* with the same initial buffer size without needing to reach into
|
|
72
|
+
* this module's private constants. */
|
|
73
|
+
declare function createBinaryEncoder(): Encoder;
|
|
74
|
+
/**
|
|
75
|
+
* Serializes an RpcMessage + args into V5 binary format (msgpack6np).
|
|
76
|
+
*
|
|
77
|
+
* Layout (NO frame-level size prefix; V5 with PersistsMessageSize=false):
|
|
78
|
+
* envelope + argData
|
|
79
|
+
* Envelope:
|
|
80
|
+
* [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]
|
|
81
|
+
* [LVarSpan methodRef] [4-byte LE argLen]
|
|
82
|
+
* ArgData: concatenated MessagePack-encoded arguments
|
|
83
|
+
*
|
|
84
|
+
* Single-allocation build: pre-encodes args with the supplied reusable
|
|
85
|
+
* `Encoder`, computes the total envelope size, then writes everything
|
|
86
|
+
* into one `Uint8Array`. Eliminates the previous build's intermediate
|
|
87
|
+
* `headerParts` number array + four `Uint8Array` concats per call,
|
|
88
|
+
* which showed up as ~2% self time in the 300-pull profile and
|
|
89
|
+
* pulled the msgpack `resizeBuffer` + `Encoder` constructor along
|
|
90
|
+
* with it for another ~6%.
|
|
91
|
+
*
|
|
92
|
+
* NOTE: `@msgpack/msgpack` `Encoder.encode()` already returns a fresh
|
|
93
|
+
* copy of the internal buffer (there's a separate `encodeSharedRef()`
|
|
94
|
+
* for zero-copy access), so we can push the results straight into
|
|
95
|
+
* `argBufs` without an extra slice. The benefit of reusing the Encoder
|
|
96
|
+
* is that its internal write buffer stays at its high-water mark —
|
|
97
|
+
* once the first ~11 KB frame has grown it to the full size, no
|
|
98
|
+
* subsequent `resizeBuffer` calls are needed.
|
|
99
|
+
*/
|
|
100
|
+
declare function serializeBinaryMessage(message: RpcMessage, args?: unknown[], encoder?: Encoder): Uint8Array;
|
|
101
|
+
/**
|
|
102
|
+
* Deserializes a single V5 binary message starting at `offset`.
|
|
103
|
+
* Returns parsed message, args, and number of bytes consumed.
|
|
104
|
+
*
|
|
105
|
+
* Accepts an optional `Decoder` for reuse across calls on the same
|
|
106
|
+
* connection — avoids constructing a new `@msgpack/msgpack.Decoder`
|
|
107
|
+
* (and its internal resolver/table state) on every inbound message.
|
|
108
|
+
*/
|
|
109
|
+
declare function deserializeBinaryMessage(data: Uint8Array, offset: number, decoder?: Decoder): {
|
|
110
|
+
message: RpcMessage;
|
|
111
|
+
args: unknown[];
|
|
112
|
+
bytesRead: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Splits a binary WebSocket frame into individual deserialized messages.
|
|
116
|
+
* V5 envelopes are self-delimiting (header + method + argLen + argData), so
|
|
117
|
+
* multiple envelopes concatenated in a single WebSocket frame can be decoded
|
|
118
|
+
* by iterating with the bytesRead returned from deserializeBinaryMessage.
|
|
119
|
+
*
|
|
120
|
+
* This must stay symmetric with serializeBinaryFrame() (which concatenates
|
|
121
|
+
* multiple envelopes). The .NET server batches outbound RPC messages into a
|
|
122
|
+
* single WebSocket frame via WebSocketChannel/WriteDelayer, so any assumption
|
|
123
|
+
* that a frame is a single envelope silently drops every trailing message.
|
|
124
|
+
*
|
|
125
|
+
* Accepts an optional `Decoder` for reuse across calls on the same
|
|
126
|
+
* connection — forwarded to `deserializeBinaryMessage`.
|
|
127
|
+
*/
|
|
128
|
+
declare function splitBinaryFrame(frame: Uint8Array, decoder?: Decoder): Array<{
|
|
129
|
+
message: RpcMessage;
|
|
130
|
+
args: unknown[];
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* Serializes multiple binary messages into a single WebSocket frame.
|
|
134
|
+
* Simply concatenates the already size-prefixed messages.
|
|
135
|
+
*/
|
|
136
|
+
declare function serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Serializes an RpcMessage + args into V5Compact binary format.
|
|
140
|
+
* Same as V5 except the method is encoded as a 4-byte LE hash
|
|
141
|
+
* instead of an LVar-prefixed UTF-8 string.
|
|
142
|
+
*
|
|
143
|
+
* Layout:
|
|
144
|
+
* [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]
|
|
145
|
+
* [4-byte LE methodHash] [4-byte LE argLen]
|
|
146
|
+
* [argData]
|
|
147
|
+
*/
|
|
148
|
+
declare function serializeCompactBinaryMessage(message: RpcMessage, args?: unknown[], registry?: RpcMethodRegistry, encoder?: Encoder): Uint8Array;
|
|
149
|
+
/**
|
|
150
|
+
* Deserializes a single V5Compact binary message starting at `offset`.
|
|
151
|
+
* Reads a 4-byte LE method hash and resolves it via the registry.
|
|
152
|
+
*/
|
|
153
|
+
declare function deserializeCompactBinaryMessage(data: Uint8Array, offset: number, registry?: RpcMethodRegistry, decoder?: Decoder): {
|
|
154
|
+
message: RpcMessage;
|
|
155
|
+
args: unknown[];
|
|
156
|
+
bytesRead: number;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Splits a compact binary frame into individual deserialized messages.
|
|
160
|
+
*/
|
|
161
|
+
declare function splitCompactBinaryFrame(frame: Uint8Array, registry?: RpcMethodRegistry, decoder?: Decoder): Array<{
|
|
162
|
+
message: RpcMessage;
|
|
163
|
+
args: unknown[];
|
|
164
|
+
}>;
|
|
165
|
+
|
|
166
|
+
/** Parsed inbound message — result of deserialization. */
|
|
167
|
+
interface RpcDeserializedMessage {
|
|
168
|
+
message: RpcMessage;
|
|
169
|
+
args: unknown[];
|
|
170
|
+
}
|
|
171
|
+
/** Wire data produced by serialization — either text or binary. */
|
|
172
|
+
type RpcWireData = string | Uint8Array;
|
|
173
|
+
/**
|
|
174
|
+
* Base class for RPC serialization formats.
|
|
175
|
+
* Instances are immutable singletons — one per format key.
|
|
176
|
+
* State (like method registries) is passed via parameters.
|
|
177
|
+
*/
|
|
178
|
+
declare abstract class RpcSerializationFormat {
|
|
179
|
+
abstract readonly key: string;
|
|
180
|
+
abstract readonly isBinary: boolean;
|
|
181
|
+
/** Whether this format uses compact method hashes (needs RpcMethodRegistry). */
|
|
182
|
+
abstract readonly isCompact: boolean;
|
|
183
|
+
/** Serialize an outbound message. */
|
|
184
|
+
abstract serializeMessage(message: RpcMessage, args?: unknown[], encoder?: Encoder, registry?: RpcMethodRegistry): RpcWireData;
|
|
185
|
+
/** Deserialize a single text message (only called for text formats). */
|
|
186
|
+
abstract deserializeTextMessage(raw: string): RpcDeserializedMessage;
|
|
187
|
+
/** Split + deserialize a binary frame (only called for binary formats). */
|
|
188
|
+
abstract splitBinaryFrame(frame: Uint8Array, decoder?: Decoder, registry?: RpcMethodRegistry): RpcDeserializedMessage[];
|
|
189
|
+
/** Concatenate multiple serialized binary messages into one frame. */
|
|
190
|
+
abstract serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;
|
|
191
|
+
/** Create a reusable binary encoder for this format. Returns undefined for text formats. */
|
|
192
|
+
createEncoder(): Encoder | undefined;
|
|
193
|
+
/** Create a reusable binary decoder for this format. Returns undefined for text formats. */
|
|
194
|
+
createDecoder(): Decoder | undefined;
|
|
195
|
+
private static _all;
|
|
196
|
+
static register(format: RpcSerializationFormat): void;
|
|
197
|
+
static get(key: string): RpcSerializationFormat;
|
|
198
|
+
static tryGet(key: string): RpcSerializationFormat | undefined;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* JSON text format — "json5np" (System.Text.Json, no polymorphism).
|
|
202
|
+
*/
|
|
203
|
+
declare class RpcJsonSerializationFormat extends RpcSerializationFormat {
|
|
204
|
+
readonly key: string;
|
|
205
|
+
readonly isBinary = false;
|
|
206
|
+
readonly isCompact = false;
|
|
207
|
+
constructor(key: string);
|
|
208
|
+
serializeMessage(message: RpcMessage, args?: unknown[]): string;
|
|
209
|
+
deserializeTextMessage(raw: string): RpcDeserializedMessage;
|
|
210
|
+
splitBinaryFrame(): RpcDeserializedMessage[];
|
|
211
|
+
serializeBinaryFrame(): Uint8Array;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* MessagePack binary format — "msgpack6" / "mempack6" (V5 wire format).
|
|
215
|
+
*/
|
|
216
|
+
declare class RpcMessagePackSerializationFormat extends RpcSerializationFormat {
|
|
217
|
+
readonly key: string;
|
|
218
|
+
readonly isBinary = true;
|
|
219
|
+
readonly isCompact = false;
|
|
220
|
+
constructor(key: string);
|
|
221
|
+
serializeMessage(message: RpcMessage, args?: unknown[], encoder?: Encoder): Uint8Array;
|
|
222
|
+
deserializeTextMessage(raw: string): RpcDeserializedMessage;
|
|
223
|
+
splitBinaryFrame(frame: Uint8Array, decoder?: Decoder): RpcDeserializedMessage[];
|
|
224
|
+
serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;
|
|
225
|
+
createEncoder(): Encoder;
|
|
226
|
+
createDecoder(): Decoder;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* MessagePack compact binary format — "msgpack6c" / "mempack6c" (V5Compact).
|
|
230
|
+
* Uses 4-byte method hash instead of full method name.
|
|
231
|
+
* Registry is passed via parameters — format instance is immutable.
|
|
232
|
+
*/
|
|
233
|
+
declare class RpcMessagePackCompactSerializationFormat extends RpcSerializationFormat {
|
|
234
|
+
readonly key: string;
|
|
235
|
+
readonly isBinary = true;
|
|
236
|
+
readonly isCompact = true;
|
|
237
|
+
constructor(key: string);
|
|
238
|
+
serializeMessage(message: RpcMessage, args?: unknown[], encoder?: Encoder, registry?: RpcMethodRegistry): Uint8Array;
|
|
239
|
+
deserializeTextMessage(raw: string): RpcDeserializedMessage;
|
|
240
|
+
splitBinaryFrame(frame: Uint8Array, decoder?: Decoder, registry?: RpcMethodRegistry): RpcDeserializedMessage[];
|
|
241
|
+
serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;
|
|
242
|
+
createEncoder(): Encoder;
|
|
243
|
+
createDecoder(): Decoder;
|
|
244
|
+
}
|
|
44
245
|
|
|
45
246
|
/** Abstract WebSocket interface — works with both browser WebSocket and Node.js ws. */
|
|
46
247
|
interface WebSocketLike {
|
|
47
248
|
readonly readyState: number;
|
|
48
|
-
|
|
249
|
+
binaryType?: string;
|
|
250
|
+
send(data: string | ArrayBufferLike | Uint8Array | ArrayBufferView): void;
|
|
49
251
|
close(code?: number, reason?: string): void;
|
|
50
252
|
onopen: ((ev: unknown) => void) | null;
|
|
51
253
|
onmessage: ((ev: {
|
|
@@ -63,35 +265,72 @@ declare const WebSocketState: {
|
|
|
63
265
|
readonly CLOSING: 2;
|
|
64
266
|
readonly CLOSED: 3;
|
|
65
267
|
};
|
|
268
|
+
/** Received message — either text (string) or binary (already parsed). */
|
|
269
|
+
type RpcReceivedMessage = {
|
|
270
|
+
kind: 'text';
|
|
271
|
+
raw: string;
|
|
272
|
+
} | {
|
|
273
|
+
kind: 'binary';
|
|
274
|
+
message: RpcMessage;
|
|
275
|
+
args: unknown[];
|
|
276
|
+
};
|
|
66
277
|
/** Abstract RPC connection — transport-agnostic interface for sending/receiving messages. */
|
|
67
278
|
interface RpcConnection {
|
|
68
279
|
readonly isOpen: boolean;
|
|
280
|
+
readonly binaryMode: boolean;
|
|
69
281
|
readonly whenConnected: Promise<void>;
|
|
70
|
-
readonly messageReceived: EventHandlerSet<
|
|
282
|
+
readonly messageReceived: EventHandlerSet<RpcReceivedMessage>;
|
|
71
283
|
readonly closed: EventHandlerSet<{
|
|
72
284
|
code: number;
|
|
73
285
|
reason: string;
|
|
74
286
|
}>;
|
|
287
|
+
/** Optional reusable msgpack encoder for outbound binary messages.
|
|
288
|
+
* Callers passing this to `serializeBinaryMessage` skip the per-call
|
|
289
|
+
* Encoder construction + resizeBuffer growth. Implementations that
|
|
290
|
+
* don't support binary serialization can leave it undefined. */
|
|
291
|
+
readonly encoder?: Encoder;
|
|
75
292
|
send(serializedMessage: string): void;
|
|
293
|
+
sendBinary(data: Uint8Array): void;
|
|
76
294
|
close(code?: number, reason?: string): void;
|
|
77
295
|
}
|
|
78
|
-
/** WebSocket-based RpcConnection — handles frame splitting and message queueing. */
|
|
296
|
+
/** WebSocket-based RpcConnection — handles frame splitting, binary/text modes, and message queueing. */
|
|
79
297
|
declare class RpcWebSocketConnection implements RpcConnection {
|
|
80
298
|
private _ws;
|
|
81
299
|
private _sendBuffer;
|
|
82
300
|
private _connected;
|
|
83
|
-
|
|
301
|
+
/** Per-connection msgpack encoder — reused across outbound messages to
|
|
302
|
+
* amortise construction cost and to keep the internal write buffer at
|
|
303
|
+
* its largest observed size (avoids repeated `resizeBuffer` growth on
|
|
304
|
+
* large video frames). See serializeBinaryMessage() for details. */
|
|
305
|
+
private _encoder;
|
|
306
|
+
/** Per-connection msgpack decoder — reused across inbound messages
|
|
307
|
+
* rather than reconstructing one for every `decodeMulti` call. */
|
|
308
|
+
private _decoder;
|
|
309
|
+
/** Exposed for callers who need to encode outbound binary messages
|
|
310
|
+
* through this connection (e.g. `serializeBinaryMessage`). Safe to
|
|
311
|
+
* pass to synchronous encode calls on the same event-loop turn. */
|
|
312
|
+
get encoder(): Encoder;
|
|
313
|
+
get decoder(): Decoder;
|
|
314
|
+
private _format;
|
|
315
|
+
private _methodRegistry;
|
|
316
|
+
readonly binaryMode: boolean;
|
|
317
|
+
readonly messageReceived: EventHandlerSet<RpcReceivedMessage>;
|
|
84
318
|
readonly closed: EventHandlerSet<{
|
|
85
319
|
code: number;
|
|
86
320
|
reason: string;
|
|
87
321
|
}>;
|
|
88
322
|
readonly error: EventHandlerSet<unknown>;
|
|
89
|
-
constructor(ws: WebSocketLike);
|
|
323
|
+
constructor(ws: WebSocketLike, binaryMode?: boolean, format?: RpcSerializationFormat, methodRegistry?: RpcMethodRegistry);
|
|
90
324
|
get isOpen(): boolean;
|
|
91
325
|
get whenConnected(): Promise<void>;
|
|
92
326
|
send(serializedMessage: string): void;
|
|
93
|
-
|
|
327
|
+
sendBinary(data: Uint8Array): void;
|
|
328
|
+
sendTextBatch(messages: string[]): void;
|
|
329
|
+
sendBinaryBatch(messages: Uint8Array[]): void;
|
|
330
|
+
/** Split a binary frame using the format (compact-aware) or default splitter. */
|
|
331
|
+
private _splitBinary;
|
|
94
332
|
close(code?: number, reason?: string): void;
|
|
333
|
+
private _sendRaw;
|
|
95
334
|
private _flush;
|
|
96
335
|
}
|
|
97
336
|
|
|
@@ -99,7 +338,8 @@ declare class RpcWebSocketConnection implements RpcConnection {
|
|
|
99
338
|
declare class RpcMessageChannelConnection implements RpcConnection {
|
|
100
339
|
private _port;
|
|
101
340
|
private _open;
|
|
102
|
-
readonly
|
|
341
|
+
readonly binaryMode = false;
|
|
342
|
+
readonly messageReceived: EventHandlerSet<RpcReceivedMessage>;
|
|
103
343
|
readonly closed: EventHandlerSet<{
|
|
104
344
|
code: number;
|
|
105
345
|
reason: string;
|
|
@@ -108,10 +348,14 @@ declare class RpcMessageChannelConnection implements RpcConnection {
|
|
|
108
348
|
constructor(port: MessagePort);
|
|
109
349
|
get isOpen(): boolean;
|
|
110
350
|
send(serializedMessage: string): void;
|
|
351
|
+
sendBinary(data: Uint8Array): void;
|
|
111
352
|
close(code?: number, reason?: string): void;
|
|
112
353
|
}
|
|
113
354
|
/** Creates a pair of connected RpcMessageChannelConnections for testing. */
|
|
114
|
-
declare function createMessageChannelPair(): [
|
|
355
|
+
declare function createMessageChannelPair(): [
|
|
356
|
+
RpcMessageChannelConnection,
|
|
357
|
+
RpcMessageChannelConnection
|
|
358
|
+
];
|
|
115
359
|
|
|
116
360
|
/** Describes a single method on an RPC service. */
|
|
117
361
|
interface RpcMethodDef {
|
|
@@ -153,8 +397,8 @@ declare class RpcOutboundCall {
|
|
|
153
397
|
/** Whether to remove this call from the tracker on $sys.Ok. Default: true.
|
|
154
398
|
* Subclasses (e.g. compute calls) override to false to stay in tracker for invalidation. */
|
|
155
399
|
readonly removeOnOk: boolean;
|
|
156
|
-
/** The serialized wire
|
|
157
|
-
|
|
400
|
+
/** The serialized wire data — stored for re-sending on reconnect. */
|
|
401
|
+
serializedWireData: string | Uint8Array;
|
|
158
402
|
constructor(callId: number, method: string);
|
|
159
403
|
/** Called when the connection is lost. Subclasses can override to resolve invalidation promises. */
|
|
160
404
|
onDisconnect(): void;
|
|
@@ -213,20 +457,49 @@ declare class RpcServiceHost {
|
|
|
213
457
|
getMethodDef(wireMethod: string): RpcMethodDef | undefined;
|
|
214
458
|
}
|
|
215
459
|
|
|
216
|
-
/**
|
|
460
|
+
/**
|
|
461
|
+
* Sends system RPC messages — like .NET's RpcSystemCallSender.
|
|
462
|
+
*
|
|
463
|
+
* Hot-path allocation notes: `item`, `batch`, `ack`, and `end` are called
|
|
464
|
+
* once per stream frame (or once per ack window). Naively building a fresh
|
|
465
|
+
* `{ Method, RelatedId }` envelope object plus a fresh two-element arg array
|
|
466
|
+
* on every call showed up as ~2-4% self time + non-trivial GC pressure in
|
|
467
|
+
* the 300-pull video-load-test profile.
|
|
468
|
+
*
|
|
469
|
+
* The sender itself is a per-Hub singleton and `_send`/format.serializeMessage
|
|
470
|
+
* are fully synchronous (no awaits, no re-entrancy on a single-threaded event
|
|
471
|
+
* loop), so we reuse a small pool of preallocated envelope + arg-array slots
|
|
472
|
+
* per hot method. Mutate fields just before the send call; the encoder reads
|
|
473
|
+
* them synchronously and produces a detached copy, after which the slot is
|
|
474
|
+
* free to be mutated again on the next call.
|
|
475
|
+
*/
|
|
217
476
|
declare class RpcSystemCallSender {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
477
|
+
private readonly _itemEnv;
|
|
478
|
+
private readonly _batchEnv;
|
|
479
|
+
private readonly _endEnv;
|
|
480
|
+
private readonly _ackEnv;
|
|
481
|
+
private readonly _ackEndEnv;
|
|
482
|
+
private readonly _itemArgs;
|
|
483
|
+
private readonly _batchArgs;
|
|
484
|
+
private readonly _endArgs;
|
|
485
|
+
private readonly _ackArgs;
|
|
486
|
+
private readonly _ackEndArgs;
|
|
487
|
+
private static readonly NO_ERROR;
|
|
488
|
+
registry?: RpcMethodRegistry;
|
|
489
|
+
private _send;
|
|
490
|
+
handshake(conn: RpcConnection, format: RpcSerializationFormat, peerId: string, hubId: string, index: number): void;
|
|
491
|
+
ok(conn: RpcConnection, format: RpcSerializationFormat, relatedId: number, result: unknown): void;
|
|
492
|
+
error(conn: RpcConnection, format: RpcSerializationFormat, relatedId: number, error: unknown): void;
|
|
493
|
+
cancel(conn: RpcConnection, format: RpcSerializationFormat, relatedId: number): void;
|
|
494
|
+
keepAlive(conn: RpcConnection, format: RpcSerializationFormat, activeCallIds: number[]): void;
|
|
495
|
+
item(conn: RpcConnection, format: RpcSerializationFormat, localId: number, index: number, item: unknown): void;
|
|
496
|
+
batch(conn: RpcConnection, format: RpcSerializationFormat, localId: number, index: number, items: unknown[]): void;
|
|
497
|
+
end(conn: RpcConnection, format: RpcSerializationFormat, localId: number, index: number, error?: {
|
|
498
|
+
TypeRef: string;
|
|
226
499
|
Message: string;
|
|
227
|
-
}
|
|
228
|
-
ack(conn: RpcConnection, localId: number, nextIndex: number, hostId: string): void;
|
|
229
|
-
ackEnd(conn: RpcConnection, localId: number, hostId: string): void;
|
|
500
|
+
}): void;
|
|
501
|
+
ack(conn: RpcConnection, format: RpcSerializationFormat, localId: number, nextIndex: number, hostId: string): void;
|
|
502
|
+
ackEnd(conn: RpcConnection, format: RpcSerializationFormat, localId: number, hostId: string): void;
|
|
230
503
|
}
|
|
231
504
|
|
|
232
505
|
/** Central RPC coordinator — manages peers, services, and configuration. */
|
|
@@ -236,6 +509,9 @@ declare class RpcHub {
|
|
|
236
509
|
readonly serviceHost: RpcServiceHost;
|
|
237
510
|
readonly systemCallSender: RpcSystemCallSender;
|
|
238
511
|
systemCallHandler: RpcSystemCallHandler;
|
|
512
|
+
/** Method registry for compact format hash ↔ name resolution.
|
|
513
|
+
* Created lazily, populated by addService/addClient. */
|
|
514
|
+
readonly registry: RpcMethodRegistry;
|
|
239
515
|
constructor(hubId?: string);
|
|
240
516
|
addPeer(peer: RpcPeer): void;
|
|
241
517
|
removePeer(ref: string): void;
|
|
@@ -281,6 +557,7 @@ declare class RpcRemoteObjectTracker {
|
|
|
281
557
|
private _objects;
|
|
282
558
|
register(obj: IRpcObject): void;
|
|
283
559
|
get(localId: number): IRpcObject | undefined;
|
|
560
|
+
keys(): IterableIterator<number>;
|
|
284
561
|
unregister(obj: IRpcObject): void;
|
|
285
562
|
disconnectAll(): void;
|
|
286
563
|
reconnectAll(): void;
|
|
@@ -292,6 +569,7 @@ declare class RpcSharedObjectTracker {
|
|
|
292
569
|
nextId(): number;
|
|
293
570
|
register(obj: IRpcObject): void;
|
|
294
571
|
get(localId: number): IRpcObject | undefined;
|
|
572
|
+
keys(): IterableIterator<number>;
|
|
295
573
|
unregister(obj: IRpcObject): void;
|
|
296
574
|
disconnectAll(): void;
|
|
297
575
|
}
|
|
@@ -302,6 +580,8 @@ declare class RpcSharedObjectTracker {
|
|
|
302
580
|
* so "json5np" (System.Text.Json, no polymorphism) is the correct match.
|
|
303
581
|
*/
|
|
304
582
|
declare const DEFAULT_SERIALIZATION_FORMAT = "json5np";
|
|
583
|
+
/** WebSocket close code sent by the server when the client's serialization format is unsupported. */
|
|
584
|
+
declare const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
|
|
305
585
|
/** Builds the WebSocket connection URL for an RpcClientPeer. */
|
|
306
586
|
type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string;
|
|
307
587
|
/** Default connection URL provider — appends `clientId` and `f` query parameters. */
|
|
@@ -346,11 +626,15 @@ declare abstract class RpcPeer {
|
|
|
346
626
|
get connection(): RpcConnection | undefined;
|
|
347
627
|
get isConnected(): boolean;
|
|
348
628
|
protected setupConnection(conn: RpcConnection): void;
|
|
629
|
+
/** The serialization format used by this peer. Override in subclasses. */
|
|
630
|
+
get format(): RpcSerializationFormat;
|
|
349
631
|
call(method: string, args?: unknown[], options?: RpcCallOptions): RpcOutboundCall;
|
|
350
632
|
callNoWait(method: string, args?: unknown[]): void;
|
|
351
633
|
close(): void;
|
|
352
634
|
/** Send any messages buffered while disconnected. Call after connection + handshake are ready. */
|
|
353
635
|
protected _flushPendingSends(): void;
|
|
636
|
+
/** Send pre-serialized wire data through the current connection. */
|
|
637
|
+
protected _sendWireData(data: string | Uint8Array): void;
|
|
354
638
|
/** Override in subclasses to handle the remote peer's handshake response. */
|
|
355
639
|
protected _onHandshakeReceived(_handshake: RemoteHandshake): void;
|
|
356
640
|
private _handleMessage;
|
|
@@ -384,11 +668,17 @@ declare class RpcClientPeer extends RpcPeer {
|
|
|
384
668
|
/** Builds the WebSocket URL for this peer. Replace to customize URL construction. */
|
|
385
669
|
connectionUrlResolver: RpcConnectionUrlResolver;
|
|
386
670
|
readonly peerChanged: EventHandlerSet<void>;
|
|
671
|
+
/** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */
|
|
672
|
+
readonly unsupportedFormat: EventHandlerSet<{
|
|
673
|
+
reason: string;
|
|
674
|
+
}>;
|
|
387
675
|
readonly reconnectDelayer: RpcClientPeerReconnectDelayer;
|
|
388
676
|
readonly reconnectsAtChanged: EventHandlerSet<void>;
|
|
389
677
|
get reconnectsAt(): number;
|
|
390
678
|
private _setReconnectsAt;
|
|
679
|
+
private _format;
|
|
391
680
|
constructor(hub: RpcHub, url: string, serializationFormat?: string);
|
|
681
|
+
get format(): RpcSerializationFormat;
|
|
392
682
|
get connectionKind(): RpcPeerConnectionKind;
|
|
393
683
|
/** One-shot connection for tests — no reconnection loop, no handshake. */
|
|
394
684
|
connectWith(conn: RpcConnection): void;
|
|
@@ -425,6 +715,7 @@ interface RpcStreamRef {
|
|
|
425
715
|
readonly ackPeriod: number;
|
|
426
716
|
readonly ackAdvance: number;
|
|
427
717
|
readonly allowReconnect: boolean;
|
|
718
|
+
readonly isRealTime: boolean;
|
|
428
719
|
}
|
|
429
720
|
/**
|
|
430
721
|
* Parses a stream reference string of the form "{hostId},{localId},{ackPeriod},{ackAdvance}".
|
|
@@ -471,6 +762,7 @@ declare class RpcStream<T> implements AsyncIterable<T>, IRpcObject {
|
|
|
471
762
|
dispose(): void;
|
|
472
763
|
private _notifyConsumer;
|
|
473
764
|
private _maybeSendAck;
|
|
765
|
+
private static readonly _emptyGuid;
|
|
474
766
|
private _sendAck;
|
|
475
767
|
private _sendAckEnd;
|
|
476
768
|
}
|
|
@@ -498,17 +790,21 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
498
790
|
readonly id: RpcObjectId;
|
|
499
791
|
readonly kind: 0;
|
|
500
792
|
readonly allowReconnect: boolean;
|
|
793
|
+
readonly isRealTime: boolean;
|
|
794
|
+
readonly canSkipTo: (item: T) => boolean;
|
|
501
795
|
readonly peer: RpcPeer;
|
|
502
796
|
readonly ackPeriod: number;
|
|
503
797
|
readonly ackAdvance: number;
|
|
504
798
|
private _nextIndex;
|
|
505
799
|
private _ended;
|
|
506
800
|
private _started;
|
|
507
|
-
|
|
801
|
+
private _lastAckedIndex;
|
|
802
|
+
private _ackWaiting;
|
|
803
|
+
constructor(peer: RpcPeer, ackPeriod?: number, ackAdvance?: number, allowReconnect?: boolean, isRealTime?: boolean, canSkipTo?: (item: T) => boolean);
|
|
508
804
|
/** Returns the stream reference string for the $sys.Ok response. */
|
|
509
805
|
toRef(): string;
|
|
510
806
|
/** Called by system call handler when $sys.Ack is received from the client. */
|
|
511
|
-
onAck(
|
|
807
|
+
onAck(nextIndex: number, _hostId: string): void;
|
|
512
808
|
/** Called by system call handler when $sys.AckEnd is received from the client. */
|
|
513
809
|
onAckEnd(_hostId: string): void;
|
|
514
810
|
/** Send a single item to the client. */
|
|
@@ -520,8 +816,15 @@ declare class RpcStreamSender<T> implements IRpcObject {
|
|
|
520
816
|
/**
|
|
521
817
|
* Consume an AsyncIterable and send all items to the client.
|
|
522
818
|
* Waits for the client's initial Ack before starting to pump items.
|
|
819
|
+
*
|
|
820
|
+
* When `isRealTime` is true, applies flow control based on `ackAdvance`:
|
|
821
|
+
* if the sender is `ackAdvance` items ahead of the last ACK, it enters
|
|
822
|
+
* skip mode — draining the source and discarding items until it finds
|
|
823
|
+
* one where `canSkipTo` returns true.
|
|
523
824
|
*/
|
|
524
825
|
writeFrom(source: AsyncIterable<T>): Promise<void>;
|
|
826
|
+
/** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
|
|
827
|
+
private _waitForAckBudget;
|
|
525
828
|
reconnect(): void;
|
|
526
829
|
disconnect(): void;
|
|
527
830
|
}
|
|
@@ -590,4 +893,24 @@ declare class RpcPeerStateMonitor {
|
|
|
590
893
|
private _scheduleRecompute;
|
|
591
894
|
}
|
|
592
895
|
|
|
593
|
-
|
|
896
|
+
/**
|
|
897
|
+
* Compute XXH3-64 hash of a byte array. Returns a BigInt (unsigned 64-bit).
|
|
898
|
+
* Pure TypeScript, synchronous, supports any input length.
|
|
899
|
+
*/
|
|
900
|
+
declare function xxh3_64(input: Uint8Array, seed?: bigint): bigint;
|
|
901
|
+
/**
|
|
902
|
+
* Compute XXH3-64 hash of a UTF-8 string. Returns a BigInt (unsigned 64-bit).
|
|
903
|
+
*/
|
|
904
|
+
declare function xxh3_64str(input: string, seed?: bigint): bigint;
|
|
905
|
+
/**
|
|
906
|
+
* Compute the RPC method hash matching .NET's RpcMethodRef.ComputeHashCode.
|
|
907
|
+
* Returns a 32-bit signed integer (lower 32 bits of XXH3-64 of [prefix + utf8Name]).
|
|
908
|
+
*
|
|
909
|
+
* The prefix is uint32 LE of (67211 * utf8Name.length), which ensures methods
|
|
910
|
+
* with different name lengths but similar text produce different hashes.
|
|
911
|
+
*
|
|
912
|
+
* Computed on demand — caller should cache the result per method name.
|
|
913
|
+
*/
|
|
914
|
+
declare function computeMethodHash(methodName: string): number;
|
|
915
|
+
|
|
916
|
+
export { ARG_DELIMITER, DEFAULT_SERIALIZATION_FORMAT, ENVELOPE_DELIMITER, FRAME_DELIMITER, type IRpcObject, type MethodMeta, RPC_CLOSE_CODE_UNSUPPORTED_FORMAT, type RemoteHandshake, type RpcCallOptions, RpcCallTypeId, RpcClientPeer, RpcClientPeerReconnectDelayer, type RpcConnection, type RpcConnectionUrlResolver, type RpcDeserializedMessage, type RpcDispatchContext, RpcHub, RpcInboundCall, RpcInboundCallTracker, RpcJsonSerializationFormat, type RpcMessage, RpcMessageChannelConnection, RpcMessagePackCompactSerializationFormat, RpcMessagePackSerializationFormat, type RpcMethodDef, type RpcMethodDefInput, RpcMethodRegistry, type RpcObjectId, RpcObjectKind, RpcOutboundCall, RpcOutboundCallTracker, RpcPeer, RpcPeerConnectionKind, type RpcPeerState, RpcPeerStateKind, RpcPeerStateMonitor, type RpcReceivedMessage, RpcRemoteObjectTracker, RpcSerializationFormat, RpcServerPeer, type RpcServiceDef, RpcServiceHost, type RpcServiceImpl, RpcSharedObjectTracker, RpcStream, type RpcStreamRef, RpcStreamSender, RpcSystemCallHandler, RpcSystemCallSender, RpcSystemCalls, RpcType, RpcWebSocketConnection, type RpcWireData, type ServiceMeta, type WebSocketLike, type WebSocketServer, WebSocketState, computeMethodHash, createBinaryEncoder, createMessageChannelPair, createRpcClient, defaultBinaryDecoder, defaultBinaryEncoder, defaultConnectionUrlResolver, defineRpcService, deserializeBinaryMessage, deserializeCompactBinaryMessage, deserializeMessage, getMethodsMeta, getServiceMeta, getStateDescription, isConnected, likelyConnected, parseStreamRef, resolveStreamRefs, rpcMethod, rpcService, serializeBinaryFrame, serializeBinaryMessage, serializeCompactBinaryMessage, serializeFrame, serializeMessage, splitBinaryFrame, splitCompactBinaryFrame, splitFrame, wireMethodName, xxh3_64, xxh3_64str };
|