@actuallab/rpc 13.0.12 → 13.0.14

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/logging.ts","../src/rpc-message.ts","../src/rpc-serialization.ts","../src/msgpack-map-patch.ts","../src/rpc-connection.ts","../src/rpc-message-channel-connection.ts","../src/rpc-service-def.ts","../src/rpc-call-tracker.ts","../src/rpc-call-stage.ts","../src/rpc-error.ts","../src/increasing-seq-compressor.ts","../src/rpc-stream.ts","../src/rpc-object.ts","../src/rpc-stream-sender.ts","../src/base64.ts","../src/rpc-system-call-handler.ts","../src/rpc-system-call-sender.ts","../src/rpc-remote-object-tracker.ts","../src/rpc-shared-object-tracker.ts","../src/rpc-peer.ts","../src/rpc-serialization-format.ts","../src/rpc-limits.ts","../src/rpc-client-peer-reconnect-delayer.ts","../src/rpc-service-host.ts","../src/rpc-decorators.ts","../src/rpc-xxhash3.ts","../src/rpc-method-registry.ts","../src/rpc-hub.ts","../src/rpc-peer-ref-builder.ts","../src/rpc-client.ts","../src/rpc-peer-state.ts","../src/rpc-peer-state-monitor.ts"],"sourcesContent":["import { createLogProvider, LogLevel } from '@actuallab/core';\n\n// Per-package log scopes. Use createLogProvider to get a typed helper:\n// const { debugLog, warnLog } = getLogs('RpcPeer');\n// Final scope name is 'rpc.RpcPeer' — the prefix isolates this package's\n// scopes from other packages' scopes when overriding levels at runtime.\nexport type RpcLogScope =\n | 'RpcCallTracker'\n | 'RpcClient'\n | 'RpcClientPeerReconnectDelayer'\n | 'RpcConnection'\n | 'RpcHub'\n | 'RpcInterceptor'\n | 'RpcMessageChannelConnection'\n | 'RpcMethodRegistry'\n | 'RpcOutboundCall'\n | 'RpcPeer'\n | 'RpcPeerStateMonitor'\n | 'RpcRemoteObjectTracker'\n | 'RpcSerialization'\n | 'RpcServiceHost'\n | 'RpcSharedObjectTracker'\n | 'RpcSharedStream'\n | 'RpcStream'\n | 'RpcStreamSender'\n | 'RpcSystemCallHandler'\n | 'RpcSystemCallSender'\n | 'RpcWebSocketClient';\n\n// Per-scope defaults. All Warn — output goes to the browser console, so the\n// out-of-the-box experience must be quiet. Users opt in to Info-level\n// connectivity tracing manually:\n// logLevels.override('rpc.*', LogLevel.Info)\n// or for a single scope:\n// logLevels.override('rpc.RpcPeer', LogLevel.Info)\nconst scopeDefaults: Record<RpcLogScope, LogLevel> = {\n RpcCallTracker: LogLevel.Warn,\n RpcClient: LogLevel.Warn,\n RpcClientPeerReconnectDelayer: LogLevel.Warn,\n RpcConnection: LogLevel.Warn,\n RpcHub: LogLevel.Warn,\n RpcInterceptor: LogLevel.Warn,\n RpcMessageChannelConnection: LogLevel.Warn,\n RpcMethodRegistry: LogLevel.Warn,\n RpcOutboundCall: LogLevel.Warn,\n RpcPeer: LogLevel.Info,\n RpcPeerStateMonitor: LogLevel.Warn,\n RpcRemoteObjectTracker: LogLevel.Warn,\n RpcSerialization: LogLevel.Warn,\n RpcServiceHost: LogLevel.Warn,\n RpcSharedObjectTracker: LogLevel.Warn,\n RpcSharedStream: LogLevel.Warn,\n RpcStream: LogLevel.Warn,\n RpcStreamSender: LogLevel.Warn,\n RpcSystemCallHandler: LogLevel.Warn,\n RpcSystemCallSender: LogLevel.Warn,\n RpcWebSocketClient: LogLevel.Warn,\n};\n\nexport const getLogs = createLogProvider<RpcLogScope>('rpc.', scopeDefaults);\n","// .NET counterparts:\n// RpcMessage — .NET has no single \"message\" type; the wire format is built by\n// RpcMessageSerializer / RpcOutboundMessage / RpcInboundMessage. In .NET the\n// envelope carries MethodRef, RelatedId, Headers, CallTypeId, and serialized\n// argument data as binary or text segments. In TS we use a simpler JSON\n// envelope + delimiter-separated args.\n//\n// Omitted from .NET:\n// - RpcCallTypeId Reliable (stage-based reconnection protocol) — the TS client\n// is browser-only; reconnection replays entire calls rather than resuming at\n// a stage, so Reliable call type adds no value.\n// - RpcHeader / RpcHeaders — .NET passes typed header arrays on every message\n// (Hash, ActivityContext, etc.). TS has no RPC caching, OpenTelemetry\n// propagation, or middleware pipeline, so headers aren't needed yet. The\n// Headers field on RpcMessage is reserved for future use.\n// - $sys.M (Match) / $sys.NotFound / $sys.Disconnect / Stream calls\n// (Ack, AckEnd, I, B, End) — Match is for hash-based response caching\n// (bandwidth optimisation); NotFound is a server-side \"service not registered\"\n// response; Disconnect is for RpcSharedObject lifetime management; the stream\n// calls are for RpcStream (server→client IAsyncEnumerable). None of these\n// are needed in the TS client today because:\n// * No response caching → no Match.\n// * Service-not-found is sent as $sys.Error.\n// * No shared-object tracking (server-side concept) → no Disconnect.\n// * No RpcStream yet → no Ack/AckEnd/I/B/End.\n// They can be added when streaming or caching is ported.\n// - Binary serialization variants (RpcByteMessageSerializer) — TS uses\n// text (JSON) exclusively; binary MessagePack/MemoryPack require native\n// codecs that aren't available in browsers.\n\n/** Identifies the type of an RPC call on the wire. */\nexport const enum RpcCallTypeId {\n Regular = 0,\n Reliable = 1,\n}\n\n/** Identifies system call methods — names include :argCount suffix for .NET interop. */\nexport const RpcSystemCalls = {\n handshake: '$sys.Handshake:1',\n ok: '$sys.Ok:1',\n error: '$sys.Error:1',\n cancel: '$sys.Cancel:0',\n keepAlive: '$sys.KeepAlive:1',\n reconnect: '$sys.Reconnect:3',\n ack: '$sys.Ack:2',\n ackEnd: '$sys.AckEnd:1',\n item: '$sys.I:2',\n batch: '$sys.B:2',\n end: '$sys.End:2',\n disconnect: '$sys.Disconnect:1',\n} as const;\n\n/** Wire-format RPC message envelope. */\nexport interface RpcMessage {\n CallType?: number;\n RelatedId?: number;\n Method?: string;\n Headers?: unknown[];\n}\n\n// Delimiters used in the wire format\nexport const ENVELOPE_DELIMITER = '\\n'; // \\x0A between envelope and args\nexport const ARG_DELIMITER = '\\x1F'; // Unit Separator between args\nexport const FRAME_DELIMITER = '\\n\\x1E'; // \\x0A\\x1E between messages in a frame\n","// Serialization for the RPC wire format.\r\n//\r\n// Supports two formats:\r\n// - Text (json5np): JSON envelope + delimiter-separated JSON args (V3 wire format)\r\n// - Binary (msgpack6np): V5 binary envelope + MessagePack-encoded args\r\n//\r\n// The format is selected at connection time via the `f=` query parameter.\r\n\r\nimport {\r\n Encoder,\r\n Decoder,\r\n} from '@msgpack/msgpack';\r\n// Side-effect import: patches Encoder.prototype to handle JS Map instances\r\n// as proper msgpack maps with typed keys (.NET-wire-compatible).\r\nimport './msgpack-map-patch.js';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport {\r\n ENVELOPE_DELIMITER,\r\n ARG_DELIMITER,\r\n FRAME_DELIMITER,\r\n} from './rpc-message.js';\r\n\r\nconst { warnLog } = getLogs('RpcSerialization');\r\n\r\n// ============================================================\r\n// Text format (json5np) — V3 wire format\r\n// ============================================================\r\n\r\n/** Serializes an RpcMessage + args into the json5 wire format. */\r\nexport function serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[]\r\n): string {\r\n const envelope = JSON.stringify(message);\r\n if (args === undefined || args.length === 0) {\r\n return envelope + ENVELOPE_DELIMITER;\r\n }\r\n const argsStr = args.map(a => JSON.stringify(a)).join(ARG_DELIMITER);\r\n return envelope + ENVELOPE_DELIMITER + argsStr;\r\n}\r\n\r\n/** Serializes multiple messages into a single WebSocket frame. */\r\nexport function serializeFrame(messages: string[]): string {\r\n return messages.join(FRAME_DELIMITER);\r\n}\r\n\r\n/** Splits a WebSocket text frame into individual message strings. */\r\nexport function splitFrame(frame: string): string[] {\r\n return frame.split(FRAME_DELIMITER);\r\n}\r\n\r\n/** Deserializes a single message string into envelope + args. */\r\nexport function deserializeMessage(raw: string): {\r\n message: RpcMessage;\r\n args: unknown[];\r\n} {\r\n const nlIndex = raw.indexOf(ENVELOPE_DELIMITER);\r\n if (nlIndex === -1) {\r\n return { message: JSON.parse(raw) as RpcMessage, args: [] };\r\n }\r\n\r\n const envelopeStr = raw.substring(0, nlIndex);\r\n const argsStr = raw.substring(nlIndex + 1);\r\n const message = JSON.parse(envelopeStr) as RpcMessage;\r\n\r\n if (argsStr.length === 0) {\r\n return { message, args: [] };\r\n }\r\n\r\n const args = argsStr.split(ARG_DELIMITER).map(s => {\r\n try {\r\n return JSON.parse(s) as unknown;\r\n } catch {\r\n warnLog?.log(`failed to parse arg as JSON: ${s.substring(0, 100)}`);\r\n return s; // Return raw string if not valid JSON\r\n }\r\n });\r\n\r\n return { message, args };\r\n}\r\n\r\n// ============================================================\r\n// Binary format (msgpack6np) — V5 wire format\r\n// ============================================================\r\n\r\nconst textEncoder = new TextEncoder();\r\nconst textDecoder = new TextDecoder();\r\n\r\n/**\r\n * Default module-level encoder/decoder. RpcWebSocketConnection passes its own\r\n * per-connection instances for the hot path; these defaults cover callers\r\n * that don't bother (tests, one-shot uses).\r\n *\r\n * Reusing an Encoder across calls is critical at high message rates because\r\n * - construction cost (internal resolvers, buffer allocation) is nontrivial;\r\n * - the internal write buffer stays at its largest observed size after the\r\n * first few encodes, eliminating `resizeBuffer` growth churn (which was\r\n * ~5% self time in the 300-pull profile).\r\n *\r\n * NOT thread-safe and NOT re-entrant. Safe under Node's single-threaded event\r\n * loop as long as `serializeBinaryMessage` is called synchronously (which it\r\n * always is — no awaits inside).\r\n *\r\n * Sized large enough to fit a typical video frame (~11 KB) + envelope\r\n * overhead without any `resizeBuffer` growth.\r\n *\r\n * `useBigInt64: true` makes the encoder emit JS `bigint` values as msgpack\r\n * int64/uint64 (0xd3/0xcf) instead of float64. Required for .NET `long`\r\n * fields whose 2026-era values (e.g. `Moment.EpochOffsetTicks` ~1.8×10¹⁶)\r\n * exceed `Number.MAX_SAFE_INTEGER` — a float64 payload would be rejected by\r\n * the server's `ReadInt64` path. Callers must pass a real `bigint` (not a\r\n * `number`) for such fields; `number` values follow the standard\r\n * int-or-float branching regardless of this flag.\r\n */\r\nconst INITIAL_ENCODER_BUFFER_SIZE = 32 * 1024;\r\nexport const defaultBinaryEncoder = new Encoder({ initialBufferSize: INITIAL_ENCODER_BUFFER_SIZE, useBigInt64: true });\r\nexport const defaultBinaryDecoder = new Decoder();\r\n/** Exposed so connection code can instantiate a matching Encoder\r\n * with the same initial buffer size without needing to reach into\r\n * this module's private constants. */\r\nexport function createBinaryEncoder(): Encoder {\r\n return new Encoder({ initialBufferSize: INITIAL_ENCODER_BUFFER_SIZE, useBigInt64: true });\r\n}\r\n\r\n// --- LEB128 VarUInt helpers ---\r\n\r\n/** Number of bytes a VarUint encoding of `value` will occupy. */\r\nfunction varUintByteLen(value: number): number {\r\n if (value < 0) value = 0;\r\n let n = 0;\r\n do {\r\n n++;\r\n value >>>= 7;\r\n } while (value > 0);\r\n return n;\r\n}\r\n\r\n/** Write VarUint directly into a `Uint8Array` at `pos`. Returns new pos. */\r\nfunction writeVarUintInto(out: Uint8Array, pos: number, value: number): number {\r\n if (value < 0) value = 0;\r\n do {\r\n let byte = value & 0x7f;\r\n value >>>= 7;\r\n if (value > 0) byte |= 0x80;\r\n out[pos++] = byte;\r\n } while (value > 0);\r\n return pos;\r\n}\r\n\r\nfunction readVarUint(\r\n data: Uint8Array,\r\n offset: number\r\n): { value: number; bytesRead: number } {\r\n let value = 0,\r\n shift = 0,\r\n bytesRead = 0;\r\n do {\r\n if (offset + bytesRead >= data.length) break;\r\n const byte = data[offset + bytesRead];\r\n value |= (byte & 0x7f) << shift;\r\n bytesRead++;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n } while (shift < 35); // Max 5 bytes for uint32\r\n return { value: value >>> 0, bytesRead };\r\n}\r\n\r\n// --- Binary helpers ---\r\n\r\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Serializes an RpcMessage + args into V5 binary format (msgpack6np).\r\n *\r\n * Layout (NO frame-level size prefix; V5 with PersistsMessageSize=false):\r\n * envelope + argData\r\n * Envelope:\r\n * [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]\r\n * [LVarSpan methodRef] [4-byte LE argLen]\r\n * ArgData: concatenated MessagePack-encoded arguments\r\n *\r\n * Single-allocation build: pre-encodes args with the supplied reusable\r\n * `Encoder`, computes the total envelope size, then writes everything\r\n * into one `Uint8Array`. Eliminates the previous build's intermediate\r\n * `headerParts` number array + four `Uint8Array` concats per call,\r\n * which showed up as ~2% self time in the 300-pull profile and\r\n * pulled the msgpack `resizeBuffer` + `Encoder` constructor along\r\n * with it for another ~6%.\r\n *\r\n * NOTE: `@msgpack/msgpack` `Encoder.encode()` already returns a fresh\r\n * copy of the internal buffer (there's a separate `encodeSharedRef()`\r\n * for zero-copy access), so we can push the results straight into\r\n * `argBufs` without an extra slice. The benefit of reusing the Encoder\r\n * is that its internal write buffer stays at its high-water mark —\r\n * once the first ~11 KB frame has grown it to the full size, no\r\n * subsequent `resizeBuffer` calls are needed.\r\n */\r\nexport function serializeBinaryMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder: Encoder = defaultBinaryEncoder\r\n): Uint8Array {\r\n // 1. Encode each arg. encode() returns a fresh Uint8Array copy per\r\n // call, so the results are safe to retain across subsequent encodes.\r\n let argsDataLen = 0;\r\n let argBufs: Uint8Array[] | null = null;\r\n if (args && args.length > 0) {\r\n argBufs = new Array<Uint8Array>(args.length);\r\n for (let i = 0; i < args.length; i++) {\r\n const buf = encoder.encode(args[i]);\r\n argBufs[i] = buf;\r\n argsDataLen += buf.length;\r\n }\r\n }\r\n\r\n // 2. Compute header sizes without intermediate allocations.\r\n const callType = message.CallType ?? 0;\r\n const relatedId = message.RelatedId ?? 0;\r\n const methodBytes = textEncoder.encode(message.Method ?? '');\r\n const relatedIdLen = varUintByteLen(relatedId);\r\n const methodLenVarintLen = varUintByteLen(methodBytes.length);\r\n const headerSize =\r\n 1 + relatedIdLen + methodLenVarintLen + methodBytes.length;\r\n const totalSize = headerSize + 4 + argsDataLen;\r\n\r\n // 3. Allocate the final wire buffer once and fill it in place.\r\n const out = new Uint8Array(totalSize);\r\n let pos = 0;\r\n out[pos++] = (callType << 5) & 0xe0;\r\n pos = writeVarUintInto(out, pos, relatedId);\r\n pos = writeVarUintInto(out, pos, methodBytes.length);\r\n out.set(methodBytes, pos);\r\n pos += methodBytes.length;\r\n // argLen as int32 LE\r\n out[pos++] = argsDataLen & 0xff;\r\n out[pos++] = (argsDataLen >>> 8) & 0xff;\r\n out[pos++] = (argsDataLen >>> 16) & 0xff;\r\n out[pos++] = (argsDataLen >>> 24) & 0xff;\r\n if (argBufs !== null) {\r\n for (const buf of argBufs) {\r\n out.set(buf, pos);\r\n pos += buf.length;\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Deserializes a single V5 binary message starting at `offset`.\r\n * Returns parsed message, args, and number of bytes consumed.\r\n *\r\n * Accepts an optional `Decoder` for reuse across calls on the same\r\n * connection — avoids constructing a new `@msgpack/msgpack.Decoder`\r\n * (and its internal resolver/table state) on every inbound message.\r\n */\r\nexport function deserializeBinaryMessage(\r\n data: Uint8Array,\r\n offset: number,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[]; bytesRead: number } {\r\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\r\n\r\n // V5: no frame-level size prefix. Parse envelope directly.\r\n let pos = offset;\r\n\r\n // Byte 0: upper 3 bits = CallTypeId, lower 5 bits = HeaderCount\r\n const byte0 = data[pos++];\r\n const callTypeId = (byte0 >> 5) & 0x7;\r\n const headerCount = byte0 & 0x1f;\r\n\r\n // RelatedId as VarUint\r\n const relId = readVarUint(data, pos);\r\n pos += relId.bytesRead;\r\n\r\n // MethodRef as LVarSpan\r\n const methodLen = readVarUint(data, pos);\r\n pos += methodLen.bytesRead;\r\n const methodBytes = data.subarray(pos, pos + methodLen.value);\r\n const method = textDecoder.decode(methodBytes);\r\n pos += methodLen.value;\r\n\r\n // ArgData length as fixed 4-byte LE\r\n const argDataLen = view.getInt32(pos, true);\r\n pos += 4;\r\n\r\n // Skip headers (if any — TS client doesn't use them)\r\n if (headerCount > 0) {\r\n for (let h = 0; h < headerCount; h++) {\r\n // L1Memory: 1-byte length prefix + key bytes\r\n const keyLen = data[pos++];\r\n pos += keyLen;\r\n // LVarSpan: VarUint length + value bytes\r\n const valLen = readVarUint(data, pos);\r\n pos += valLen.bytesRead + valLen.value;\r\n }\r\n }\r\n\r\n // Deserialize arguments from argData — multiple concatenated MessagePack values\r\n const args: unknown[] = [];\r\n const argEnd = pos + argDataLen;\r\n if (argDataLen > 0) {\r\n const argSlice = data.subarray(pos, argEnd);\r\n // Reuse the provided decoder across calls; `decodeMulti` is a generator\r\n // so each yield completes before the next `decode` begins, which is\r\n // safe on a single-threaded event loop.\r\n for (const decoded of decoder.decodeMulti(argSlice)) {\r\n args.push(decoded);\r\n }\r\n pos = argEnd;\r\n }\r\n\r\n const message: RpcMessage = {\r\n Method: method,\r\n RelatedId: relId.value,\r\n CallType: callTypeId,\r\n };\r\n\r\n return { message, args, bytesRead: pos - offset };\r\n}\r\n\r\n/**\r\n * Splits a binary WebSocket frame into individual deserialized messages.\r\n * V5 envelopes are self-delimiting (header + method + argLen + argData), so\r\n * multiple envelopes concatenated in a single WebSocket frame can be decoded\r\n * by iterating with the bytesRead returned from deserializeBinaryMessage.\r\n *\r\n * This must stay symmetric with serializeBinaryFrame() (which concatenates\r\n * multiple envelopes). The .NET server batches outbound RPC messages into a\r\n * single WebSocket frame via WebSocketChannel/WriteDelayer, so any assumption\r\n * that a frame is a single envelope silently drops every trailing message.\r\n *\r\n * Accepts an optional `Decoder` for reuse across calls on the same\r\n * connection — forwarded to `deserializeBinaryMessage`.\r\n */\r\nexport function splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[] }[] {\r\n const results: { message: RpcMessage; args: unknown[] }[] = [];\r\n let offset = 0;\r\n while (offset < frame.length) {\r\n const { message, args, bytesRead } = deserializeBinaryMessage(\r\n frame,\r\n offset,\r\n decoder\r\n );\r\n if (bytesRead <= 0) break; // defensive — avoid infinite loop on malformed data\r\n results.push({ message, args });\r\n offset += bytesRead;\r\n }\r\n return results;\r\n}\r\n\r\n/**\r\n * Serializes multiple binary messages into a single WebSocket frame.\r\n * Simply concatenates the already size-prefixed messages.\r\n */\r\nexport function serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return concatUint8Arrays(messages);\r\n}\r\n\r\n// ============================================================\r\n// Compact binary format (V5Compact) — 4-byte method hash\r\n// ============================================================\r\n\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\n/**\r\n * Serializes an RpcMessage + args into V5Compact binary format.\r\n * Same as V5 except the method is encoded as a 4-byte LE hash\r\n * instead of an LVar-prefixed UTF-8 string.\r\n *\r\n * Layout:\r\n * [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]\r\n * [4-byte LE methodHash] [4-byte LE argLen]\r\n * [argData]\r\n */\r\nexport function serializeCompactBinaryMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n registry?: RpcMethodRegistry,\r\n encoder: Encoder = defaultBinaryEncoder\r\n): Uint8Array {\r\n // 1. Encode args\r\n let argsDataLen = 0;\r\n let argBufs: Uint8Array[] | null = null;\r\n if (args && args.length > 0) {\r\n argBufs = new Array<Uint8Array>(args.length);\r\n for (let i = 0; i < args.length; i++) {\r\n const buf = encoder.encode(args[i]);\r\n argBufs[i] = buf;\r\n argsDataLen += buf.length;\r\n }\r\n }\r\n\r\n // 2. Get method hash\r\n const methodName = message.Method ?? '';\r\n const methodHash = registry?.requireHash(methodName) ?? 0;\r\n\r\n // 3. Compute sizes\r\n const callType = message.CallType ?? 0;\r\n const relatedId = message.RelatedId ?? 0;\r\n const relatedIdLen = varUintByteLen(relatedId);\r\n // Compact: 4 bytes for hash (instead of LVar method name)\r\n const headerSize = 1 + relatedIdLen + 4;\r\n const totalSize = headerSize + 4 + argsDataLen;\r\n\r\n // 4. Build wire buffer\r\n const out = new Uint8Array(totalSize);\r\n let pos = 0;\r\n out[pos++] = (callType << 5) & 0xe0;\r\n pos = writeVarUintInto(out, pos, relatedId);\r\n // Method hash as 4-byte LE uint32\r\n const hashU32 = methodHash >>> 0;\r\n out[pos++] = hashU32 & 0xff;\r\n out[pos++] = (hashU32 >>> 8) & 0xff;\r\n out[pos++] = (hashU32 >>> 16) & 0xff;\r\n out[pos++] = (hashU32 >>> 24) & 0xff;\r\n // argLen as int32 LE\r\n out[pos++] = argsDataLen & 0xff;\r\n out[pos++] = (argsDataLen >>> 8) & 0xff;\r\n out[pos++] = (argsDataLen >>> 16) & 0xff;\r\n out[pos++] = (argsDataLen >>> 24) & 0xff;\r\n if (argBufs !== null) {\r\n for (const buf of argBufs) {\r\n out.set(buf, pos);\r\n pos += buf.length;\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Deserializes a single V5Compact binary message starting at `offset`.\r\n * Reads a 4-byte LE method hash and resolves it via the registry.\r\n */\r\nexport function deserializeCompactBinaryMessage(\r\n data: Uint8Array,\r\n offset: number,\r\n registry?: RpcMethodRegistry,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[]; bytesRead: number } {\r\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\r\n let pos = offset;\r\n\r\n // Byte 0: upper 3 bits = CallTypeId, lower 5 bits = HeaderCount\r\n const byte0 = data[pos++];\r\n const callTypeId = (byte0 >> 5) & 0x7;\r\n const headerCount = byte0 & 0x1f;\r\n\r\n // RelatedId as VarUint\r\n const relId = readVarUint(data, pos);\r\n pos += relId.bytesRead;\r\n\r\n // Method hash as fixed 4-byte LE uint32\r\n const methodHash = view.getUint32(pos, true);\r\n pos += 4;\r\n const method =\r\n registry?.getName(methodHash | 0) ??\r\n `<hash:0x${methodHash.toString(16).padStart(8, '0')}>`;\r\n\r\n // ArgData length as fixed 4-byte LE\r\n const argDataLen = view.getInt32(pos, true);\r\n pos += 4;\r\n\r\n // Skip headers (if any)\r\n if (headerCount > 0) {\r\n for (let h = 0; h < headerCount; h++) {\r\n const keyLen = data[pos++];\r\n pos += keyLen;\r\n const valLen = readVarUint(data, pos);\r\n pos += valLen.bytesRead + valLen.value;\r\n }\r\n }\r\n\r\n // Deserialize arguments\r\n const args: unknown[] = [];\r\n const argEnd = pos + argDataLen;\r\n if (argDataLen > 0) {\r\n const argSlice = data.subarray(pos, argEnd);\r\n for (const decoded of decoder.decodeMulti(argSlice)) {\r\n args.push(decoded);\r\n }\r\n pos = argEnd;\r\n }\r\n\r\n const message: RpcMessage = {\r\n Method: method,\r\n RelatedId: relId.value,\r\n CallType: callTypeId,\r\n };\r\n\r\n return { message, args, bytesRead: pos - offset };\r\n}\r\n\r\n/**\r\n * Splits a compact binary frame into individual deserialized messages.\r\n */\r\nexport function splitCompactBinaryFrame(\r\n frame: Uint8Array,\r\n registry?: RpcMethodRegistry,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[] }[] {\r\n const results: { message: RpcMessage; args: unknown[] }[] = [];\r\n let offset = 0;\r\n while (offset < frame.length) {\r\n const { message, args, bytesRead } =\r\n deserializeCompactBinaryMessage(\r\n frame,\r\n offset,\r\n registry,\r\n decoder\r\n );\r\n if (bytesRead <= 0) break;\r\n results.push({ message, args });\r\n offset += bytesRead;\r\n }\r\n return results;\r\n}\r\n","import { Encoder } from '@msgpack/msgpack';\r\n\r\n/**\r\n * Patches `@msgpack/msgpack`'s `Encoder` so JS `Map` instances are encoded\r\n * as proper msgpack `map` types with typed keys (matching .NET's\r\n * `Dictionary<K, V>` wire format). Without this patch the encoder treats\r\n * `Map` as a plain object — since `Object.keys(new Map())` returns `[]`,\r\n * any `Map` is silently serialized as an empty `{}`.\r\n *\r\n * Idempotent: the patch is applied at most once per process.\r\n *\r\n * This is needed by the `$sys.Reconnect:3` protocol to send\r\n * `Dictionary<int, byte[]>` over msgpack in a way that a .NET server can\r\n * deserialize. The JSON side is naturally compatible (keys become strings\r\n * in JSON regardless) — this patch fixes the binary path only.\r\n */\r\n/* eslint-disable @typescript-eslint/no-explicit-any,\r\n @typescript-eslint/no-unsafe-member-access,\r\n @typescript-eslint/no-unsafe-call,\r\n @typescript-eslint/no-unsafe-assignment\r\n -- the entire patch reaches into private Encoder internals to make Map\r\n encoding work; the type annotations on `@msgpack/msgpack` don't expose\r\n these methods so all access is necessarily untyped. */\r\nexport function patchMsgpackEncoderForMaps(): void {\r\n const proto = Encoder.prototype as any;\r\n if (proto._hasMapPatch) return;\r\n proto._hasMapPatch = true;\r\n\r\n const originalDoEncode = proto.doEncode as (object: unknown, depth: number) => void;\r\n\r\n proto.doEncode = function (this: unknown, object: unknown, depth: number): void {\r\n if (object instanceof Map) {\r\n _encodeMapInstance.call(this, object, depth);\r\n return;\r\n }\r\n originalDoEncode.call(this, object, depth);\r\n };\r\n\r\n function _encodeMapInstance(this: unknown, m: Map<unknown, unknown>, depth: number): void {\r\n const self = this as any;\r\n const size = m.size;\r\n if (size < 16) {\r\n self.writeU8(0x80 + size);\r\n } else if (size < 0x10000) {\r\n self.writeU8(0xde);\r\n self.writeU16(size);\r\n } else {\r\n self.writeU8(0xdf);\r\n self.writeU32(size);\r\n }\r\n for (const [key, value] of m) {\r\n // Recurse via the (now patched) doEncode so nested Maps work too.\r\n self.doEncode(key, depth + 1);\r\n self.doEncode(value, depth + 1);\r\n }\r\n }\r\n}\r\n/* eslint-enable @typescript-eslint/no-explicit-any,\r\n @typescript-eslint/no-unsafe-member-access,\r\n @typescript-eslint/no-unsafe-call,\r\n @typescript-eslint/no-unsafe-assignment */\r\n\r\n// Apply at module load — idempotent.\r\npatchMsgpackEncoderForMaps();\r\n","// .NET counterparts:\r\n// RpcConnection — thin wrapper around RpcTransport + PropertyBag. RpcTransport\r\n// is an abstract IAsyncEnumerable<RpcInboundMessage> + Send() that can be\r\n// WebSocket-based or in-memory. The transport also handles frame batching and\r\n// back-pressure via a Channel<T> send queue.\r\n//\r\n// Omitted from .NET:\r\n// - PropertyBag on connection — used in .NET for per-connection metadata (e.g.\r\n// authentication info attached by middleware). TS has no middleware pipeline,\r\n// so no need for an extensible property bag.\r\n// - IsLocal flag — .NET distinguishes local loopback connections (same-process\r\n// server) from remote ones. TS is always a remote client.\r\n// - RpcTransport as IAsyncEnumerable — .NET reads inbound messages via\r\n// async iteration with cancellation. TS uses event-based delivery\r\n// (messageReceived handler) because browser WebSocket API is event-driven.\r\n// - Channel<RpcOutboundMessage> send queue with backpressure — .NET buffers\r\n// outbound messages in an async channel that the transport drains. TS sends\r\n// synchronously on the WebSocket; if the socket isn't ready, messages are\r\n// buffered in _sendBuffer (CONNECTING) or silently dropped (CLOSING/CLOSED).\r\n// JS is single-threaded so there's no contention, and WebSocket.send() itself\r\n// handles buffering at the OS level.\r\n// - IAsyncDisposable — .NET transports are disposable resources. TS connections\r\n// are closed via close() and garbage-collected.\r\n\r\nimport { Encoder, Decoder } from '@msgpack/msgpack';\r\nimport { PromiseSource, EventHandlerSet } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport {\r\n splitFrame,\r\n serializeFrame,\r\n splitBinaryFrame as splitBinaryFrameFn,\r\n serializeBinaryFrame,\r\n createBinaryEncoder,\r\n} from './rpc-serialization.js';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport type { RpcSerializationFormat } from './rpc-serialization-format.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\nconst { warnLog } = getLogs('RpcConnection');\r\n\r\n/** Abstract WebSocket interface — works with both browser WebSocket and Node.js ws. */\r\nexport interface WebSocketLike {\r\n readonly readyState: number;\r\n binaryType?: string;\r\n send(data: string | ArrayBufferLike | Uint8Array | ArrayBufferView): void;\r\n close(code?: number, reason?: string): void;\r\n onopen: ((ev: unknown) => void) | null;\r\n onmessage: ((ev: { data: unknown }) => void) | null;\r\n onclose: ((ev: { code: number; reason: string }) => void) | null;\r\n onerror: ((ev: unknown) => void) | null;\r\n}\r\n\r\nexport const WebSocketState = {\r\n CONNECTING: 0,\r\n OPEN: 1,\r\n CLOSING: 2,\r\n CLOSED: 3,\r\n} as const;\r\n\r\n/** Received message — either text (string) or binary (already parsed). */\r\nexport type RpcReceivedMessage =\r\n | { kind: 'text'; raw: string }\r\n | { kind: 'binary'; message: RpcMessage; args: unknown[] };\r\n\r\n/** Abstract RPC connection — transport-agnostic interface for sending/receiving messages. */\r\nexport interface RpcConnection {\r\n readonly isOpen: boolean;\r\n readonly binaryMode: boolean;\r\n readonly whenConnected: Promise<void>;\r\n readonly messageReceived: EventHandlerSet<RpcReceivedMessage>;\r\n readonly closed: EventHandlerSet<{ code: number; reason: string }>;\r\n /** Optional reusable msgpack encoder for outbound binary messages.\r\n * Callers passing this to `serializeBinaryMessage` skip the per-call\r\n * Encoder construction + resizeBuffer growth. Implementations that\r\n * don't support binary serialization can leave it undefined. */\r\n readonly encoder?: Encoder;\r\n send(serializedMessage: string): void;\r\n sendBinary(data: Uint8Array): void;\r\n close(code?: number, reason?: string): void;\r\n}\r\n\r\n/** WebSocket-based RpcConnection — handles frame splitting, binary/text modes, and message queueing. */\r\nexport class RpcWebSocketConnection implements RpcConnection {\r\n private _ws: WebSocketLike;\r\n private _sendBuffer: (string | Uint8Array)[] = [];\r\n private _connected = new PromiseSource<void>();\r\n\r\n /** Per-connection msgpack encoder — reused across outbound messages to\r\n * amortise construction cost and to keep the internal write buffer at\r\n * its largest observed size (avoids repeated `resizeBuffer` growth on\r\n * large video frames). See serializeBinaryMessage() for details. */\r\n private _encoder: Encoder = createBinaryEncoder();\r\n\r\n /** Per-connection msgpack decoder — reused across inbound messages\r\n * rather than reconstructing one for every `decodeMulti` call. */\r\n private _decoder: Decoder = new Decoder();\r\n\r\n /** Exposed for callers who need to encode outbound binary messages\r\n * through this connection (e.g. `serializeBinaryMessage`). Safe to\r\n * pass to synchronous encode calls on the same event-loop turn. */\r\n get encoder(): Encoder {\r\n return this._encoder;\r\n }\r\n get decoder(): Decoder {\r\n return this._decoder;\r\n }\r\n\r\n private _format: RpcSerializationFormat | undefined;\r\n private _methodRegistry: RpcMethodRegistry | undefined;\r\n\r\n readonly binaryMode: boolean;\r\n readonly messageReceived = new EventHandlerSet<RpcReceivedMessage>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly error = new EventHandlerSet<unknown>();\r\n\r\n constructor(\r\n ws: WebSocketLike,\r\n binaryMode = false,\r\n format?: RpcSerializationFormat,\r\n methodRegistry?: RpcMethodRegistry\r\n ) {\r\n this._ws = ws;\r\n this.binaryMode = binaryMode;\r\n this._format = format;\r\n this._methodRegistry = methodRegistry;\r\n\r\n if (binaryMode && ws.binaryType !== undefined)\r\n ws.binaryType = 'arraybuffer';\r\n\r\n if (ws.readyState === WebSocketState.OPEN) {\r\n this._connected.resolve();\r\n this._flush();\r\n }\r\n\r\n ws.onopen = () => {\r\n this._connected.resolve();\r\n this._flush();\r\n };\r\n\r\n ws.onmessage = ev => {\r\n // Binary frame — V5 self-delimiting envelopes (one or more per WS frame).\r\n //\r\n // Accept BOTH `ArrayBuffer` (browser WebSocket when `binaryType` is\r\n // 'arraybuffer') AND `Uint8Array`-shaped views (Node `ws` delivering a\r\n // `Buffer`, which is a `Uint8Array` subclass). The node-ws adapter\r\n // intentionally passes Buffer through without an ArrayBuffer copy —\r\n // at 300 concurrent video streams that copy was ~100 MB/s of pure\r\n // memcpy + allocation pressure. Matching `ArrayBuffer.isView` here\r\n // keeps the zero-copy path alive all the way to `splitBinaryFrame`.\r\n if (\r\n ev.data instanceof Uint8Array ||\r\n ev.data instanceof ArrayBuffer\r\n ) {\r\n // Node `Buffer` is a `Uint8Array` subclass, so we can hand it to\r\n // `splitBinaryFrame` directly with no view wrapping or copying.\r\n // Browsers deliver `ArrayBuffer`, which needs a single zero-copy\r\n // view wrap.\r\n const frame =\r\n ev.data instanceof Uint8Array\r\n ? ev.data\r\n : new Uint8Array(ev.data);\r\n try {\r\n const messages = this._splitBinary(frame);\r\n for (const { message, args } of messages) {\r\n this.messageReceived.trigger({\r\n kind: 'binary',\r\n message,\r\n args,\r\n });\r\n }\r\n } catch (e) {\r\n warnLog?.log('Failed to split binary frame:', e);\r\n this.error.trigger(e);\r\n }\r\n } else if (\r\n typeof Blob !== 'undefined' &&\r\n ev.data instanceof Blob &&\r\n this.binaryMode\r\n ) {\r\n // Fallback: some Chromium builds deliver the first frames as Blob even\r\n // after setting binaryType='arraybuffer'. Convert and route through\r\n // the binary path so we don't silently drop them.\r\n void ev.data.arrayBuffer().then(ab => {\r\n const frame = new Uint8Array(ab);\r\n try {\r\n const messages = this._splitBinary(frame);\r\n for (const { message, args } of messages) {\r\n this.messageReceived.trigger({\r\n kind: 'binary',\r\n message,\r\n args,\r\n });\r\n }\r\n } catch (e) {\r\n warnLog?.log('Failed to split binary blob frame:', e);\r\n this.error.trigger(e);\r\n }\r\n });\r\n } else {\r\n // Text frame — JSON delimited messages\r\n const data =\r\n typeof ev.data === 'string' ? ev.data : String(ev.data);\r\n const messages = splitFrame(data);\r\n for (const msg of messages) {\r\n if (msg.length > 0)\r\n this.messageReceived.trigger({\r\n kind: 'text',\r\n raw: msg,\r\n });\r\n }\r\n }\r\n };\r\n\r\n ws.onclose = ev => {\r\n this.closed.trigger({ code: ev.code, reason: ev.reason });\r\n };\r\n\r\n ws.onerror = ev => {\r\n this.error.trigger(ev);\r\n };\r\n }\r\n\r\n get isOpen(): boolean {\r\n return this._ws.readyState === WebSocketState.OPEN;\r\n }\r\n\r\n get whenConnected(): Promise<void> {\r\n return this._connected.promise;\r\n }\r\n\r\n send(serializedMessage: string): void {\r\n this._sendRaw(serializedMessage);\r\n }\r\n\r\n sendBinary(data: Uint8Array): void {\r\n this._sendRaw(data);\r\n }\r\n\r\n sendTextBatch(messages: string[]): void {\r\n this._sendRaw(serializeFrame(messages));\r\n }\r\n\r\n sendBinaryBatch(messages: Uint8Array[]): void {\r\n this._sendRaw(serializeBinaryFrame(messages));\r\n }\r\n\r\n /** Split a binary frame using the format (compact-aware) or default splitter. */\r\n private _splitBinary(\r\n frame: Uint8Array\r\n ): { message: RpcMessage; args: unknown[] }[] {\r\n if (this._format) {\r\n return this._format.splitBinaryFrame(\r\n frame,\r\n this._decoder,\r\n this._methodRegistry\r\n );\r\n }\r\n return splitBinaryFrameFn(frame, this._decoder);\r\n }\r\n\r\n close(code?: number, reason?: string): void {\r\n this._ws.close(code, reason);\r\n }\r\n\r\n private _sendRaw(data: string | Uint8Array): void {\r\n try {\r\n if (this._ws.readyState === WebSocketState.OPEN) {\r\n this._ws.send(data);\r\n } else if (this._ws.readyState === WebSocketState.CONNECTING)\r\n this._sendBuffer.push(data);\r\n // CLOSING/CLOSED: silently drop\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n\r\n private _flush(): void {\r\n if (this._sendBuffer.length === 0) return;\r\n const buffer = this._sendBuffer;\r\n this._sendBuffer = [];\r\n try {\r\n for (const item of buffer) this._ws.send(item);\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n}\r\n","// .NET counterpart: none — this is a TS-only test transport.\r\n//\r\n// .NET tests use a different mechanism: RpcTestClient creates an in-memory\r\n// Channel<RpcMessage> pair wrapped in an RpcTransport. We use the browser-\r\n// standard MessageChannel API instead, which gives us a synchronous in-process\r\n// transport without requiring WebSocket mocks.\r\n\r\nimport { EventHandlerSet } from '@actuallab/core';\r\nimport { splitFrame } from './rpc-serialization.js';\r\nimport type { RpcConnection, RpcReceivedMessage } from './rpc-connection.js';\r\n\r\n/** MessagePort-based RpcConnection — for in-process testing without WebSocket mocks. */\r\nexport class RpcMessageChannelConnection implements RpcConnection {\r\n private _port: MessagePort;\r\n private _open = true;\r\n\r\n readonly binaryMode = false;\r\n readonly messageReceived = new EventHandlerSet<RpcReceivedMessage>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly whenConnected: Promise<void> = Promise.resolve(); // immediately connected\r\n\r\n constructor(port: MessagePort) {\r\n this._port = port;\r\n port.onmessage = (ev: MessageEvent) => {\r\n const data =\r\n typeof ev.data === 'string' ? ev.data : String(ev.data);\r\n for (const msg of splitFrame(data))\r\n if (msg.length > 0)\r\n this.messageReceived.trigger({ kind: 'text', raw: msg });\r\n };\r\n }\r\n\r\n get isOpen(): boolean {\r\n return this._open;\r\n }\r\n\r\n send(serializedMessage: string): void {\r\n if (!this._open) return;\r\n try {\r\n this._port.postMessage(serializedMessage);\r\n } catch {\r\n // never fail\r\n }\r\n }\r\n\r\n sendBinary(data: Uint8Array): void {\r\n if (!this._open) return;\r\n try {\r\n this._port.postMessage(data, [data.buffer]);\r\n } catch {\r\n // never fail\r\n }\r\n }\r\n\r\n close(code?: number, reason?: string): void {\r\n if (!this._open) return;\r\n this._open = false;\r\n this._port.close();\r\n this.closed.trigger({ code: code ?? 1000, reason: reason ?? '' });\r\n }\r\n}\r\n\r\n/** Creates a pair of connected RpcMessageChannelConnections for testing. */\r\nexport function createMessageChannelPair(): [\r\n RpcMessageChannelConnection,\r\n RpcMessageChannelConnection,\r\n ] {\r\n const channel = new MessageChannel();\r\n return [\r\n new RpcMessageChannelConnection(channel.port1),\r\n new RpcMessageChannelConnection(channel.port2),\r\n ];\r\n}\r\n","// .NET counterparts:\r\n// RpcServiceDef (Configuration/) — 162 lines. Describes a registered service\r\n// including its .NET Type, Mode (Local/Client/Server/Hybrid), ServerResolver\r\n// (DI-based lazy resolution), ClientType, IsSystem, IsBackend flags, Scope,\r\n// LegacyNames, PropertyBag, and a method dictionary built via reflection.\r\n// RpcMethodDef (Configuration/) — 189 lines. Describes a single method:\r\n// FullName (ServiceName.MethodName:ArgCount), CallType (Regular/Reliable),\r\n// NoWait, HasPolymorphicArguments/Result, RpcMethodKind (Query/Command/System),\r\n// RpcMethodAttribute, LegacyNames, Tracer, outbound/inbound call pipeline\r\n// (factories, invokers, middleware chains, routers, timeouts).\r\n//\r\n// Omitted from .NET:\r\n// - Reflection-based method discovery (BuildMethods) — .NET scans interface\r\n// methods via reflection, filters generics, builds call pipelines. TS uses\r\n// explicit defineRpcService() or decorator metadata — no runtime reflection.\r\n// - RpcMethodKind (Query/Command/System/Other) — .NET classifies methods to\r\n// route Commands through ICommander. TS has no command bus; the distinction\r\n// is unnecessary.\r\n// - IsBackend / IBackendService — .NET separates \"backend-only\" services that\r\n// require backend auth. TS client never exposes backend services.\r\n// - RpcMethodAttribute / LegacyNames — .NET supports custom wire names and\r\n// version-specific legacy name mappings for rolling upgrades. TS has a\r\n// single version; wire names = \"ServiceName.MethodName:wireArgCount\".\r\n// - Call pipeline (OutboundCallFactory, InboundCallFactory, InboundCallInvoker,\r\n// MiddlewareFilter, OutboundCallRouter, OutboundCallTimeouts) — .NET builds a\r\n// per-method middleware chain at startup via generic factory types. TS dispatches\r\n// directly from RpcServiceHost; no middleware or routing layer.\r\n// - CallType (Regular/Reliable) — Reliable calls track stages for reconnect\r\n// resumption. TS replays entire calls on reconnect.\r\n// - HasPolymorphicArguments/Result — governs serializer behavior for abstract\r\n// argument types. TS uses JSON (inherently polymorphic); not applicable.\r\n// - Tracer / RpcCallTracer — per-method distributed tracing. Not ported.\r\n// - PropertyBag — extensible metadata on service/method def. Not needed.\r\n// - Mode (ServiceMode) / Scope / ServerResolver / ClientType — DI wiring.\r\n\r\n/** Controls outbound RPC call behavior regarding connection waiting, reconnection, and resending. */\r\nexport const RpcRemoteExecutionMode = {\r\n /** Wait for connection if disconnected when the call is made. */\r\n AwaitForConnection: 1,\r\n /** Resend on reconnection to the same peer. */\r\n AllowReconnect: 2,\r\n /** Resend on reconnection to a different peer (implies AllowReconnect). */\r\n AllowResend: 6, // 4 | AllowReconnect\r\n /** Default: wait + reconnect + resend. */\r\n Default: 7, // AwaitForConnection | AllowResend\r\n} as const;\r\n/** Bitfield combination of RpcRemoteExecutionMode flags. */\r\nexport type RpcRemoteExecutionMode = number;\r\n\r\n/** Describes a single method on an RPC service. */\r\nexport interface RpcMethodDef {\r\n readonly name: string;\r\n readonly serviceName: string;\r\n readonly argCount: number;\r\n readonly wireArgCount: number;\r\n readonly callTypeId: number;\r\n readonly stream: boolean;\r\n readonly noWait: boolean;\r\n readonly remoteExecutionMode: RpcRemoteExecutionMode;\r\n}\r\n\r\n/** Describes an RPC service — its name and method definitions. */\r\nexport interface RpcServiceDef {\r\n readonly name: string;\r\n readonly methods: ReadonlyMap<string, RpcMethodDef>;\r\n}\r\n\r\nexport interface RpcMethodDefInput {\r\n args: unknown[];\r\n returns?: symbol;\r\n callTypeId?: number;\r\n /** Override for wireArgCount. Default: args.length + 1 (assumes a CancellationToken slot). */\r\n wireArgCount?: number;\r\n /** Override for remoteExecutionMode. Default: RpcRemoteExecutionMode.Default (7). NoWait methods always use 0. */\r\n remoteExecutionMode?: RpcRemoteExecutionMode;\r\n}\r\n\r\nexport const RpcType = {\r\n object: Symbol('object'),\r\n stream: Symbol('stream'),\r\n noWait: Symbol('noWait'),\r\n void: Symbol('void'),\r\n} as const;\r\n\r\n/** Returns the full wire method name: \"ServiceName.MethodName:wireArgCount\". */\r\nexport function wireMethodName(def: RpcMethodDef): string {\r\n return `${def.serviceName}.${def.name}:${def.wireArgCount}`;\r\n}\r\n\r\nexport function defineRpcService(\r\n name: string,\r\n methods: Record<string, RpcMethodDefInput>\r\n): RpcServiceDef {\r\n const map = new Map<string, RpcMethodDef>();\r\n for (const [key, input] of Object.entries(methods)) {\r\n // If the key contains \":\", the part before \":\" is the clean name\r\n // (the suffix is for JS object key disambiguation of overloads).\r\n // Otherwise the key IS the clean name.\r\n const colonIndex = key.indexOf(':');\r\n const cleanName = colonIndex >= 0 ? key.substring(0, colonIndex) : key;\r\n const wireArgCount = input.wireArgCount ?? input.args.length + 1;\r\n const mapKey = `${cleanName}:${wireArgCount}`;\r\n const isNoWait = input.returns === RpcType.noWait;\r\n map.set(mapKey, {\r\n name: cleanName,\r\n serviceName: name,\r\n argCount: input.args.length,\r\n wireArgCount,\r\n callTypeId: input.callTypeId ?? 0,\r\n stream: input.returns === RpcType.stream,\r\n noWait: isNoWait,\r\n remoteExecutionMode: isNoWait\r\n ? 0\r\n : (input.remoteExecutionMode ?? RpcRemoteExecutionMode.Default),\r\n });\r\n }\r\n return { name, methods: map };\r\n}\r\n","// .NET counterparts:\r\n// RpcCall (29 lines) — base class: MethodDef, Id, NoWait flag, Lock.\r\n// RpcOutboundCall (437 lines) — tracks an outbound call's full lifecycle:\r\n// result via AsyncTaskMethodBuilder, CacheInfoCapture, CancellationHandler,\r\n// CompletedStage (for Reliable reconnection), StartedAt (for timeouts),\r\n// hashing for Match, tracing, routing, and retry logic.\r\n// RpcInboundCall (298 lines) — tracks an inbound call: deserialization,\r\n// middleware-chain invocation, cancellation via linked CTS, stage-based\r\n// re-processing for reconnection, sending the result back.\r\n// RpcOutboundCallTracker (255 lines) — thread-safe ConcurrentDictionary of\r\n// outbound calls with: Maintain() loop (timeout monitoring + logging),\r\n// Reconnect() protocol (stage-based call resumption), Abort(), TryReroute().\r\n// RpcInboundCallTracker (64 lines) — ConcurrentDictionary, GetOrRegister,\r\n// Unregister, Clear.\r\n//\r\n// Omitted from .NET:\r\n// - AsyncTaskMethodBuilder / typed RpcOutboundCall<T> — .NET creates a typed\r\n// task builder for each call's return type. TS uses PromiseSource<unknown>\r\n// (untyped) because TypeScript erases generics at runtime.\r\n// - CancellationHandler (CancellationToken.Register → Cancel) — .NET registers\r\n// a callback per outbound call. TS implements equivalent via optional\r\n// AbortSignal on RpcPeer.call(); the cancel handler rejects the promise,\r\n// removes from tracker, and sends $sys.Cancel — same behavior as .NET's\r\n// Cancel() + NotifyCancelled().\r\n// - StartedAt / CpuTimestamp / Timeout monitoring — .NET's Maintain() loop\r\n// checks elapsed time against per-method timeouts every CallTimeoutCheckPeriod.\r\n// TS has no per-call timeout mechanism; the WebSocket-level disconnect +\r\n// rejectAll handles stuck calls.\r\n// - CompletedStage / RpcCallStage — supports Reliable call type's stage-based\r\n// reconnection protocol where the server tells the client which stage each\r\n// call reached, avoiding re-execution of completed stages. TS replays entire\r\n// calls on reconnect (simpler, adequate for browser client).\r\n// - Reconnect() protocol — outbound tracker sends $sys.Reconnect with compressed\r\n// call IDs grouped by stage; server responds with \"unknown\" IDs that need\r\n// re-sending. Not ported; TS uses full replay.\r\n// - Abort() with multi-pass retry — .NET iterates 3+ times with delays to\r\n// catch late-registered calls. TS does a single-pass rejectAll().\r\n// - TryReroute() — checks if the call's peer route has changed (load-balancer\r\n// rerouting) and sets RpcRerouteException. TS has no routing layer.\r\n// - IsLongLiving / _longLivingCalls — separate tracking for calls that outlive\r\n// normal timeouts (e.g. streaming). Not needed without RpcStream.\r\n// - RpcInboundCall: middleware-chain invocation, deserialization with polymorphic\r\n// argument handling, stage-based TryReprocess, CancellationTokenSource per\r\n// call. TS inbound calls are dispatched directly by RpcServiceHost; no\r\n// middleware, no per-call CTS, no stage tracking.\r\n// - RpcCall.Lock (monitor-based synchronisation) — .NET uses lock(this) for\r\n// thread-safe result setting. TS is single-threaded; no locking needed.\r\n\r\nimport { PromiseSource } from '@actuallab/core';\r\nimport { RpcCallStage } from './rpc-call-stage.js';\r\nimport { RpcRemoteExecutionMode } from './rpc-service-def.js';\r\n\r\n/** Tracks a pending outbound RPC call. */\r\nexport class RpcOutboundCall {\r\n readonly callId: number;\r\n readonly method: string;\r\n readonly result = new PromiseSource<unknown>();\r\n /** Bitfield of RpcRemoteExecutionMode flags controlling reconnect/resend behavior. */\r\n readonly remoteExecutionMode: number;\r\n\r\n /** Whether to remove this call from the tracker on $sys.Ok. Default: true.\r\n * Subclasses (e.g. compute calls) override to false to stay in tracker for invalidation. */\r\n readonly removeOnOk: boolean = true;\r\n\r\n /** The serialized wire data — stored for re-sending on reconnect. */\r\n serializedWireData: string | Uint8Array = '';\r\n\r\n /**\r\n * Bitfield of {@link RpcCallStage} flags indicating how far this call\r\n * has progressed. Used by the `$sys.Reconnect` protocol to tell the\r\n * remote peer which calls are still pending its attention.\r\n *\r\n * - 0: brand new, no result yet (the peer is expected to be actively processing).\r\n * - `ResultReady` (1): `$sys.Ok` received for this call.\r\n * - `Invalidated` (3): result was invalidated (compute calls only).\r\n * - `Unregistered` (0x1000): removed from the tracker.\r\n */\r\n completedStage = 0;\r\n\r\n constructor(callId: number, method: string, remoteExecutionMode = 7) {\r\n this.callId = callId;\r\n this.method = method;\r\n this.remoteExecutionMode = remoteExecutionMode;\r\n }\r\n\r\n /**\r\n * Returns the stage to report to the remote peer via `$sys.Reconnect`,\r\n * or `null` if this call should not be reconciled and must be aborted.\r\n *\r\n * Mirrors .NET `RpcOutboundCall.GetReconnectStage` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcOutboundCall.cs.\r\n */\r\n getReconnectStage(isPeerChanged: boolean): number | null {\r\n const mode = this.remoteExecutionMode;\r\n if (!(mode & RpcRemoteExecutionMode.AllowReconnect)) return null;\r\n if (isPeerChanged && !(mode & RpcRemoteExecutionMode.AllowResend)) return null;\r\n return this.completedStage;\r\n }\r\n\r\n /** Called when the connection is lost. Subclasses can override to resolve invalidation promises. */\r\n onDisconnect(): void {\r\n // no-op by default\r\n }\r\n}\r\n\r\n/** Manages outbound calls by their RelatedId. */\r\nexport class RpcOutboundCallTracker {\r\n private _calls = new Map<number, RpcOutboundCall>();\r\n private _nextId = 1;\r\n\r\n get size(): number {\r\n return this._calls.size;\r\n }\r\n\r\n nextId(): number {\r\n return this._nextId++;\r\n }\r\n\r\n register(call: RpcOutboundCall): void {\r\n this._calls.set(call.callId, call);\r\n }\r\n\r\n get(callId: number): RpcOutboundCall | undefined {\r\n return this._calls.get(callId);\r\n }\r\n\r\n remove(callId: number): RpcOutboundCall | undefined {\r\n const call = this._calls.get(callId);\r\n if (call !== undefined) {\r\n this._calls.delete(callId);\r\n call.completedStage |= RpcCallStage.Unregistered;\r\n }\r\n return call;\r\n }\r\n\r\n values(): IterableIterator<RpcOutboundCall> {\r\n return this._calls.values();\r\n }\r\n\r\n activeCallIds(): number[] {\r\n return [...this._calls.keys()];\r\n }\r\n\r\n /** Reject all pending calls with the given error.\r\n * Stage-3 compute calls (result resolved, awaiting invalidation) are kept in the tracker. */\r\n rejectAll(error: Error): void {\r\n for (const [id, call] of this._calls) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call — keep it for later invalidation on reconnect/stop\r\n continue;\r\n }\r\n call.result.reject(error);\r\n call.onDisconnect();\r\n this._calls.delete(id);\r\n }\r\n }\r\n\r\n /** Invalidate all remaining stage-3 calls (on reconnect or peer stop). */\r\n invalidateAll(): void {\r\n for (const call of this._calls.values()) {\r\n call.onDisconnect();\r\n }\r\n this._calls.clear();\r\n }\r\n\r\n clear(): void {\r\n this._calls.clear();\r\n }\r\n}\r\n\r\n/** Tracks an incoming inbound RPC call. */\r\nexport class RpcInboundCall {\r\n readonly callId: number;\r\n readonly method: string;\r\n readonly args: unknown[];\r\n\r\n constructor(callId: number, method: string, args: unknown[]) {\r\n this.callId = callId;\r\n this.method = method;\r\n this.args = args;\r\n }\r\n}\r\n\r\n/** Manages inbound calls by their RelatedId. */\r\nexport class RpcInboundCallTracker {\r\n private _calls = new Map<number, RpcInboundCall>();\r\n\r\n get size(): number {\r\n return this._calls.size;\r\n }\r\n\r\n register(call: RpcInboundCall): void {\r\n this._calls.set(call.callId, call);\r\n }\r\n\r\n get(callId: number): RpcInboundCall | undefined {\r\n return this._calls.get(callId);\r\n }\r\n\r\n remove(callId: number): RpcInboundCall | undefined {\r\n const call = this._calls.get(callId);\r\n if (call !== undefined) this._calls.delete(callId);\r\n return call;\r\n }\r\n\r\n clear(): void {\r\n this._calls.clear();\r\n }\r\n}\r\n","/**\n * Well-known RPC call completion stage constants, used by the\n * `$sys.Reconnect` protocol to tell the peer which calls have already\n * reached which stage and therefore don't need to be re-processed.\n *\n * Direct port of .NET `ActualLab.Rpc.Infrastructure.RpcCallStage` at\n * src/ActualLab.Rpc/Infrastructure/RpcCallStage.cs.\n */\nexport const RpcCallStage = {\n /** The call's result has been received from the peer. */\n ResultReady: 1,\n /** The compute call's result has been invalidated (compute-call specific). */\n Invalidated: 3,\n /**\n * Bit flag added on top of ResultReady/Invalidated when the outbound\n * call has been removed from the tracker (unregistered).\n */\n Unregistered: 0x1_000,\n} as const;\n\nexport type RpcCallStage = number;\n","/** Error thrown for a failed remote RPC call — carries the remote exception's type name when available. */\nexport class RpcError extends Error {\n readonly typeName?: string;\n\n constructor(message: string, typeName?: string) {\n super(message);\n this.name = 'RpcError';\n if (typeName !== undefined)\n this.typeName = typeName;\n }\n}\n","/**\n * Compresses and decompresses monotonically increasing sequences of\n * integers using variable-length LEB128 encoding of successive deltas.\n *\n * Wire-compatible with .NET `ActualLab.Rpc.Internal.IncreasingSeqCompressor`\n * (src/ActualLab.Rpc/Internal/IncreasingSeqCompressor.cs) — which\n * underlies the `$sys.Reconnect` protocol.\n *\n * Inputs MUST be sorted non-decreasing. Maximum supported value is\n * 2^53 - 1 (JavaScript `Number` safe-integer limit); call IDs in TS\n * never exceed this because they're generated from a 32-bit counter.\n */\nexport const IncreasingSeqCompressor = {\n /**\n * Serialize a non-decreasing sequence of non-negative integers as\n * LEB128-encoded deltas.\n *\n * Throws `RangeError` if the input is not monotonically non-decreasing,\n * or if a value exceeds `Number.MAX_SAFE_INTEGER`.\n */\n serialize(values: Iterable<number>): Uint8Array {\n // First pass: compute total byte length; second pass: write.\n // (A growable buffer would also work, but values are small here.)\n const deltas: number[] = [];\n let last = 0;\n let totalBytes = 0;\n for (const value of values) {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new RangeError(\n `IncreasingSeqCompressor: value must be a non-negative safe integer (got ${value}).`);\n const delta = value - last;\n if (delta < 0)\n throw new RangeError(\n `IncreasingSeqCompressor: sequence must be non-decreasing (saw ${value} after ${last}).`);\n deltas.push(delta);\n last = value;\n totalBytes += _varUintByteLength(delta);\n }\n\n const out = new Uint8Array(totalBytes);\n let offset = 0;\n for (const delta of deltas) {\n offset = _writeVarUint(out, delta, offset);\n }\n return out;\n },\n\n /**\n * Decode bytes produced by {@link serialize} back into the original\n * sorted sequence of non-negative integers.\n */\n deserialize(data: Uint8Array): number[] {\n const result: number[] = [];\n let last = 0;\n let offset = 0;\n while (offset < data.length) {\n const [delta, next] = _readVarUint(data, offset);\n offset = next;\n last += delta;\n result.push(last);\n }\n return result;\n },\n} as const;\n\n// -- LEB128 (variable-length unsigned) encoding -----------------------------\n// Kept in sync with .NET SpanExt.WriteVarUInt64 / ReadVarUInt64 at\n// src/ActualLab.Core/Collections/SpanExt.ReadWriteVarUInt.cs:22-91.\n// We use `Number` arithmetic up to the 53-bit safe-integer ceiling; values\n// beyond that would need BigInt, which is not required for TS call IDs.\n\nconst LOW_BITS = 0x7f;\nconst HIGH_BIT = 0x80;\n\nfunction _varUintByteLength(value: number): number {\n let n = 0;\n let v = value;\n while (v >= HIGH_BIT) {\n n++;\n v = Math.floor(v / 128);\n }\n return n + 1;\n}\n\nfunction _writeVarUint(out: Uint8Array, value: number, offset: number): number {\n let v = value;\n while (v >= HIGH_BIT) {\n out[offset++] = (HIGH_BIT | (v & LOW_BITS)) & 0xff;\n v = Math.floor(v / 128);\n }\n out[offset++] = v & 0xff;\n return offset;\n}\n\nfunction _readVarUint(data: Uint8Array, offset: number): [number, number] {\n let value = 0;\n let shift = 0;\n // Up to 9 bytes contribute 7 bits each = 63 bits.\n while (shift < 63) {\n if (offset >= data.length)\n throw new RangeError('IncreasingSeqCompressor: truncated VarUInt.');\n const b = data[offset++];\n // Multiply by 2^shift using Math.pow to stay in Number arithmetic.\n // For shift up to 49 (7*7) the intermediate `(b & LOW_BITS) * 2^shift`\n // stays within Number.MAX_SAFE_INTEGER; beyond that the caller is\n // expected to keep call IDs within 2^53.\n value += (b & LOW_BITS) * Math.pow(2, shift);\n if (b <= LOW_BITS) return [value, offset];\n shift += 7;\n }\n // 10th byte — matches .NET's 1-bit allowance.\n if (offset >= data.length)\n throw new RangeError('IncreasingSeqCompressor: truncated VarUInt.');\n const b = data[offset++];\n if (b > 1)\n throw new RangeError('IncreasingSeqCompressor: VarUInt value exceeds 64 bits.');\n value += b * Math.pow(2, 63);\n return [value, offset];\n}\n","import Denque from 'denque';\r\nimport { PromiseSource } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcObjectId, IRpcObject } from './rpc-object.js';\r\nimport { RpcObjectKind } from './rpc-object.js';\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport { RpcStreamSender } from './rpc-stream-sender.js';\r\n\r\nconst { warnLog } = getLogs('RpcStream');\r\n\r\n/** Default ack period (matches .NET RpcStream defaults). */\r\nconst DEFAULT_ACK_PERIOD = 30;\r\n/** Default ack advance window (matches .NET RpcStream defaults). */\r\nconst DEFAULT_ACK_ADVANCE = 61;\r\n\r\n/** Parsed stream reference from the server's result payload. */\r\nexport interface RpcStreamRef {\r\n readonly hostId: string;\r\n readonly localId: number;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n}\r\n\r\n/**\r\n * A stream source is either a plain async iterable or a factory function\r\n * that receives an AbortSignal and returns an async iterable.\r\n * The AbortSignal is aborted when the sender is disconnected, allowing\r\n * the source to release resources (e.g., camera, microphone) promptly.\r\n */\r\nexport type RpcStreamSource<T> = AsyncIterable<T> | ((abortSignal: AbortSignal) => AsyncIterable<T>);\r\n\r\n/** Configuration options for creating a local (origin-side) RpcStream. */\r\nexport interface RpcStreamOptions<T> {\r\n ackPeriod?: number;\r\n /**\r\n * Wire-level flow-control window: the maximum index the sender may advance\r\n * past the last acknowledged index. Mirrors .NET `RpcStream.AckAdvance`.\r\n */\r\n ackAdvance?: number;\r\n /**\r\n * Local-only sender ring buffer capacity. When undefined (or smaller than\r\n * `ackAdvance`), `ackAdvance` is used as the buffer size.\r\n * Set this larger than `ackAdvance` to let the sender pre-buffer items\r\n * beyond the in-flight window. Not transmitted on the wire.\r\n */\r\n bufferSize?: number;\r\n allowReconnect?: boolean;\r\n isRealTime?: boolean;\r\n canSkipTo?: (item: T) => boolean;\r\n}\r\n\r\n/**\r\n * Parses a stream reference string of the form \"{hostId},{localId},{ackPeriod},{ackAdvance}\".\r\n * Returns null if the value is not a valid stream reference.\r\n */\r\nexport function parseStreamRef(value: unknown): RpcStreamRef | null {\r\n // Text format: \"hostId,localId,ackPeriod,ackAdvance[,allowReconnect[,isRealTime]]\"\r\n if (typeof value === 'string') {\r\n const parts = value.split(',');\r\n if (parts.length < 4 || parts.length > 6) return null;\r\n const hostId = parts[0];\r\n const localId = parseInt(parts[1], 10);\r\n const ackPeriod = parseInt(parts[2], 10);\r\n const ackAdvance = parseInt(parts[3], 10);\r\n if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance))\r\n return null;\r\n const allowReconnect = parts.length < 5 || parts[4] !== '0';\r\n const isRealTime = parts.length >= 6 && parts[5] === '1';\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };\r\n }\r\n // Binary (MessagePack) format: { SerializedId: [hostId, localId], AckPeriod, AckAdvance, AllowReconnect }\r\n if (typeof value === 'object' && value !== null) {\r\n const obj = value as Record<string, unknown>;\r\n const serializedId = obj.SerializedId as unknown[];\r\n if (!Array.isArray(serializedId) || serializedId.length < 2)\r\n return null;\r\n const rawHostId = serializedId[0];\r\n const hostId = String(rawHostId);\r\n const localId = Number(serializedId[1]);\r\n const ackPeriod = Number(obj.AckPeriod ?? 256);\r\n const ackAdvance = Number(obj.AckAdvance ?? 128);\r\n const allowReconnect = obj.AllowReconnect !== false;\r\n const isRealTime = obj.IsRealTime === true;\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Dual-mode RPC stream — works as both local (origin-side) and remote (target-side).\r\n *\r\n * **Local mode**: wraps an AsyncIterable and carries streaming configuration\r\n * (IsRealTime, CanSkipTo, etc.). Created by service methods and sent to the\r\n * remote peer via RpcStreamSender.\r\n *\r\n * **Remote mode**: consumes items sent by the server via system calls\r\n * ($sys.I, $sys.B, $sys.End) and implements AsyncIterable for for-await-of consumption.\r\n *\r\n * This mirrors the .NET RpcStream<T> design where the same type serves both roles.\r\n */\r\nexport class RpcStream<T> implements AsyncIterable<T>, IRpcObject {\r\n /**\r\n * Grace period (ms) given to a factory-form local source to honor its\r\n * AbortSignal and exit naturally before the sender force-closes the\r\n * iterator via `iterator.return()`.\r\n *\r\n * Only applies to sources provided as `(abortSignal) => AsyncIterable<T>`.\r\n * Plain `AsyncIterable<T>` sources are force-closed immediately on disconnect.\r\n */\r\n public static disconnectGracePeriodMs = 100;\r\n\r\n readonly id: RpcObjectId;\r\n readonly kind: RpcObjectKind;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n readonly canSkipTo: (item: T) => boolean;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n /**\r\n * Local-only ring buffer capacity hint for the sender. Undefined means\r\n * \"use ackAdvance as the buffer size\". Not part of the wire ref.\r\n */\r\n readonly bufferSize?: number;\r\n\r\n // Local (origin) mode\r\n readonly localSource?: RpcStreamSource<T>;\r\n\r\n // Remote (target) mode\r\n readonly peer!: RpcPeer;\r\n\r\n // Local-only state\r\n private _sender?: RpcStreamSender<T>;\r\n private _toRefValue?: unknown;\r\n private _whenSent?: Promise<void>;\r\n\r\n // Remote-only state\r\n private _buffer: Denque<T> = new Denque<T>();\r\n private _nextExpectedIndex = 0;\r\n private _consumerWaiting: PromiseSource<void> | null = null;\r\n private _completed = false;\r\n private _completionError: Error | null = null;\r\n private _disposed = false;\r\n private _started = false;\r\n private _iterating = false;\r\n private _ackSentUpTo = 0;\r\n private _nextConsumedIndex = 0;\r\n\r\n /** Create a local (origin-side) stream wrapping an async iterable or source factory with optional configuration. */\r\n constructor(source: RpcStreamSource<T>, options?: RpcStreamOptions<T>);\r\n /** Create a remote (target-side) stream from a parsed stream reference and peer. */\r\n constructor(ref: RpcStreamRef, peer: RpcPeer);\r\n constructor(\r\n sourceOrRef: RpcStreamSource<T> | RpcStreamRef,\r\n optionsOrPeer?: RpcStreamOptions<T> | RpcPeer,\r\n ) {\r\n if (_isStreamSource(sourceOrRef)) {\r\n // Local (origin) mode\r\n const options = (optionsOrPeer as RpcStreamOptions<T> | undefined) ?? {};\r\n this.kind = RpcObjectKind.Local;\r\n this.localSource = sourceOrRef;\r\n this.id = { hostId: '', localId: 0 };\r\n this.allowReconnect = options.allowReconnect ?? true;\r\n this.isRealTime = options.isRealTime ?? false;\r\n this.canSkipTo = options.canSkipTo ?? (() => true);\r\n this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD;\r\n this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE;\r\n this.bufferSize = options.bufferSize;\r\n } else {\r\n // Remote (target) mode\r\n const ref = sourceOrRef;\r\n const peer = optionsOrPeer as RpcPeer;\r\n this.kind = RpcObjectKind.Remote;\r\n this.id = { hostId: ref.hostId, localId: ref.localId };\r\n this.peer = peer;\r\n this.allowReconnect = ref.allowReconnect;\r\n this.isRealTime = ref.isRealTime;\r\n this.canSkipTo = () => true; // not serialized\r\n this.ackPeriod = ref.ackPeriod;\r\n this.ackAdvance = ref.ackAdvance;\r\n // bufferSize is local-only; not present on the wire ref\r\n }\r\n }\r\n\r\n /**\r\n * For local streams: resolves when the sender finishes pumping (after `toRef()` was called).\r\n * Rejects if the source throws. Throws if `toRef()` hasn't been called yet.\r\n */\r\n get whenSent(): Promise<void> {\r\n if (!this._whenSent)\r\n throw new Error('toRef() must be called before awaiting whenSent.');\r\n return this._whenSent;\r\n }\r\n\r\n /** Local sender backing this stream after `toRef()` has been called.\r\n * `undefined` for remote streams or before `toRef()`. */\r\n get sender(): RpcStreamSender<T> | undefined {\r\n return this._sender;\r\n }\r\n\r\n /**\r\n * For local streams: bind to a peer, create and register an `RpcStreamSender`,\r\n * start pumping items from the local source in the background, and return the\r\n * stream reference.\r\n *\r\n * For binary peers the ref is a MessagePack-compatible object\r\n * (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);\r\n * for text peers it is a comma-separated string.\r\n *\r\n * Can only be called once per stream.\r\n */\r\n toRef(peer: RpcPeer): unknown {\r\n if (this.kind !== RpcObjectKind.Local)\r\n throw new Error('toRef() is only valid for local streams.');\r\n if (this._sender) {\r\n if (this._sender.peer !== peer)\r\n throw new Error('toRef() was already called with a different peer.');\r\n // Same peer — return the cached ref\r\n return this._toRefValue!;\r\n }\r\n\r\n const isFactory = typeof this.localSource === 'function';\r\n const sender = new RpcStreamSender<T>(\r\n peer, this.ackPeriod, this.ackAdvance,\r\n this.allowReconnect, this.isRealTime, this.canSkipTo,\r\n isFactory, this.bufferSize,\r\n );\r\n peer.sharedObjects.register(sender);\r\n this._sender = sender;\r\n\r\n // Resolve source: if it's a factory, call it with the sender's AbortSignal\r\n const source = isFactory\r\n ? (this.localSource as (s: AbortSignal) => AsyncIterable<T>)(sender.abortSignal)\r\n : this.localSource as AsyncIterable<T>;\r\n\r\n // Start pumping in background (writeFrom awaits the initial Ack)\r\n this._whenSent = sender.writeFrom(source);\r\n\r\n // Cache and return ref in the format appropriate for the peer's serialization\r\n this._toRefValue = peer.serializationFormat.isBinary\r\n ? {\r\n SerializedId: [sender.id.hostId, sender.id.localId],\r\n AckPeriod: sender.ackPeriod,\r\n AckAdvance: sender.ackAdvance,\r\n AllowReconnect: sender.allowReconnect,\r\n }\r\n : sender.toRef();\r\n return this._toRefValue;\r\n }\r\n\r\n /** Called by system call handler when a single item arrives ($sys.I). */\r\n onItem(index: number, item: T): void {\r\n if (this._disposed || this._completed) return;\r\n\r\n if (index > this._nextExpectedIndex) {\r\n warnLog?.log(`item index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);\r\n if (!this.allowReconnect) {\r\n this._completed = true;\r\n this._completionError = new Error(\r\n `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`\r\n );\r\n this._notifyConsumer();\r\n return;\r\n }\r\n this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n // Duplicate — ack and ignore\r\n this._maybeSendAck(Math.min(index + 1, this._nextConsumedIndex));\r\n return;\r\n }\r\n\r\n this._buffer.push(item);\r\n this._nextExpectedIndex = index + 1;\r\n this._notifyConsumer();\r\n }\r\n\r\n /** Called by system call handler when a batch arrives ($sys.B). */\r\n onBatch(index: number, items: T[]): void {\r\n if (this._disposed || this._completed) return;\r\n\r\n if (index > this._nextExpectedIndex) {\r\n warnLog?.log(`batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);\r\n if (!this.allowReconnect) {\r\n this._completed = true;\r\n this._completionError = new Error(\r\n `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`\r\n );\r\n this._notifyConsumer();\r\n return;\r\n }\r\n this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n this._maybeSendAck(Math.min(index + items.length, this._nextConsumedIndex));\r\n return;\r\n }\r\n\r\n for (const item of items) this._buffer.push(item);\r\n this._nextExpectedIndex = index + items.length;\r\n this._notifyConsumer();\r\n }\r\n\r\n /** Called by system call handler when the stream ends ($sys.End). */\r\n onEnd(index: number, error: Error | null): void {\r\n if (this._disposed || this._completed) return;\r\n this._completed = true;\r\n this._completionError = error;\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- IRpcObject --\r\n\r\n reconnect(): void {\r\n if (this.kind === RpcObjectKind.Local) return; // no-op for local streams\r\n if (this.allowReconnect) {\r\n this._sendAck(this._nextExpectedIndex, true);\r\n } else {\r\n this.disconnect();\r\n }\r\n }\r\n\r\n disconnect(): void {\r\n if (this.kind === RpcObjectKind.Local) {\r\n // Propagate to sender — aborts the AbortSignal and terminates the iterator\r\n this._sender?.disconnect();\r\n return;\r\n }\r\n if (this._completed) return;\r\n this._completed = true;\r\n this._completionError = new Error('Peer disconnected.');\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- AsyncIterable --\r\n\r\n [Symbol.asyncIterator](): AsyncIterator<T> {\r\n // Local mode: delegate to the local source\r\n if (this.kind === RpcObjectKind.Local) {\r\n const source = typeof this.localSource === 'function'\r\n ? this.localSource(new AbortController().signal)\r\n : this.localSource!;\r\n return source[Symbol.asyncIterator]();\r\n }\r\n\r\n // Remote mode: buffer-based consumption\r\n if (this._iterating)\r\n throw new Error('RpcStream can only be iterated once.');\r\n this._iterating = true;\r\n\r\n return {\r\n next: async (): Promise<IteratorResult<T>> => {\r\n // Lazy start: send initial ack on first next() call\r\n if (!this._started) {\r\n this._started = true;\r\n this._sendAck(0, true);\r\n }\r\n this._maybeSendAck(this._nextConsumedIndex);\r\n\r\n // Read from buffer or wait for new data\r\n for (;;) {\r\n if (!this._buffer.isEmpty()) {\r\n // shift() is O(1) on Denque and releases the slot so the\r\n // ring buffer can be reclaimed as the consumer drains.\r\n const value = this._buffer.shift()!;\r\n this._nextConsumedIndex++;\r\n return { value, done: false };\r\n }\r\n\r\n if (this._completed) {\r\n if (this._completionError) throw this._completionError;\r\n return { value: undefined as T, done: true };\r\n }\r\n\r\n // Wait for more data\r\n this._consumerWaiting = new PromiseSource<void>();\r\n await this._consumerWaiting.promise;\r\n }\r\n },\r\n\r\n return: (): Promise<IteratorResult<T>> => {\r\n this._sendAckEnd();\r\n this.dispose();\r\n return Promise.resolve({ value: undefined as T, done: true });\r\n },\r\n };\r\n }\r\n\r\n dispose(): void {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n this._completed = true;\r\n if (this.kind === RpcObjectKind.Remote) {\r\n this.peer.remoteObjects.unregister(this);\r\n }\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- Private --\r\n\r\n private _notifyConsumer(): void {\r\n if (this._consumerWaiting !== null) {\r\n this._consumerWaiting.resolve();\r\n this._consumerWaiting = null;\r\n }\r\n }\r\n\r\n private _maybeSendAck(nextIndex: number): void {\r\n if (this.ackPeriod <= 0) return;\r\n const threshold = this._ackSentUpTo + this.ackPeriod;\r\n if (nextIndex >= threshold) {\r\n this._sendAck(nextIndex, false);\r\n }\r\n }\r\n\r\n private static readonly _emptyGuid = '00000000-0000-0000-0000-000000000000';\r\n\r\n private _sendAck(nextIndex: number, mustReset: boolean): void {\r\n this._ackSentUpTo = nextIndex;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n const hostId = mustReset ? this.id.hostId : RpcStream._emptyGuid;\r\n this.peer.hub.systemCallSender.ack(\r\n conn,\r\n this.peer.serializationFormat,\r\n this.id.localId,\r\n nextIndex,\r\n hostId\r\n );\r\n }\r\n }\r\n\r\n private _sendAckEnd(): void {\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.ackEnd(\r\n conn,\r\n this.peer.serializationFormat,\r\n this.id.localId,\r\n this.id.hostId\r\n );\r\n }\r\n }\r\n}\r\n\r\n/** @internal Type guard for RpcStreamSource (AsyncIterable or factory function). */\r\nfunction _isStreamSource<T>(value: unknown): value is RpcStreamSource<T> {\r\n if (typeof value === 'function') return true;\r\n return (\r\n value !== null &&\r\n typeof value === 'object' &&\r\n Symbol.asyncIterator in (value)\r\n );\r\n}\r\n\r\n/**\r\n * Recursively walks a deserialized value and replaces any stream ref strings\r\n * with live RpcStream<unknown> instances registered on the given peer.\r\n * This handles nested stream refs inside complex return types (e.g. Table<T>\r\n * containing RpcStream<Row<T>> fields serialized as \"{hostId},{localId},{ackPeriod},{ackAdvance}\").\r\n */\r\nexport function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown {\r\n if (value === null || value === undefined) return value;\r\n\r\n if (typeof value === 'string') {\r\n const ref = parseStreamRef(value);\r\n if (ref !== null) {\r\n const stream = new RpcStream(ref, peer);\r\n peer.remoteObjects.register(stream);\r\n return stream;\r\n }\r\n return value;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n for (let i = 0; i < value.length; i++) {\r\n value[i] = resolveStreamRefs(value[i], peer);\r\n }\r\n return value;\r\n }\r\n\r\n if (typeof value === 'object') {\r\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer)\r\n return value;\r\n\r\n for (const key of Object.keys(value as Record<string, unknown>)) {\r\n (value as Record<string, unknown>)[key] = resolveStreamRefs(\r\n (value as Record<string, unknown>)[key],\r\n peer\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n return value;\r\n}\r\n","export interface RpcObjectId {\r\n readonly hostId: string;\r\n readonly localId: number;\r\n}\r\n\r\nexport const RpcObjectKind = { Local: 0, Remote: 1 } as const;\r\nexport type RpcObjectKind = (typeof RpcObjectKind)[keyof typeof RpcObjectKind];\r\n\r\nexport interface IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind: RpcObjectKind;\r\n readonly allowReconnect: boolean;\r\n reconnect(): void;\r\n disconnect(): void;\r\n}\r\n","import { PromiseSource, RingBuffer } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcObjectId, IRpcObject } from './rpc-object.js';\r\nimport { RpcObjectKind } from './rpc-object.js';\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport { RpcStream } from './rpc-stream.js';\r\n\r\nconst { warnLog } = getLogs('RpcSharedStream');\r\n\r\n/** Default ack period for server-side streams. */\r\nconst DEFAULT_ACK_PERIOD = 256;\r\n/** Default ack advance window for server-side streams. */\r\nconst DEFAULT_ACK_ADVANCE = 128;\r\n\r\n/**\r\n * Server-side RPC stream producer — sends items to a remote consumer via\r\n * $sys.I / $sys.B / $sys.End system calls.\r\n *\r\n * Wire protocol (server perspective):\r\n * - Server returns the stream reference string as $sys.Ok\r\n * - Client sends Ack(0, hostId) to start the stream\r\n * - Server sends I (single item) / B (batch) messages\r\n * - Client acks every ackPeriod items for flow control\r\n * - Server sends End to signal completion (with optional error)\r\n * - Client sends AckEnd to acknowledge completion\r\n *\r\n * The sender uses a single ACK-driven loop:\r\n * - On each ACK, drain the source synchronously into the ring buffer up to\r\n * `effectiveBufferSize` (this lets us pre-buffer past `ackAdvance`).\r\n * - For real-time, optionally compact the unsent suffix to the latest\r\n * buffered `canSkipTo` item.\r\n * - Send items up to the `ackAdvance` flow-control window.\r\n * - Wait for the next ACK.\r\n *\r\n * Setting `bufferSize > ackAdvance` lets the sender pre-buffer items past\r\n * the in-flight window so a fresh ACK can be served from RAM instead of\r\n * waiting on the source.\r\n *\r\n * Direct port of .NET `RpcSharedStream<T>` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.\r\n */\r\nexport class RpcStreamSender<T> implements IRpcObject {\r\n private static readonly _emptyGuid = '00000000-0000-0000-0000-000000000000';\r\n\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Local;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n readonly canSkipTo: (item: T) => boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n /**\r\n * Wire-level flow-control window: the maximum index the sender may\r\n * advance past the most recently acknowledged index. Mirrors .NET\r\n * `RpcStream.AckAdvance`.\r\n */\r\n readonly ackAdvance: number;\r\n /**\r\n * Effective local ring buffer capacity used by the pump. Set larger\r\n * than `ackAdvance` to pre-buffer items beyond the in-flight window;\r\n * values below `ackAdvance` are clamped up (with a warning) at\r\n * construction time. Not transmitted on the wire.\r\n */\r\n readonly bufferSize: number;\r\n /**\r\n * If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`\r\n * and may honor the AbortSignal to exit gracefully. `disconnect()` first aborts\r\n * the signal and gives the source up to `RpcStream.disconnectGracePeriodMs` ms\r\n * before force-closing via `iterator.return()`.\r\n *\r\n * If false, the source is a plain AsyncIterable that cannot observe the signal,\r\n * so `disconnect()` force-closes immediately.\r\n */\r\n readonly sourceUsesAbortSignal: boolean;\r\n\r\n // -- Pump state --\r\n /** Next stream index that will be sent (matches .NET's `index`). */\r\n private _nextIndex = 0;\r\n private _ended = false;\r\n private _started = new PromiseSource<void>();\r\n /** Highest `nextIndex` observed in an ACK so far (matches .NET's `lastAckNextIndex` idiom). */\r\n private _lastAckedIndex = 0;\r\n private _abortController = new AbortController();\r\n private _iterator: AsyncIterator<T> | null = null;\r\n\r\n // -- Recorder-controller metrics (Step 9.1) --\r\n /** Total items skipped via real-time `canSkipTo` compaction. */\r\n private _skipCount = 0;\r\n /** Fires after a $sys.Ack has been processed (post-compaction). The\r\n * recorder's quality controller uses this to bump a \"last ACK at\"\r\n * watchdog so it can distinguish \"stuck\" (no ACK > N s) from\r\n * \"throttled\" (ACKs flowing). */\r\n onAckProcessed?: () => void;\r\n /** Fires after each item is pushed onto the local ring buffer. The\r\n * argument is the buffer count *after* the push. Together with\r\n * `onAckProcessed` (which is the only place items leave the buffer),\r\n * this gives controllers a complete picture of buffer utilization.\r\n * When `bufferedCount === bufferSize`, the source pull is paused\r\n * until the next ACK frees space. Listener errors are swallowed. */\r\n onBuffered?: (bufferedCount: number) => void;\r\n\r\n get nextIndex(): number {\r\n return this._nextIndex;\r\n }\r\n get lastAckIndex(): number {\r\n return this._lastAckedIndex;\r\n }\r\n get skipCount(): number {\r\n return this._skipCount;\r\n }\r\n\r\n // -- ACK queue (analog of .NET's Channel<(long, bool)>) --\r\n private _acks: { nextIndex: number; mustReset: boolean }[] = [];\r\n private _whenAckReady: PromiseSource<void> | null = null;\r\n\r\n constructor(\r\n peer: RpcPeer,\r\n ackPeriod = DEFAULT_ACK_PERIOD,\r\n ackAdvance = DEFAULT_ACK_ADVANCE,\r\n allowReconnect = true,\r\n isRealTime = false,\r\n canSkipTo: (item: T) => boolean = () => true,\r\n sourceUsesAbortSignal = false,\r\n bufferSize?: number,\r\n ) {\r\n const localId = peer.sharedObjects.nextId();\r\n this.id = { hostId: peer.hub.hubId, localId };\r\n this.allowReconnect = allowReconnect;\r\n this.isRealTime = isRealTime;\r\n this.canSkipTo = canSkipTo;\r\n this.peer = peer;\r\n this.ackPeriod = ackPeriod;\r\n this.ackAdvance = ackAdvance;\r\n this.sourceUsesAbortSignal = sourceUsesAbortSignal;\r\n\r\n if (bufferSize !== undefined && bufferSize !== 0 && bufferSize < ackAdvance)\r\n warnLog?.log(\r\n `RpcStream bufferSize (${bufferSize}) is below ackAdvance (${ackAdvance}); using ackAdvance as buffer size.`,\r\n );\r\n this.bufferSize = bufferSize !== undefined && bufferSize >= ackAdvance ? bufferSize : ackAdvance;\r\n }\r\n\r\n /** AbortSignal that is aborted when the sender is disconnected. */\r\n get abortSignal(): AbortSignal {\r\n return this._abortController.signal;\r\n }\r\n\r\n /** Returns the stream reference string for the $sys.Ok response. */\r\n toRef(): string {\r\n return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? '1' : '0'},${this.isRealTime ? '1' : '0'}`;\r\n }\r\n\r\n /**\r\n * Called by system call handler when $sys.Ack is received from the client.\r\n *\r\n * Mirrors .NET `RpcSharedStream<T>.OnAck` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:68-101.\r\n */\r\n onAck(nextIndex: number, hostId: string): void {\r\n const mustReset = hostId !== '' && hostId !== RpcStreamSender._emptyGuid;\r\n if (!this._started.isCompleted) this._started.resolve();\r\n this._acks.push({ nextIndex, mustReset });\r\n if (this._whenAckReady) {\r\n this._whenAckReady.resolve();\r\n this._whenAckReady = null;\r\n }\r\n }\r\n\r\n /** Called by system call handler when $sys.AckEnd is received from the client. */\r\n onAckEnd(_hostId: string): void {\r\n this.disconnect();\r\n }\r\n\r\n /**\r\n * Send a single item to the client at the current `_nextIndex`.\r\n *\r\n * Advances `_nextIndex` unconditionally — even if the peer is disconnected\r\n * the item is considered \"sent\" from the pump's perspective so the replay\r\n * buffer indexing stays consistent. If a connection is absent, the wire\r\n * message is dropped; the item will be resent from the replay buffer\r\n * after a reconnect (client sends `Ack(N, mustReset=true)`).\r\n */\r\n sendItem(item: T): void {\r\n if (this._ended) return;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.item(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, item,\r\n );\r\n }\r\n this._nextIndex++;\r\n }\r\n\r\n /** Send a batch of items to the client. See {@link sendItem} for disconnect semantics. */\r\n sendBatch(items: T[]): void {\r\n if (this._ended || items.length === 0) return;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.batch(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, items,\r\n );\r\n }\r\n this._nextIndex += items.length;\r\n }\r\n\r\n /** Signal stream completion to the client. */\r\n sendEnd(error?: Error | null): void {\r\n if (this._ended) return;\r\n this._ended = true;\r\n const conn = this.peer.connection;\r\n if (!conn) return;\r\n // .NET ExceptionInfo is a non-nullable value type, so we must always\r\n // emit a valid map shape (empty TypeRef+Message for the \"no error\" case).\r\n const errorInfo = error\r\n ? { TypeRef: 'System.Exception', Message: error.message }\r\n : { TypeRef: '', Message: '' };\r\n this.peer.hub.systemCallSender.end(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, errorInfo,\r\n );\r\n }\r\n\r\n /**\r\n * Consume an AsyncIterable and send all items to the client.\r\n *\r\n * Matches .NET `RpcSharedStream<T>.OnRun` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.\r\n */\r\n async writeFrom(source: AsyncIterable<T>): Promise<void> {\r\n await this._started.promise;\r\n if (this._ended) return;\r\n\r\n const iterator = source[Symbol.asyncIterator]();\r\n this._iterator = iterator;\r\n // Tracks whether the generator has reached a terminal state (done=true\r\n // or thrown). When true, its finally block has already run and the\r\n // iterator reference can be cleared. When false, disconnect()'s\r\n // scheduled iterator.return() is what drives it to its finally.\r\n const state: _PumpState = { iteratorDone: false };\r\n try {\r\n await this._run(iterator, state);\r\n } finally {\r\n if (state.iteratorDone && this._iterator === iterator) {\r\n this._iterator = null;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * ACK-driven pump. Non-real-time streams apply normal backpressure at\r\n * `ackAdvance`; real-time streams may compact the already-buffered unsent\r\n * suffix to the latest buffered `canSkipTo` item after the consumer ACKs.\r\n * With `bufferSize > ackAdvance` the local ring buffer holds more than the\r\n * in-flight window — real-time mode uses the extra space to pre-buffer.\r\n */\r\n /* eslint-disable @typescript-eslint/no-unnecessary-condition -- _ended changes across awaits */\r\n private async _run(\r\n iterator: AsyncIterator<T>,\r\n state: _PumpState,\r\n ): Promise<void> {\r\n const buffer = new RingBuffer<_StreamItem<T>>(this.bufferSize + 1);\r\n const isRealTime = this.isRealTime;\r\n let bufferStart = 0;\r\n let isFullyBuffered = false;\r\n let whenMovedNext: Promise<IteratorResult<T>> | null = null;\r\n const pending = Symbol('pending');\r\n\r\n const readNext = async (): Promise<IteratorResult<T>> => {\r\n const next = whenMovedNext ?? iterator.next();\r\n whenMovedNext = null;\r\n return await next;\r\n };\r\n\r\n const tryReadReady = async (): Promise<IteratorResult<T> | typeof pending> => {\r\n whenMovedNext ??= iterator.next();\r\n return await Promise.race([\r\n whenMovedNext,\r\n Promise.resolve(pending),\r\n ]);\r\n };\r\n\r\n const fireBuffered = (): void => {\r\n try {\r\n this.onBuffered?.(buffer.count);\r\n } catch { /* listener errors don't break the pump */ }\r\n };\r\n\r\n const bufferNext = async (onlyIfReady: boolean, prefetchNext: boolean): Promise<boolean> => {\r\n let r: IteratorResult<T> | typeof pending;\r\n try {\r\n r = onlyIfReady ? await tryReadReady() : await readNext();\r\n if (r === pending)\r\n return false;\r\n whenMovedNext = null;\r\n if (this._ended) return false;\r\n if (r.done) {\r\n buffer.pushTail(_endItem);\r\n isFullyBuffered = true;\r\n state.iteratorDone = true;\r\n } else {\r\n buffer.pushTail({ kind: 'value', value: r.value });\r\n if (prefetchNext)\r\n whenMovedNext = iterator.next();\r\n }\r\n fireBuffered();\r\n return true;\r\n } catch (e) {\r\n whenMovedNext = null;\r\n state.iteratorDone = true;\r\n isFullyBuffered = true;\r\n buffer.pushTail({\r\n kind: 'error',\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n fireBuffered();\r\n return true;\r\n }\r\n };\r\n\r\n while (true) {\r\n // ---- nextAck ----\r\n // 1. Await for an acknowledgement & process accumulated ACKs.\r\n let ack = this._tryProcessAcks();\r\n if (!ack) {\r\n await this._waitAckReady();\r\n if (this._ended) return;\r\n ack = this._tryProcessAcks();\r\n if (!ack) {\r\n warnLog?.log(\"Something is off: couldn't read an acknowledgement\");\r\n return;\r\n }\r\n }\r\n this._lastAckedIndex = ack.nextIndex;\r\n\r\n // 1.1. Reconnect: real-time streams can clear stale buffered data\r\n // and restart from the next source item accepted by canSkipTo.\r\n if (isRealTime && ack.mustReset && !isFullyBuffered) {\r\n buffer.clear();\r\n bufferStart = this._nextIndex;\r\n while (true) {\r\n if (this._ended) return;\r\n let item: _StreamItem<T>;\r\n let accepted = false;\r\n try {\r\n const r = await readNext();\r\n if (this._ended) return;\r\n if (r.done) {\r\n item = _endItem;\r\n isFullyBuffered = true;\r\n state.iteratorDone = true;\r\n } else if (this.canSkipTo(r.value)) {\r\n item = { kind: 'value', value: r.value };\r\n accepted = true;\r\n } else {\r\n continue; // discard; keep draining\r\n }\r\n } catch (e) {\r\n state.iteratorDone = true;\r\n isFullyBuffered = true;\r\n item = {\r\n kind: 'error',\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n };\r\n }\r\n buffer.pushTail(item);\r\n fireBuffered();\r\n if (accepted || isFullyBuffered) break;\r\n }\r\n }\r\n\r\n // 2. Remove what's useless from the buffer.\r\n const shift = _clamp(ack.nextIndex - bufferStart, 0, buffer.count);\r\n if (shift > 0) {\r\n buffer.moveHead(shift);\r\n bufferStart += shift;\r\n }\r\n\r\n // 3. Recalculate the next range to send.\r\n if (this._nextIndex < bufferStart) {\r\n if (!this._ended)\r\n this.sendEnd(new Error('Stream position unavailable.'));\r\n return;\r\n }\r\n let bufferIndex = this._nextIndex - bufferStart;\r\n\r\n // 3. Send as much as the current ACK window allows.\r\n const maxIndex = ack.nextIndex + this.ackAdvance;\r\n while (this._nextIndex < maxIndex) {\r\n if (this._ended) return;\r\n\r\n if (isRealTime) {\r\n // Real-time: pre-buffer aggressively up to the ring's\r\n // capacity (which may exceed ackAdvance when bufferSize\r\n // is set), so a freshly arrived ACK is served from RAM.\r\n while (buffer.hasRemainingCapacity && !isFullyBuffered && this._acks.length === 0) {\r\n if (!await bufferNext(true, true))\r\n break;\r\n }\r\n if (bufferIndex >= buffer.count\r\n && !isFullyBuffered\r\n && buffer.hasRemainingCapacity\r\n && this._acks.length === 0) {\r\n await bufferNext(false, true);\r\n }\r\n if (ack.nextIndex > 0) {\r\n const result = _compactBufferedUnsentSuffix(\r\n buffer, bufferIndex, this.canSkipTo);\r\n bufferIndex = result.newIndex;\r\n if (result.skipped > 0)\r\n this._skipCount += result.skipped;\r\n }\r\n\r\n // Bail out so step 1 can process a mustReset ACK immediately.\r\n if (this._acks.length > 0) break;\r\n }\r\n\r\n // Drain buffered items until the window is full.\r\n while (this._nextIndex < maxIndex && bufferIndex < buffer.count) {\r\n const item = buffer.get(bufferIndex++);\r\n if (item.kind === 'value') {\r\n this.sendItem(item.value);\r\n } else if (item.kind === 'end') {\r\n if (!this._ended) this.sendEnd();\r\n return;\r\n } else {\r\n if (!this._ended) this.sendEnd(item.error);\r\n return;\r\n }\r\n }\r\n if (this._nextIndex >= maxIndex)\r\n break;\r\n if (isFullyBuffered) break;\r\n if (!buffer.hasRemainingCapacity) break;\r\n\r\n if (!isRealTime)\r\n await bufferNext(false, false);\r\n }\r\n }\r\n }\r\n /* eslint-enable @typescript-eslint/no-unnecessary-condition */\r\n\r\n /** Drain all queued ACKs, returning the most recent one (or null if none). */\r\n private _tryProcessAcks(): { nextIndex: number; mustReset: boolean } | null {\r\n if (this._acks.length === 0) return null;\r\n let last: { nextIndex: number; mustReset: boolean } | null = null;\r\n while (this._acks.length > 0) {\r\n const a = this._acks.shift()!;\r\n last = a;\r\n if (a.mustReset || this._nextIndex < a.nextIndex) {\r\n this._nextIndex = a.nextIndex;\r\n }\r\n }\r\n try {\r\n this.onAckProcessed?.();\r\n } catch { /* listener errors don't break the pump */ }\r\n return last;\r\n }\r\n\r\n /** Wait for the ACK queue to have at least one entry (or `_ended`). */\r\n private async _waitAckReady(): Promise<void> {\r\n if (this._acks.length > 0 || this._ended) return;\r\n this._whenAckReady = new PromiseSource<void>();\r\n await this._whenAckReady.promise;\r\n this._whenAckReady = null;\r\n }\r\n\r\n // -- IRpcObject --\r\n\r\n reconnect(): void {\r\n // Server-side streams don't support reconnection — the client\r\n // creates a new stream on reconnect.\r\n }\r\n\r\n disconnect(): void {\r\n if (this._ended) return;\r\n this._ended = true;\r\n this._abortController.abort();\r\n if (!this._started.isCompleted) {\r\n this._started.resolve();\r\n }\r\n if (this._whenAckReady) {\r\n this._whenAckReady.resolve();\r\n this._whenAckReady = null;\r\n }\r\n this.peer.sharedObjects.unregister(this);\r\n\r\n // Iterator force-close policy:\r\n // - Plain AsyncIterable: abortSignal is unobserved, so force-close now.\r\n // - Factory source (abortSignal-aware): give it a grace period to exit\r\n // gracefully, then force-close if still pumping.\r\n const iterator = this._iterator;\r\n if (!iterator) return;\r\n if (!this.sourceUsesAbortSignal) {\r\n this._iterator = null;\r\n this._forceCloseIterator(iterator);\r\n return;\r\n }\r\n setTimeout(() => {\r\n if (this._iterator === iterator) {\r\n this._iterator = null;\r\n this._forceCloseIterator(iterator);\r\n }\r\n }, RpcStream.disconnectGracePeriodMs);\r\n }\r\n\r\n private _forceCloseIterator(iterator: AsyncIterator<T>): void {\r\n try {\r\n const ret = iterator.return?.(undefined);\r\n if (ret) void ret.catch(() => { /* ignore */ });\r\n } catch { /* ignore */ }\r\n }\r\n}\r\n\r\n// -- Internal helpers (file-local) --\r\n\r\n/** Wrapper matching .NET's `Result<T>` — value, end-of-stream, or error. */\r\ntype _StreamItem<T> =\r\n | { kind: 'value'; value: T }\r\n | { kind: 'end' }\r\n | { kind: 'error'; error: Error };\r\n\r\n/** Singleton \"source exhausted\" marker (matches .NET's `NoMoreItemsTag`). */\r\nconst _endItem: _StreamItem<never> = { kind: 'end' };\r\n\r\n/** Shared mutable state between writeFrom and its per-mode pump. */\r\ninterface _PumpState {\r\n iteratorDone: boolean;\r\n}\r\n\r\nfunction _clamp(value: number, min: number, max: number): number {\r\n return value < min ? min : value > max ? max : value;\r\n}\r\n\r\nfunction _compactBufferedUnsentSuffix<T>(\r\n buffer: RingBuffer<_StreamItem<T>>,\r\n firstUnsentIndex: number,\r\n canSkipTo: (item: T) => boolean,\r\n): { newIndex: number; skipped: number } {\r\n // Keep items that have already been assigned RPC stream indexes, then\r\n // collapse the unsent suffix to the latest buffered restart point. The\r\n // surviving suffix is intentionally sent under fresh RPC stream indexes;\r\n // item-level timestamps/frame indexes are the caller's responsibility.\r\n let skipToIndex = -1;\r\n for (let i = firstUnsentIndex; i < buffer.count; i++) {\r\n const item = buffer.get(i);\r\n if (item.kind === 'value' && canSkipTo(item.value))\r\n skipToIndex = i;\r\n }\r\n if (skipToIndex <= firstUnsentIndex)\r\n return { newIndex: firstUnsentIndex, skipped: 0 };\r\n\r\n const cutFrom = firstUnsentIndex;\r\n const cutTo = skipToIndex;\r\n\r\n const items = buffer.toArray();\r\n buffer.clear();\r\n for (let i = 0; i < cutFrom; i++)\r\n buffer.pushTail(items[i]);\r\n for (let i = cutTo; i < items.length; i++)\r\n buffer.pushTail(items[i]);\r\n return { newIndex: firstUnsentIndex, skipped: skipToIndex - firstUnsentIndex };\r\n}\r\n","/**\n * Minimal base64 ↔ Uint8Array helpers used by the `$sys.Reconnect` protocol\n * (byte arrays are encoded as base64 strings on the JSON wire to match\n * .NET's default `System.Text.Json` serialization of `byte[]`).\n */\n\nexport function base64Encode(bytes: Uint8Array): string {\n // btoa works on latin1 strings; chunk to avoid \"Maximum call stack size exceeded\"\n // when building the intermediate string for large inputs.\n const chunkSize = 0x8000;\n let binary = '';\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(\n ...(bytes.subarray(i, Math.min(bytes.length, i + chunkSize)) as unknown as number[]),\n );\n }\n return btoa(binary);\n}\n\nexport function base64Decode(encoded: string): Uint8Array {\n const binary = atob(encoded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n","// .NET counterpart:\n// RpcSystemCalls (239 lines) — implements IRpcSystemCalls interface; handles\n// all inbound system messages as regular RPC method calls dispatched through\n// the same inbound-call pipeline as user calls.\n//\n// Omitted from .NET:\n// - Reconnect() handler — receives compressed call-ID sets grouped by stage,\n// looks up each inbound call, calls TryReprocess(), and returns the IDs of\n// unknown calls. TS doesn't implement the Reliable reconnection protocol.\n// - Cancel() handler — .NET finds the inbound call and cancels its\n// CancellationTokenSource, aborting the running handler. TS removes the\n// inbound call from the tracker but does not yet propagate cancellation\n// to the service handler (no AbortSignal threading through dispatch).\n// - M() (Match) handler — tells an outbound call to use its cached result\n// instead of the response payload. TS has no response caching.\n// - NotFound() — throws an EndpointNotFound error. TS sends this as a regular\n// $sys.Error; no separate system call type.\n// - KeepAlive() / Disconnect() — manage RpcSharedObject lifetimes. TS has no\n// shared-object tracker.\n// - IRpcPolymorphicArgumentHandler.IsValidCall — resolves the concrete\n// deserialization type for polymorphic Ok/Item/Batch arguments by looking up\n// the related outbound call's return type. TS deserializes all args as\n// unknown via JSON.parse (inherently polymorphic).\n// - DI / IServiceProvider / RpcServiceBase — .NET system calls are resolved via\n// DI. TS uses a class with a handle() method.\n\nimport { getLogs } from './logging.js';\nimport { RpcError } from './rpc-error.js';\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\nimport type { RpcPeer } from './rpc-peer.js';\nimport type { RpcStream } from './rpc-stream.js';\nimport { resolveStreamRefs } from './rpc-stream.js';\nimport type { RpcStreamSender } from './rpc-stream-sender.js';\nimport { RpcCallStage } from './rpc-call-stage.js';\nimport { IncreasingSeqCompressor } from './increasing-seq-compressor.js';\nimport { base64Decode, base64Encode } from './base64.js';\n\nconst { debugLog, warnLog } = getLogs('RpcSystemCallHandler');\n\n/** Handles incoming system call messages — class-based equivalent of the former standalone function. */\nexport class RpcSystemCallHandler {\n handle(message: RpcMessage, args: unknown[], peer: RpcPeer): void {\n const method = message.Method;\n const relatedId = message.RelatedId ?? 0;\n debugLog?.log(`'${peer.ref}': handle ${method}#${relatedId}`);\n\n switch (method) {\n case RpcSystemCalls.ok: {\n const call = peer.outboundCalls.get(relatedId);\n if (call !== undefined) {\n call.completedStage |= RpcCallStage.ResultReady;\n if (call.removeOnOk) {\n peer.outboundCalls.remove(relatedId);\n }\n call.result.resolve(args[0]);\n }\n break;\n }\n case RpcSystemCalls.reconnect: {\n // Server-side `$sys.Reconnect` handler. Computes the set of call\n // IDs the inbound tracker doesn't know about, replies with a\n // byte[] result via $sys.Ok. Mirrors .NET `RpcSystemCalls.Reconnect`\n // at src/ActualLab.Rpc/Infrastructure/RpcSystemCalls.cs:51-85.\n //\n // Limitations vs .NET: does not validate `handshakeIndex` against\n // this peer's own handshake, and does not perform per-stage\n // re-processing of compute calls (TS has no compute-invalidation\n // tracking on the inbound side). A call ID is reported as\n // \"known\" iff it is still in the inbound tracker.\n this._handleReconnect(relatedId, args, peer);\n break;\n }\n case RpcSystemCalls.error: {\n const call = peer.outboundCalls.remove(relatedId);\n if (call !== undefined) {\n const errorInfo = args[0] as\n | Record<string, unknown>\n | undefined;\n const msg = (errorInfo?.Message ??\n errorInfo?.message ??\n 'RPC error') as string;\n const errorType = errorInfo?.TypeRef ?? errorInfo?.typeRef;\n let typeName: string | undefined;\n if (typeof errorType === 'string') {\n // .NET TypeRef is serialized as the assembly-qualified name string,\n // e.g. \"System.InvalidOperationException, System.Private.CoreLib\".\n // Mirrors .NET TypeRef.TypeName: substring before the first ','.\n const commaIdx = errorType.indexOf(',');\n typeName = commaIdx >= 0 ? errorType.slice(0, commaIdx) : errorType;\n } else if (errorType !== null && typeof errorType === 'object') {\n const t = errorType as Record<string, unknown>;\n typeName = (t.TypeName ?? t.typeName) as string | undefined;\n }\n // Mirrors RpcSystemCalls.cs:102 — surface RpcRerouteException.\n if (typeName === 'ActualLab.Rpc.RpcRerouteException')\n warnLog?.log('Got RpcRerouteException from remote peer:', msg);\n call.result.reject(new RpcError(msg, typeName));\n }\n break;\n }\n case RpcSystemCalls.cancel: {\n // Remote peer is cancelling a call it asked us to process — remove\n // from the inbound tracker. Full cancellation propagation (aborting\n // the running service handler) is not yet implemented; this just\n // unregisters the call so we don't send a response for it.\n peer.inboundCalls.remove(relatedId);\n break;\n }\n case RpcSystemCalls.keepAlive: {\n // Remote keep-alive — mark the peer as \"alive\" so the peer's\n // watchdog doesn't force-close the connection. Mirrors .NET\n // `RpcObjectTrackers.KeepAlive` which sets `LastKeepAliveAt`.\n peer.notifyKeepAliveReceived();\n break;\n }\n case RpcSystemCalls.item: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n // Silent no-op when no stream — mirrors RpcSystemCalls.cs:164-170.\n // This is normal during reconnect: the server may send items for\n // streams the client has already disposed.\n if (!stream)\n debugLog?.log(`$sys.I: no stream for relatedId=${relatedId}`);\n else\n stream.onItem(\n args[0] as number,\n resolveStreamRefs(args[1], peer)\n );\n break;\n }\n case RpcSystemCalls.batch: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n if (!stream)\n debugLog?.log(`$sys.B: no stream for relatedId=${relatedId}`);\n else if (Array.isArray(args[1])) {\n const items = args[1] as unknown[];\n for (let i = 0; i < items.length; i++)\n items[i] = resolveStreamRefs(items[i], peer);\n stream.onBatch(args[0] as number, items);\n }\n break;\n }\n case RpcSystemCalls.end: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n if (!stream) {\n debugLog?.log(`$sys.End: no stream for relatedId=${relatedId}`);\n } else {\n // .NET ExceptionInfo is a struct — even for normal completion, it serializes\n // as a non-null object with empty fields (e.g. { \"message\": \"\", \"typeRef\": {...} }).\n // Check both PascalCase and camelCase, and treat empty messages as no error.\n const errorInfo = args[1] as Record<string, unknown> | null;\n const msg = (errorInfo?.Message ?? errorInfo?.message) as\n | string\n | undefined;\n const error = msg ? new Error(msg) : null;\n stream.onEnd(args[0] as number, error);\n }\n break;\n }\n case RpcSystemCalls.ack: {\n const sender = peer.sharedObjects.get(relatedId) as\n | RpcStreamSender<unknown>\n | undefined;\n sender?.onAck(args[0] as number, args[1] as string);\n break;\n }\n case RpcSystemCalls.ackEnd: {\n const sender = peer.sharedObjects.get(relatedId) as\n | RpcStreamSender<unknown>\n | undefined;\n sender?.onAckEnd(args[0] as string);\n break;\n }\n case RpcSystemCalls.disconnect: {\n // Server is telling us the listed remote object IDs have been torn down\n // on its side (e.g. shared stream closed). Propagate disconnect() to the\n // matching remote objects so any pending consumers (for-await loops) exit\n // cleanly instead of hanging.\n const ids = args[0] as number[] | undefined;\n if (Array.isArray(ids)) {\n for (const id of ids) {\n const remoteObj = peer.remoteObjects.get(id);\n if (\n remoteObj &&\n typeof remoteObj.disconnect === 'function'\n ) {\n remoteObj.disconnect();\n }\n // Also check shared objects — server may disconnect a client-to-server\n // stream sender (e.g. audio stream) after pod restart or timeout.\n const sharedObj = peer.sharedObjects.get(id);\n if (\n sharedObj &&\n typeof sharedObj.disconnect === 'function'\n ) {\n sharedObj.disconnect();\n }\n }\n }\n break;\n }\n }\n }\n\n /**\n * Server-side handler for the `$sys.Reconnect` call. Parses the peer's\n * report of in-flight call IDs (grouped by stage, IncreasingSeqCompressor-\n * encoded, base64-wrapped for JSON transport), computes the subset this\n * peer does not recognize, and replies with a compressed byte[] of the\n * unknown IDs wrapped in `$sys.Ok`.\n *\n * @param relatedId The outbound call ID the client used for this\n * `$sys.Reconnect` invocation — we send `$sys.Ok` back\n * with the same `RelatedId`.\n */\n private _handleReconnect(relatedId: number, args: unknown[], peer: RpcPeer): void {\n if (peer.connection === undefined) return;\n\n // args[0]: handshakeIndex (number) — not validated in TS, see class comment.\n // args[1]: completedStagesData — shape depends on wire format.\n // JSON: { [stage: string]: base64-string }\n // msgpack: Map-like with int keys and Uint8Array values (unsupported in TS).\n let unknownIds: number[] = [];\n try {\n const stagesRaw = args[1];\n if (stagesRaw && typeof stagesRaw === 'object' && !Array.isArray(stagesRaw)) {\n const unknownSet = new Set<number>();\n for (const [, rawValue] of Object.entries(stagesRaw as Record<string, unknown>)) {\n const bytes = _toBytes(rawValue);\n if (bytes === null) continue;\n const callIds = IncreasingSeqCompressor.deserialize(bytes);\n for (const callId of callIds) {\n if (peer.inboundCalls.get(callId) === undefined) {\n unknownSet.add(callId);\n }\n }\n }\n unknownIds = [...unknownSet].sort((a, b) => a - b);\n }\n } catch {\n // If parsing fails, report all as unknown (safest behavior — client\n // will resend everything).\n // Client handles this via its own try/catch around the reconcile call.\n }\n\n const responseBytes = IncreasingSeqCompressor.serialize(unknownIds);\n const responseValue = peer.serializationFormat.isBinary ? responseBytes : base64Encode(responseBytes);\n peer.hub.systemCallSender.ok(peer.connection, peer.serializationFormat, relatedId, responseValue);\n }\n}\n\n/**\n * Coerce a wire value carrying a byte[] into a Uint8Array.\n * - JSON format sends byte[] as a base64 string.\n * - msgpack format sends byte[] as native `bin` (Uint8Array).\n * Returns `null` if the value is neither.\n */\nfunction _toBytes(value: unknown): Uint8Array | null {\n if (value instanceof Uint8Array) return value;\n if (typeof value === 'string') {\n try { return base64Decode(value); } catch { return null; }\n }\n return null;\n}\n","// .NET counterpart:\r\n// RpcSystemCallSender (202 lines) — a DI-registered service that sends system\r\n// calls by creating RpcOutboundContext + calling PrepareCallForSendNoWait +\r\n// serialising via the full outbound message pipeline.\r\n//\r\n// Omitted from .NET:\r\n// - Complete<TResult>() / typed Ok<TResult>() — .NET serialises the Ok result\r\n// using the method's unwrapped return type for proper polymorphic handling.\r\n// TS uses JSON.stringify(result) which handles any type uniformly.\r\n// - Match() — sends $sys.M when the response hash matches a cached entry,\r\n// avoiding re-sending the full payload. TS has no response caching.\r\n// - NotFound() — TS sends \"Service not found\" / \"Method not found\" as $sys.Error.\r\n// - Disconnect() — sends $sys.Disconnect with object IDs for shared-object\r\n// lifetime management. TS has no shared-object tracker.\r\n// - Ack() / AckEnd() / Item<T>() / Batch<T>() / End() — stream control\r\n// messages for RpcStream. TS has no streaming support yet.\r\n// - RpcOutboundContext / PrepareCallForSendNoWait pipeline — .NET creates a full\r\n// outbound context with MethodDef, ArgumentList, etc., then serialises via the\r\n// peer's MessageSerializer. TS directly calls serializeMessage() (JSON) since\r\n// there's only one serialization format.\r\n// - Tracing / CallLogger integration — .NET logs each system call through the\r\n// peer's CallLogger. TS has no tracing infrastructure.\r\n// - StopMode-aware Error() — .NET checks peer.StopMode to decide whether to\r\n// suppress error responses when the peer is shutting down. TS has no stop\r\n// mode concept.\r\n\r\nimport { getLogs } from './logging.js';\r\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\r\nimport type { RpcConnection } from './rpc-connection.js';\r\nimport type { RpcSerializationFormat } from './rpc-serialization-format.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\nconst { errorLog } = getLogs('RpcSystemCallSender');\r\n\r\n/**\r\n * Sends system RPC messages — like .NET's RpcSystemCallSender.\r\n *\r\n * Hot-path allocation notes: `item`, `batch`, `ack`, and `end` are called\r\n * once per stream frame (or once per ack window). Naively building a fresh\r\n * `{ Method, RelatedId }` envelope object plus a fresh two-element arg array\r\n * on every call showed up as ~2-4% self time + non-trivial GC pressure in\r\n * the 300-pull video-load-test profile.\r\n *\r\n * The sender itself is a per-Hub singleton and `_send`/format.serializeMessage\r\n * are fully synchronous (no awaits, no re-entrancy on a single-threaded event\r\n * loop), so we reuse a small pool of preallocated envelope + arg-array slots\r\n * per hot method. Mutate fields just before the send call; the encoder reads\r\n * them synchronously and produces a detached copy, after which the slot is\r\n * free to be mutated again on the next call.\r\n */\r\nexport class RpcSystemCallSender {\r\n // Reusable envelopes — one per hot-path method.\r\n private readonly _itemEnv: RpcMessage = {\r\n Method: RpcSystemCalls.item,\r\n RelatedId: 0,\r\n };\r\n private readonly _batchEnv: RpcMessage = {\r\n Method: RpcSystemCalls.batch,\r\n RelatedId: 0,\r\n };\r\n private readonly _endEnv: RpcMessage = {\r\n Method: RpcSystemCalls.end,\r\n RelatedId: 0,\r\n };\r\n private readonly _ackEnv: RpcMessage = {\r\n Method: RpcSystemCalls.ack,\r\n RelatedId: 0,\r\n };\r\n private readonly _ackEndEnv: RpcMessage = {\r\n Method: RpcSystemCalls.ackEnd,\r\n RelatedId: 0,\r\n };\r\n\r\n // Reusable arg arrays.\r\n private readonly _itemArgs: [number, unknown] = [0, null];\r\n private readonly _batchArgs: [number, unknown] = [0, null];\r\n private readonly _endArgs: [number, { TypeRef: string; Message: string }] =\r\n [0, { TypeRef: '', Message: '' }];\r\n private readonly _ackArgs: [number, string] = [0, ''];\r\n private readonly _ackEndArgs: [string] = [''];\r\n\r\n private static readonly NO_ERROR: { TypeRef: string; Message: string } =\r\n Object.freeze({ TypeRef: '', Message: '' }) as unknown as {\r\n TypeRef: string;\r\n Message: string;\r\n };\r\n\r\n registry?: RpcMethodRegistry;\r\n\r\n private _send(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n envelope: RpcMessage,\r\n args?: unknown[]\r\n ): void {\r\n const wireData = format.serializeMessage(\r\n envelope,\r\n args,\r\n conn.encoder,\r\n this.registry\r\n );\r\n if (typeof wireData === 'string') {\r\n conn.send(wireData);\r\n } else {\r\n conn.sendBinary(wireData);\r\n }\r\n }\r\n\r\n handshake(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n peerId: string,\r\n hubId: string,\r\n index: number\r\n ): void {\r\n // RpcHandshake uses [MessagePackObject] array mode for binary.\r\n const handshakeArg = format.isBinary\r\n ? [peerId, null, hubId, 2, index]\r\n : {\r\n RemotePeerId: peerId,\r\n RemoteApiVersionSet: null,\r\n RemoteHubId: hubId,\r\n ProtocolVersion: 2,\r\n Index: index,\r\n };\r\n this._send(conn, format, { Method: RpcSystemCalls.handshake }, [\r\n handshakeArg,\r\n ]);\r\n }\r\n\r\n ok(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number,\r\n result: unknown\r\n ): void {\r\n try {\r\n this._send(\r\n conn,\r\n format,\r\n { Method: RpcSystemCalls.ok, RelatedId: relatedId },\r\n [result]\r\n );\r\n }\r\n catch (e) {\r\n // Mirrors RpcSystemCallSender.cs:104 — \"Failed to send Ok response\".\r\n errorLog?.log(`Failed to send Ok response for call #${relatedId}`, e);\r\n this.error(conn, format, relatedId, e);\r\n }\r\n }\r\n\r\n error(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number,\r\n error: unknown\r\n ): void {\r\n const message = error instanceof Error ? error.message : String(error);\r\n this._send(\r\n conn,\r\n format,\r\n { Method: RpcSystemCalls.error, RelatedId: relatedId },\r\n [{ Message: message }]\r\n );\r\n }\r\n\r\n cancel(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number\r\n ): void {\r\n this._send(conn, format, {\r\n Method: RpcSystemCalls.cancel,\r\n RelatedId: relatedId,\r\n });\r\n }\r\n\r\n keepAlive(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n activeCallIds: number[]\r\n ): void {\r\n this._send(conn, format, { Method: RpcSystemCalls.keepAlive }, [\r\n activeCallIds,\r\n ]);\r\n }\r\n\r\n item(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n item: unknown\r\n ): void {\r\n this._itemEnv.RelatedId = localId;\r\n this._itemArgs[0] = index;\r\n this._itemArgs[1] = item;\r\n this._send(conn, format, this._itemEnv, this._itemArgs);\r\n this._itemArgs[1] = null;\r\n }\r\n\r\n batch(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n items: unknown[]\r\n ): void {\r\n this._batchEnv.RelatedId = localId;\r\n this._batchArgs[0] = index;\r\n this._batchArgs[1] = items;\r\n this._send(conn, format, this._batchEnv, this._batchArgs);\r\n this._batchArgs[1] = null;\r\n }\r\n\r\n end(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n error: {\r\n TypeRef: string;\r\n Message: string;\r\n } = RpcSystemCallSender.NO_ERROR\r\n ): void {\r\n this._endEnv.RelatedId = localId;\r\n this._endArgs[0] = index;\r\n this._endArgs[1] = error;\r\n this._send(conn, format, this._endEnv, this._endArgs);\r\n }\r\n\r\n ack(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n nextIndex: number,\r\n hostId: string\r\n ): void {\r\n this._ackEnv.RelatedId = localId;\r\n this._ackArgs[0] = nextIndex;\r\n this._ackArgs[1] = hostId;\r\n this._send(conn, format, this._ackEnv, this._ackArgs);\r\n }\r\n\r\n ackEnd(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n hostId: string\r\n ): void {\r\n this._ackEndEnv.RelatedId = localId;\r\n this._ackEndArgs[0] = hostId;\r\n this._send(conn, format, this._ackEndEnv, this._ackEndArgs);\r\n }\r\n}\r\n","import type { IRpcObject } from './rpc-object.js';\r\n\r\nexport class RpcRemoteObjectTracker {\r\n private _objects = new Map<number, IRpcObject>();\r\n\r\n register(obj: IRpcObject): void {\r\n this._objects.set(obj.id.localId, obj);\r\n }\r\n\r\n get(localId: number): IRpcObject | undefined {\r\n return this._objects.get(localId);\r\n }\r\n\r\n keys(): IterableIterator<number> {\r\n return this._objects.keys();\r\n }\r\n\r\n unregister(obj: IRpcObject): void {\r\n this._objects.delete(obj.id.localId);\r\n }\r\n\r\n disconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n obj.disconnect();\r\n }\r\n this._objects.clear();\r\n }\r\n\r\n reconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n if (obj.allowReconnect) obj.reconnect();\r\n else obj.disconnect();\r\n }\r\n }\r\n}\r\n","import type { IRpcObject } from './rpc-object.js';\r\n\r\nexport class RpcSharedObjectTracker {\r\n private _nextId = 1;\r\n private _objects = new Map<number, IRpcObject>();\r\n\r\n nextId(): number {\r\n return this._nextId++;\r\n }\r\n\r\n register(obj: IRpcObject): void {\r\n this._objects.set(obj.id.localId, obj);\r\n }\r\n\r\n get(localId: number): IRpcObject | undefined {\r\n return this._objects.get(localId);\r\n }\r\n\r\n keys(): IterableIterator<number> {\r\n return this._objects.keys();\r\n }\r\n\r\n unregister(obj: IRpcObject): void {\r\n this._objects.delete(obj.id.localId);\r\n }\r\n\r\n disconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n obj.disconnect();\r\n }\r\n this._objects.clear();\r\n }\r\n}\r\n","// .NET counterparts:\r\n// RpcPeer (529 lines) — abstract WorkerBase that runs a reconnection loop in\r\n// OnRun(). Manages AsyncState<RpcPeerConnectionState> (a linked list of\r\n// immutable connection snapshots), handshake exchange, peerChanged detection,\r\n// message processing loop, maintenance tasks, Reset(), and SetConnectionState()\r\n// with thread-safe locking.\r\n// RpcClientPeer (56 lines) — calls GetConnection → WebSocket transport.\r\n// RpcServerPeer (54 lines) — receives connection via RpcServerConnectionHandler.\r\n// RpcPeerConnectionKind (48 lines) — None/Remote/Loopback/Local enum.\r\n// RpcPeerConnectionState (42 lines) — immutable record: Connection, Transport,\r\n// Handshake, OwnHandshake, ReaderTokenSource, Error, TryIndex.\r\n// RpcPeerChangeKind (11 lines) — Unchanged/ChangedToVeryFirst/Changed enum.\r\n// RpcHandshake (28 lines) — PeerId, ApiVersionSet, HubId, ProtocolVersion,\r\n// Index. Has GetPeerChangeKind(lastHandshake) comparison method.\r\n// RpcClientPeerReconnectDelayer (35 lines) — exponential backoff profiles:\r\n// test (50ms), server (250ms), client (1s) with 1.5x growth, max 10s.\r\n//\r\n// Ported from .NET:\r\n// - Handshake exchange — run() sends $sys.Handshake on connect, waits for the\r\n// server's response. RpcServerPeer._onHandshakeReceived() auto-replies.\r\n// Like .NET's OnRun() handshake sequence.\r\n// - Peer change detection — run() compares RemoteHubId from successive\r\n// handshakes to detect server restarts, triggering peerChanged + inbound.clear().\r\n// Like .NET's RpcHandshake.GetPeerChangeKind().\r\n// - Outbound call cancellation via AbortSignal — call(signal) registers an\r\n// abort listener that rejects the call, removes it from the tracker, and sends\r\n// $sys.Cancel to the server. Like .NET's CancellationHandler + NotifyCancelled().\r\n//\r\n// Omitted from .NET:\r\n// - AsyncState<RpcPeerConnectionState> — immutable linked-list of connection\r\n// states that consumers can await via WhenConnected/WhenDisconnected. TS uses\r\n// simpler EventHandlerSet events (connected/disconnected). AsyncState enables\r\n// callers to block until a specific state transition, which is essential in\r\n// .NET's multi-threaded world but unnecessary in TS's single-threaded model.\r\n// - RpcPeerConnectionState record — snapshots Connection + Handshake + OwnHandshake\r\n// + ReaderTokenSource + Error + TryIndex. TS tracks these as mutable fields\r\n// on the peer directly.\r\n// - Maintenance tasks (SharedObjects.Maintain, RemoteObjects.Maintain,\r\n// OutboundCalls.Maintain) — background loops that run while connected,\r\n// checking timeouts and managing object lifetimes. TS has simpler keep-alive\r\n// timer; no shared/remote object tracking, no call timeout monitoring.\r\n// - Reconnect protocol (OutboundCalls.Reconnect) — stage-based call resumption\r\n// after reconnection. TS uses transparent reconnect: outbound calls survive\r\n// disconnect, are re-sent on reconnect (stage-3 compute calls self-invalidate\r\n// on peer change instead of being re-sent).\r\n// - peerChangedToken / CancellationTokenSource — .NET threads a cancellation\r\n// token through all inbound calls that gets cancelled when the remote peer\r\n// changes identity. TS clears the inbound tracker on peer change;\r\n// outbound calls survive disconnect and are re-sent on reconnect.\r\n// - Reset() — aborts RemoteObjects, SharedObjects, OutboundCalls, and clears\r\n// InboundCalls. TS clears inbound tracker on peer change; outbound calls\r\n// are never rejected on disconnect (only on close/stop).\r\n// - ServerMethodResolver / GetServerMethodResolver(handshake) — resolves method\r\n// defs using the remote peer's API version set + legacy names. TS has no\r\n// versioning.\r\n// - ProcessMessage() — creates RpcInboundContext per message, dispatches via\r\n// MethodDef.InboundCallInvoker. TS dispatches via _handleMessage →\r\n// handleSystemCall or RpcServiceHost.dispatch.\r\n// - RpcPeerRef / routing — .NET peers are keyed by RpcPeerRef (which encodes\r\n// client/server, route state, versions, serialization format). TS peers are\r\n// keyed by string ref (URL for clients, \"server://{uuid}\" for servers).\r\n// - StopMode / ComputeAutoStopMode — controls behavior of inbound calls when\r\n// peer stops (cancel vs keep-incomplete). TS has no stop mode.\r\n// - ConnectionKind detector (Remote/Loopback/Local/None) — .NET detects if peer\r\n// is in-process. TS is always remote.\r\n// - Versions / VersionSet / API version negotiation — .NET exchanges version\r\n// sets during handshake for backward compatibility. TS has no versioning.\r\n// - Diagnostics (CallLogger, CallLogLevel, DebugLog) — per-peer logging/tracing.\r\n// - Per-call timeout monitoring (OutboundCallTracker.Maintain) — .NET checks\r\n// elapsed time against per-method timeouts. TS relies on WebSocket-level\r\n// disconnect + rejectAll.\r\n// - Inbound call cancellation propagation — when $sys.Cancel is received, .NET\r\n// cancels the inbound call's CancellationTokenSource, aborting the running\r\n// service method. TS removes the call from the inbound tracker but does not\r\n// yet propagate cancellation to the service handler (would require AbortSignal\r\n// threading through RpcServiceHost.dispatch).\r\n\r\nimport { EventHandlerSet, PromiseSource, TimeoutError, withTimeout } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport {\r\n RpcWebSocketConnection,\r\n type RpcConnection,\r\n type RpcReceivedMessage,\r\n type WebSocketLike,\r\n} from './rpc-connection.js';\r\nimport {\r\n RpcOutboundCall,\r\n RpcOutboundCallTracker,\r\n RpcInboundCall,\r\n RpcInboundCallTracker,\r\n} from './rpc-call-tracker.js';\r\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\r\nimport { deserializeMessage } from './rpc-serialization.js';\r\nimport type { RpcHub } from './rpc-hub.js';\r\nimport { RpcRemoteObjectTracker } from './rpc-remote-object-tracker.js';\r\nimport { RpcSharedObjectTracker } from './rpc-shared-object-tracker.js';\r\nimport { RpcStream } from './rpc-stream.js';\r\nimport { RpcSerializationFormat, RpcSerializationFormatResolver } from './rpc-serialization-format.js';\r\nimport { IncreasingSeqCompressor } from './increasing-seq-compressor.js';\r\nimport { base64Decode, base64Encode } from './base64.js';\r\n\r\n// Yields to the event loop. Used by the reconnect-resend loop to chunk\r\n// large bursts so they don't spike main-thread time post-handshake.\r\n// setTimeout(0) is a macrotask (queueMicrotask would not yield to other\r\n// tasks); 0 ms still gets bumped to 4 ms by the browser for nested\r\n// setTimeouts but the slack is fine — we want a yield, not zero latency.\r\nfunction yieldToEventLoop(): Promise<void> {\r\n return new Promise<void>(resolve => setTimeout(resolve, 0));\r\n}\r\n\r\n/** WebSocket close code sent by the server when the client's serialization format is unsupported. */\r\n\r\nexport const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;\r\n\r\n// Connect / handshake / keepalive timing limits now live on `RpcLimits`,\r\n// resolved via `hub.limits` at peer construction. See `rpc-limits.ts` for\r\n// the override paths (process-wide / per-hub / per-peer).\r\n\r\nconst { debugLog, infoLog, warnLog, errorLog } = getLogs('RpcPeer');\r\n\r\n/** Builds the WebSocket connection URL for an RpcClientPeer. */\r\nexport type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string | Promise<string>;\r\n\r\n/** Default connection URL provider — sets `clientId` and `f` query parameters,\r\n * replacing any existing values to keep the wire format consistent with the\r\n * peer's actual `serializationFormat`. */\r\nexport const defaultConnectionUrlResolver: RpcConnectionUrlResolver = peer => {\r\n const formatKey = peer.serializationFormat.key;\r\n try {\r\n const url = new URL(peer.ref);\r\n url.searchParams.set('clientId', peer.clientId);\r\n url.searchParams.set('f', formatKey);\r\n return url.toString();\r\n } catch {\r\n // Fallback for non-URL refs (tests/harnesses that pass \"ws://test\" etc.)\r\n const sep = peer.ref.includes('?') ? '&' : '?';\r\n return peer.ref + sep + `clientId=${peer.clientId}&f=${formatKey}`;\r\n }\r\n};\r\n\r\n// eslint-disable-next-line prefer-const -- intentionally `let` so consumers can swap the implementation\r\nexport let sanitizeUrl = (url: string): string => {\r\n try {\r\n const parsed = new URL(url);\r\n if (!parsed.searchParams.has('session'))\r\n return url;\r\n parsed.searchParams.set('session', '<redacted>');\r\n return parsed.toString();\r\n } catch {\r\n return url.replace(/([?&]session=)[^&]*/i, '$1<redacted>');\r\n }\r\n};\r\n\r\n/** Extract the serialization format key from an `f=` query parameter, if any. */\r\nfunction parseFormatFromUrl(url: string): string | undefined {\r\n try {\r\n const f = new URL(url).searchParams.get('f');\r\n return f ?? undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Options for RpcPeer.call() — allows custom call types and cancellation. */\r\nexport interface RpcCallOptions {\r\n /** Wire CallType field (0 = regular). */\r\n callTypeId?: number;\r\n /** Factory for creating custom outbound call instances (e.g. compute calls). */\r\n outboundCallFactory?: (id: number, method: string) => RpcOutboundCall;\r\n /** AbortSignal for caller-initiated cancellation. */\r\n signal?: AbortSignal;\r\n /** Bitfield of RpcRemoteExecutionMode flags (default: 7 = Default). */\r\n remoteExecutionMode?: number;\r\n}\r\n\r\n/** Data extracted from an inbound $sys.Handshake message. */\r\nexport interface RemoteHandshake {\r\n RemotePeerId?: string;\r\n RemoteHubId?: string;\r\n ProtocolVersion?: number;\r\n Index?: number;\r\n}\r\n\r\n/** Base class for RPC peers — handles bidirectional message dispatch. */\r\nexport abstract class RpcPeer {\r\n readonly hub: RpcHub;\r\n /** Routing/addressing key — used as key in hub.peers (URL for clients, \"server://{uuid}\" for servers). */\r\n readonly ref: string;\r\n /** Auto-generated GUID — used in handshakes only. */\r\n readonly id: string = crypto.randomUUID();\r\n readonly outboundCalls = new RpcOutboundCallTracker();\r\n readonly inboundCalls = new RpcInboundCallTracker();\r\n readonly remoteObjects = new RpcRemoteObjectTracker();\r\n readonly sharedObjects = new RpcSharedObjectTracker();\r\n\r\n /** Fired on every state transition with the new value. Initial state is\r\n * `Disconnected`; `Connecting` is entered when the peer starts an\r\n * attempt (the reconnect delay is part of this phase); `Connected` is\r\n * entered only after the handshake has succeeded. */\r\n readonly connectionStateChanged = new EventHandlerSet<RpcConnectionState>();\r\n\r\n protected _connection: RpcConnection | undefined;\r\n /** Mirror of `_connectionState === Connected`. True only after the\r\n * handshake round-trip completes; outbound `call()` checks this to\r\n * decide between sending now and deferring until the next `Connected`\r\n * transition. Kept as its own field so it can stay false through the\r\n * Handshaking phase without exposing the run loop's `_connection`\r\n * bookkeeping. */\r\n protected _isConnected = false;\r\n protected _connectionState: RpcConnectionState = RpcConnectionState.Disconnected;\r\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\r\n private _keepAliveWatchdog: ReturnType<typeof setTimeout> | undefined;\r\n /** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet\r\n * for the current connection. Useful for diagnostics. */\r\n private _lastKeepAliveAt = 0;\r\n /** Outbound keep-alive send period. Initialized from `hub.limits` at\r\n * construction; set directly to override on a single peer. */\r\n keepAlivePeriodMs: number;\r\n /** Inbound keep-alive silence tolerance. Initialized from `hub.limits`\r\n * at construction; set directly to override on a single peer. */\r\n keepAliveTimeoutMs: number;\r\n\r\n protected constructor(hub: RpcHub, ref: string) {\r\n this.hub = hub;\r\n this.ref = ref;\r\n this.keepAlivePeriodMs = hub.limits.keepAlivePeriodMs;\r\n this.keepAliveTimeoutMs = hub.limits.keepAliveTimeoutMs;\r\n }\r\n\r\n get connection(): RpcConnection | undefined {\r\n return this._connection;\r\n }\r\n\r\n get connectionState(): RpcConnectionState {\r\n return this._connectionState;\r\n }\r\n\r\n get isConnected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n protected _setConnectionState(value: RpcConnectionState): void {\r\n if (this._connectionState === value) return;\r\n const previousState = this._connectionState;\r\n this._connectionState = value;\r\n // `_isConnected` flips on entering `Connected`, and off only when\r\n // leaving `Connected`. Failed-handshake transitions (Connecting →\r\n // Disconnected) leave it alone — it was already false in production,\r\n // and tests that set it directly via `connectWith` keep it true while\r\n // the run loop spins through retry iterations without ever reaching\r\n // `Connected`.\r\n if (value === RpcConnectionState.Connected) {\r\n this._isConnected = true;\r\n this._armKeepAliveWatchdog();\r\n } else if (value === RpcConnectionState.Disconnected) {\r\n if (previousState === RpcConnectionState.Connected)\r\n this._isConnected = false;\r\n this._disarmKeepAliveWatchdog();\r\n }\r\n this.connectionStateChanged.trigger(value);\r\n }\r\n\r\n /** Resolves on the next transition into `Connected` — or immediately if\r\n * already in that state. */\r\n whenConnected(): Promise<void> {\r\n if (this._connectionState === RpcConnectionState.Connected)\r\n return Promise.resolve();\r\n return new Promise<void>(resolve => {\r\n const handler = (state: RpcConnectionState): void => {\r\n if (state !== RpcConnectionState.Connected) return;\r\n this.connectionStateChanged.remove(handler);\r\n resolve();\r\n };\r\n this.connectionStateChanged.add(handler);\r\n });\r\n }\r\n\r\n protected setupConnection(conn: RpcConnection): void {\r\n this._connection = conn;\r\n\r\n conn.messageReceived.add(raw => this._handleMessage(raw));\r\n conn.closed.add(ev => {\r\n // Bail if this conn has already been replaced (e.g. another\r\n // setupConnection() call ran in between). Without this guard,\r\n // a late-firing close event from the old socket would clobber\r\n // the live `_connection` field and force the state machine\r\n // back to Disconnected mid-session.\r\n if (this._connection !== conn) return;\r\n this._connection = undefined;\r\n // A live conn going away always drops us out of \"connected\",\r\n // regardless of where the state machine thought we were. The\r\n // `_setConnectionState(Disconnected)` below only flips\r\n // `_isConnected` off if we were coming from `Connected`, which\r\n // misses the case where the conn closes mid-handshake (state\r\n // still `Connecting`) or was set up via `connectWith` (state\r\n // still `Disconnected`).\r\n this._isConnected = false;\r\n // Mirrors RpcPeer.cs:524-526 — log disconnect with reason if present.\r\n if (ev.reason)\r\n infoLog?.log(`'${this.ref}': Disconnected: ${ev.reason} (code=${ev.code})`);\r\n else\r\n infoLog?.log(`'${this.ref}': Disconnected`);\r\n // Flipping state to Disconnected tears down the keep-alive watchdog.\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n });\r\n\r\n void conn.whenConnected.then(() => {\r\n // The state transition to `Connected` happens after the handshake —\r\n // that's where keep-alive arming happens too.\r\n infoLog?.log(`'${this.ref}': WS open`);\r\n });\r\n }\r\n\r\n /**\r\n * The serialization format used by this peer for outgoing messages.\r\n * If not set explicitly, defaults to the resolver's default (JSON /\r\n * json5np). Subclasses can pre-assign in their constructor.\r\n */\r\n private _serializationFormat: RpcSerializationFormat | undefined;\r\n\r\n get serializationFormat(): RpcSerializationFormat {\r\n return this._serializationFormat ?? RpcSerializationFormatResolver.Default.defaultFormat;\r\n }\r\n\r\n set serializationFormat(value: RpcSerializationFormat) {\r\n this._serializationFormat = value;\r\n }\r\n\r\n call(\r\n method: string,\r\n args?: unknown[],\r\n options?: RpcCallOptions\r\n ): RpcOutboundCall {\r\n const callId = this.outboundCalls.nextId();\r\n const outboundCall = options?.outboundCallFactory\r\n ? options.outboundCallFactory(callId, method)\r\n : new RpcOutboundCall(callId, method, options?.remoteExecutionMode ?? 7);\r\n\r\n const envelope: RpcMessage = {\r\n Method: method,\r\n RelatedId: callId,\r\n CallType: options?.callTypeId ?? 0,\r\n };\r\n outboundCall.serializedWireData = this.serializationFormat.serializeMessage(\r\n envelope,\r\n args,\r\n this._connection?.encoder,\r\n this.hub.registry\r\n );\r\n\r\n // Mirror .NET `RpcOutboundCall.SendAsync`: register the call up front,\r\n // then either send now (fast path) or wait for `Connected` and send.\r\n // No central \"pending\" queue — each call self-manages its wait.\r\n this.outboundCalls.register(outboundCall);\r\n if (this._isConnected) {\r\n this._sendWireData(outboundCall.serializedWireData);\r\n } else {\r\n // Defer: send when the peer transitions to `Connected`. If the\r\n // call's result is completed before that (cancel, peer close,\r\n // error), the registered handler skips the send.\r\n const stateHandler = (state: RpcConnectionState): void => {\r\n if (state !== RpcConnectionState.Connected) return;\r\n this.connectionStateChanged.remove(stateHandler);\r\n if (outboundCall.result.isCompleted) return;\r\n this._sendWireData(outboundCall.serializedWireData);\r\n };\r\n this.connectionStateChanged.add(stateHandler);\r\n // Detach the handler if the call is completed before we ever\r\n // reach `Connected` — prevents leaking the listener.\r\n outboundCall.result.promise\r\n .then(() => this.connectionStateChanged.remove(stateHandler))\r\n .catch(() => this.connectionStateChanged.remove(stateHandler));\r\n }\r\n\r\n // Wire up caller-initiated cancellation → sends $sys.Cancel to remote peer\r\n const signal = options?.signal;\r\n if (signal !== undefined) {\r\n const onAbort = () => {\r\n if (this.outboundCalls.remove(callId) !== undefined) {\r\n outboundCall.result.reject(new Error('Call cancelled.'));\r\n outboundCall.onDisconnect();\r\n if (this._connection !== undefined)\r\n this.hub.systemCallSender.cancel(this._connection, this.serializationFormat, callId);\r\n }\r\n };\r\n signal.addEventListener('abort', onAbort, { once: true });\r\n // Clean up listener when the call completes normally\r\n outboundCall.result.promise\r\n .then(() => signal.removeEventListener('abort', onAbort))\r\n .catch(() => signal.removeEventListener('abort', onAbort));\r\n }\r\n\r\n return outboundCall;\r\n }\r\n\r\n callNoWait(method: string, args?: unknown[]): void {\r\n if (!this._isConnected) return; // silently drop\r\n // Invariant: `_isConnected` is only true while `_connection` is set\r\n // (they both flip together in `_setConnectionState` / `conn.closed`).\r\n const conn = this._connection!;\r\n const envelope: RpcMessage = { Method: method, RelatedId: 0 };\r\n const wireData = this.serializationFormat.serializeMessage(\r\n envelope,\r\n args,\r\n conn.encoder,\r\n this.hub.registry\r\n );\r\n this._sendWireData(wireData);\r\n }\r\n\r\n /** Close the current WS connection without disposing the peer. The\r\n * `conn.closed` handler flips state to `Disconnected`; for\r\n * `RpcClientPeer` the run loop then iterates and reopens. The peer\r\n * stays in the hub and all client proxies bound to it remain valid. */\r\n disconnect(): void {\r\n this._connection?.close();\r\n }\r\n\r\n close(): void {\r\n // Mirrors RpcPeer.cs:397 — \"Stopping\".\r\n infoLog?.log(`'${this.ref}': Stopping`);\r\n // `_setConnectionState(Disconnected)` below will tear down the keep-alive\r\n // watchdog + send timer. Calls deferred via `whenConnected` self-detach\r\n // when their result rejects (see `call()`).\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n this.outboundCalls.rejectAll(new Error('Peer closed.'));\r\n this.outboundCalls.invalidateAll();\r\n this._connection?.close();\r\n this.hub.peers.delete(this.ref);\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n // Mirrors RpcPeer.cs:439 — \"Stopped\".\r\n infoLog?.log(`'${this.ref}': Stopped`);\r\n }\r\n\r\n /** Send pre-serialized wire data through the current connection. */\r\n protected _sendWireData(data: string | Uint8Array): void {\r\n if (this._connection === undefined) return;\r\n if (typeof data === 'string') {\r\n this._connection.send(data);\r\n } else {\r\n this._connection.sendBinary(data);\r\n }\r\n }\r\n\r\n /** Override in subclasses to handle the remote peer's handshake response. */\r\n protected _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Default: no-op. RpcClientPeer resolves a pending promise;\r\n // RpcServerPeer sends its own handshake back.\r\n }\r\n\r\n private _handleMessage(received: RpcReceivedMessage): void {\r\n let message: RpcMessage;\r\n let args: unknown[];\r\n try {\r\n if (received.kind === 'binary') {\r\n message = received.message;\r\n args = received.args;\r\n } else {\r\n const parsed = deserializeMessage(received.raw);\r\n message = parsed.message;\r\n args = parsed.args;\r\n }\r\n }\r\n catch (e) {\r\n // Mirrors RpcPeer.cs:453 — \"Failed to process inbound message\".\r\n errorLog?.log(`'${this.ref}': Failed to process inbound message:`, received, e);\r\n return;\r\n }\r\n const method = message.Method ?? '';\r\n debugLog?.log(`'${this.ref}': <- ${method}#${message.RelatedId ?? 0}`);\r\n\r\n // Handshake — dispatch to subclass handler\r\n if (method === RpcSystemCalls.handshake) {\r\n const raw = args[0];\r\n if (raw !== undefined) {\r\n // .NET's RpcHandshake is [MessagePackObject(Key)] — serialized\r\n // as a 5-element array in msgpack. In JSON it's an object with\r\n // named properties. Normalize both shapes here.\r\n const handshake = Array.isArray(raw)\r\n ? {\r\n RemotePeerId: raw[0] as string | undefined,\r\n RemoteHubId: raw[2] as string | undefined,\r\n ProtocolVersion: raw[3] as number | undefined,\r\n Index: raw[4] as number | undefined,\r\n }\r\n : raw as RemoteHandshake;\r\n this._onHandshakeReceived(handshake);\r\n }\r\n return;\r\n }\r\n\r\n // Other system calls — delegate to hub's system call handler\r\n if (method.startsWith('$sys')) {\r\n this.hub.systemCallHandler.handle(message, args, this);\r\n return;\r\n }\r\n\r\n // Regular inbound call — dispatch to service host\r\n this._handleInbound(message, args);\r\n }\r\n\r\n private _handleInbound(message: RpcMessage, args: unknown[]): void {\r\n const method = message.Method ?? '';\r\n const relatedId = message.RelatedId ?? 0;\r\n\r\n const call = new RpcInboundCall(relatedId, method, args);\r\n const methodDef = this.hub.serviceHost.getMethodDef(method);\r\n\r\n // For noWait calls: don't register in tracker, don't send response\r\n const isNoWait = methodDef?.noWait === true;\r\n if (!isNoWait) {\r\n this.inboundCalls.register(call);\r\n }\r\n\r\n // Dispatch to the hub's service host\r\n const serviceHost = this.hub.serviceHost;\r\n void (async () => {\r\n try {\r\n const context =\r\n this._connection !== undefined\r\n ? {\r\n __rpcDispatch: true as const,\r\n callId: relatedId,\r\n connection: this._connection,\r\n }\r\n : undefined;\r\n const result = await serviceHost.dispatch(\r\n method,\r\n args,\r\n context\r\n );\r\n if (\r\n methodDef?.stream === true &&\r\n this._connection !== undefined\r\n ) {\r\n // Stream method — wrap result in RpcStream if needed,\r\n // then let toRef() create the sender, register it, and start pumping.\r\n const stream = result instanceof RpcStream\r\n ? result as RpcStream<unknown>\r\n : new RpcStream<unknown>(result as AsyncIterable<unknown>);\r\n this.hub.systemCallSender.ok(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n stream.toRef(this)\r\n );\r\n } else if (!isNoWait && this._connection !== undefined) {\r\n this.hub.systemCallSender.ok(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n result\r\n );\r\n }\r\n } catch (e) {\r\n if (!isNoWait && this._connection !== undefined) {\r\n this.hub.systemCallSender.error(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n e\r\n );\r\n }\r\n debugLog?.log(`'${this.ref}': Inbound call failed: ${method}#${relatedId}`, e);\r\n } finally {\r\n if (!isNoWait) {\r\n this.inboundCalls.remove(relatedId);\r\n }\r\n }\r\n })();\r\n }\r\n\r\n /** Start sending `$sys.KeepAlive` pings and arm the silence watchdog.\r\n * Called when the peer transitions into `Connected` (after handshake).\r\n * Each inbound `$sys.KeepAlive` re-arms the watchdog; if it fires, the\r\n * WebSocket is force-closed so the normal disconnect path runs\r\n * (`conn.closed` → `connectionState = Disconnected` → UI).\r\n * Mirrors .NET `RpcObjectTrackers.Maintain`'s `KeepAliveTimeout` branch. */\r\n private _armKeepAliveWatchdog(): void {\r\n // Start the send timer once per connected session.\r\n this._keepAliveTimer ??= setInterval(() => {\r\n if (this._connection !== undefined) {\r\n // Send remote object IDs so the server's SharedObjectTracker keeps them alive.\r\n // Must NOT send outbound call IDs — those are a different ID namespace and would\r\n // cause the server to send $sys.Disconnect for IDs it doesn't recognize, which\r\n // the client may misinterpret as a disconnect of its own shared objects (e.g.\r\n // RpcStreamSender) when the IDs collide numerically.\r\n this.hub.systemCallSender.keepAlive(this._connection, this.serializationFormat, [\r\n ...this.remoteObjects.keys(),\r\n ]);\r\n }\r\n }, this.keepAlivePeriodMs);\r\n // (Re)arm the silence watchdog.\r\n if (this._keepAliveWatchdog !== undefined)\r\n clearTimeout(this._keepAliveWatchdog);\r\n this._keepAliveWatchdog = setTimeout(() => {\r\n this._keepAliveWatchdog = undefined;\r\n if (this._connection === undefined) return;\r\n warnLog?.log(\r\n `'${this.ref}': no keep-alive for ${this.keepAliveTimeoutMs}ms — force-closing connection`);\r\n this._connection.close();\r\n }, this.keepAliveTimeoutMs);\r\n }\r\n\r\n /** Stop sending keep-alives and clear the silence watchdog. Called when\r\n * the peer transitions into `Disconnected`. */\r\n private _disarmKeepAliveWatchdog(): void {\r\n if (this._keepAliveTimer !== undefined) {\r\n clearInterval(this._keepAliveTimer);\r\n this._keepAliveTimer = undefined;\r\n }\r\n if (this._keepAliveWatchdog !== undefined) {\r\n clearTimeout(this._keepAliveWatchdog);\r\n this._keepAliveWatchdog = undefined;\r\n }\r\n }\r\n\r\n /** Called by `RpcSystemCallHandler` when a `$sys.KeepAlive` message\r\n * arrives — records the timestamp and re-arms the silence watchdog. */\r\n notifyKeepAliveReceived(): void {\r\n this._lastKeepAliveAt = Date.now();\r\n if (this._keepAliveWatchdog !== undefined)\r\n this._armKeepAliveWatchdog();\r\n }\r\n}\r\n\r\n/** Connection state for RpcPeer. Mirrors .NET `RpcPeerConnectionState`'s\r\n * observable phases: the transport is `Connecting` until the WebSocket\r\n * opens, `Handshaking` from WS-open until the server's handshake reply\r\n * arrives, and `Connected` once the handshake completes. Outbound calls\r\n * are gated on `Connected`. */\r\nexport const enum RpcConnectionState {\r\n Disconnected = 0,\r\n Connecting = 1,\r\n Handshaking = 2,\r\n Connected = 3,\r\n}\r\n\r\n/** Client-side RPC peer — initiates WebSocket connection. ref = URL. */\r\nexport class RpcClientPeer extends RpcPeer {\r\n private _disposed = false;\r\n private _tryIndex = 0;\r\n private _handshakeIndex = 0;\r\n /** Index from the MOST RECENTLY RECEIVED server handshake — sent back\r\n * to the server in `$sys.Reconnect:3` so the server can verify we're\r\n * talking about the current connection. Mirrors .NET's\r\n * `connectionState.Handshake.Index` path in\r\n * `RpcOutboundCallTracker.TryReconnect`. */\r\n private _remoteHandshakeIndex = 0;\r\n private _lastRemoteHubId: string | undefined;\r\n private _pendingHandshake: PromiseSource<RemoteHandshake> | undefined;\r\n private _reconnectsAt = 0;\r\n\r\n /** Base64url-encoded peer ID — matches .NET's RpcClientPeer.ClientId (Guid.ToBase64Url). */\r\n readonly clientId: string;\r\n /** Builds the WebSocket URL for this peer. Replace to customize URL construction. */\r\n connectionUrlResolver: RpcConnectionUrlResolver =\r\n defaultConnectionUrlResolver;\r\n /** Optional WebSocket factory. When set, `run()` uses this instead of the\r\n * global `new WebSocket(url)` constructor. Used by tests (to inject mock\r\n * sockets) and by the Node load-test harness (to attach `Session` /\r\n * `Cookie` headers to the WS upgrade, which the browser `WebSocket`\r\n * constructor can't do). Must be set before `start()`. */\r\n webSocketFactory: ((url: string) => WebSocketLike) | undefined;\r\n /** Max time to wait for the WebSocket to reach OPEN before aborting the\r\n * connection attempt. Initialized from `hub.limits` at construction;\r\n * set directly to override on a single peer. */\r\n connectTimeoutMs: number;\r\n /** Max time to wait for the server's handshake response before aborting\r\n * the connection attempt. Initialized from `hub.limits` at construction;\r\n * set directly to override on a single peer. */\r\n handshakeTimeoutMs: number;\r\n readonly peerChanged = new EventHandlerSet<void>();\r\n /** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */\r\n readonly unsupportedFormat = new EventHandlerSet<{ reason: string }>();\r\n readonly reconnectsAtChanged = new EventHandlerSet<void>();\r\n\r\n get reconnectsAt(): number {\r\n return this._reconnectsAt;\r\n }\r\n\r\n private _setReconnectsAt(value: number): void {\r\n if (this._reconnectsAt === value) return;\r\n this._reconnectsAt = value;\r\n this.reconnectsAtChanged.trigger();\r\n }\r\n\r\n /** The promise returned by the reconnect loop. `undefined` until\r\n * `start()` is invoked (which happens automatically when\r\n * `mustStart = true` is passed to the ctor). */\r\n whenRunning: Promise<void> | undefined;\r\n\r\n /**\r\n * @param hub RpcHub instance this peer will be tracked by\r\n * @param url Full WebSocket URL — the serialization format is taken from\r\n * the `f=` query parameter (e.g. `wss://host/rpc/ws?f=msgpack6`);\r\n * falls back to the resolver's default when absent. Use\r\n * {@link RpcPeerRefBuilder.forClient} to compose URLs with a format.\r\n * @param mustStart Kick off the reconnect loop automatically at the end of\r\n * construction (default `true`). Pass `false` if you need to\r\n * tweak `reconnectDelayer`, `handshakeTimeoutMs`, or similar\r\n * options before the loop starts — then call `peer.start()`\r\n * manually.\r\n */\r\n constructor(hub: RpcHub, url: string, mustStart = true) {\r\n super(hub, url);\r\n this.connectTimeoutMs = hub.limits.connectTimeoutMs;\r\n this.handshakeTimeoutMs = hub.limits.handshakeTimeoutMs;\r\n this.clientId = guidToBase64Url(this.id);\r\n const resolver = RpcSerializationFormatResolver.Default;\r\n this.serializationFormat = resolver.get(parseFormatFromUrl(url) ?? resolver.defaultFormatKey);\r\n if (mustStart)\r\n this.start();\r\n }\r\n\r\n /** One-shot connection for tests — no reconnection loop, no handshake.\r\n * @param conn Rpc connection\r\n * @param isPeerChanged Whether to treat this as a peer-identity change\r\n * (default `true` — no handshake was exchanged, so stage-3 compute calls\r\n * self-invalidate and remote/shared objects are disposed). Pass `false`\r\n * to simulate a same-peer reconnect in tests. */\r\n connectWith(conn: RpcConnection, isPeerChanged = true): void {\r\n this.setupConnection(conn);\r\n // Test-only: synthesize the \"ready for calls\" state without running\r\n // the handshake. Stays off the public state machine on purpose —\r\n // tests don't drive the run loop, and entering `Connected` via\r\n // `_setConnectionState` would arm the keep-alive watchdog against a\r\n // mock transport that never replies to keep-alives. We still need\r\n // to release any calls deferred via `whenConnected`, so we trigger\r\n // the listener set directly.\r\n this._isConnected = true;\r\n this.connectionStateChanged.trigger(RpcConnectionState.Connected);\r\n void this._reconnect(isPeerChanged);\r\n }\r\n\r\n /** Kick off the reconnect loop. Idempotent — subsequent calls are\r\n * no-ops; await `peer.whenRunning` if you need the loop's completion\r\n * promise. Customize `peer.webSocketFactory` first if you need a\r\n * non-default WebSocket constructor. */\r\n start(): void {\r\n void this.run();\r\n }\r\n\r\n /** The reconnect loop itself. Exposed as `protected` so subclasses (and\r\n * `start()`) can drive it; public callers should use {@link start}. */\r\n protected run(): Promise<void> {\r\n return this.whenRunning ??= this._runImpl();\r\n }\r\n\r\n private async _runImpl(): Promise<void> {\r\n if (this._disposed) return;\r\n // Mirrors RpcPeer.cs:230 — \"Started\".\r\n infoLog?.log(`'${this.ref}': Started (Client)`);\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n while (!this._disposed) {\r\n // Enter Connecting for this iteration — the reconnect delay\r\n // below (if any) is part of this state per the contract\r\n // \"connecting when the peer starts to reconnect\".\r\n this._setConnectionState(RpcConnectionState.Connecting);\r\n\r\n // Apply retry delay (no-op on first iteration where tryIndex === 0).\r\n // The ApiReconnectDelayer may park indefinitely here while\r\n // `Api.canConnect === false`.\r\n const retryDelay = this.hub.reconnectDelayer.getDelay(this._tryIndex);\r\n if (retryDelay.isLimitExceeded) {\r\n warnLog?.log(`'${this.ref}': Reconnect limit exceeded - shutting down`);\r\n this._disposed = true;\r\n break;\r\n }\r\n this._setReconnectsAt(retryDelay.endsAt);\r\n try {\r\n await retryDelay.promise;\r\n } finally {\r\n this._setReconnectsAt(0);\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n if (this._disposed) break;\r\n\r\n let lastCloseCode = 0;\r\n let lastCloseReason = '';\r\n try {\r\n const connUrl = await this.connectionUrlResolver(this);\r\n // Mirrors RpcClientPeer.cs:45 — \"Connecting...\".\r\n infoLog?.log(`'${this.ref}': Connecting to ${sanitizeUrl(connUrl)}`);\r\n const ws =\r\n this.webSocketFactory?.(connUrl) ??\r\n (new WebSocket(connUrl) as unknown as WebSocketLike);\r\n const conn = new RpcWebSocketConnection(\r\n ws,\r\n this.serializationFormat.isBinary,\r\n this.serializationFormat,\r\n this.hub.registry\r\n );\r\n\r\n // Eager \"connection closed\" PromiseSource — captured right\r\n // after connection creation so every subsequent await can\r\n // race against it without missing an already-fired close.\r\n // EventHandlerSet fires synchronously and does not replay\r\n // past triggers, so a late `conn.closed.add(...)` would\r\n // deadlock if the socket closed just before registration.\r\n const whenClosed = new PromiseSource<{ code: number; reason: string }>();\r\n const closedController = new AbortController();\r\n conn.closed.add(ev => {\r\n lastCloseCode = ev.code;\r\n lastCloseReason = ev.reason;\r\n whenClosed.resolve(ev);\r\n closedController.abort();\r\n });\r\n\r\n // Derived rejecting promise — used to unblock awaits that\r\n // expect a successful signal (whenConnected, handshake).\r\n const closedRejection = whenClosed.promise.then<never>(() => {\r\n throw new Error('Connection failed');\r\n });\r\n closedRejection.catch(() => { /* noop — prevent unhandled rejection when conn closes normally */ });\r\n\r\n // Create a fresh handshake promise before setupConnection (which registers\r\n // the message handler). The handler can only fire after the WS opens,\r\n // and we send our handshake below, so timing is safe.\r\n this._pendingHandshake = new PromiseSource<RemoteHandshake>();\r\n this.setupConnection(conn);\r\n // `_connection` stays set throughout the handshake window so\r\n // `close()` can tear down the WS. Outbound calls are gated by\r\n // `_isConnected`, which only flips on when we enter `Connected`\r\n // below — before that, `call()` defers the send via the\r\n // per-call `whenConnected` listener.\r\n\r\n // Race connection open against close — if WS fails to connect,\r\n // whenConnected stays pending forever, so we must also watch for close.\r\n // The connect timeout caps how long we wait for the OPEN state: on a\r\n // hung connect (mobile after network change, half-open after sleep)\r\n // the browser may take ~2 min to emit onerror/onclose, which blocks\r\n // the reconnect loop and surfaces as \"can't reconnect\" to the user.\r\n const connectTimeoutMs = this.connectTimeoutMs;\r\n try {\r\n await withTimeout(\r\n Promise.race([conn.whenConnected, closedRejection]),\r\n connectTimeoutMs,\r\n 'Connect timeout');\r\n } catch (e) {\r\n if (e instanceof TimeoutError) {\r\n warnLog?.log(`'${this.ref}': Connect timed out after ${connectTimeoutMs}ms`);\r\n // Force-close the WS so the run loop iterates to retry-delay\r\n // instead of leaking a half-open socket. The browser onclose\r\n // may still take a moment but the loop no longer waits for it.\r\n conn.close();\r\n }\r\n throw e;\r\n }\r\n\r\n // WS open, handshake exchange about to start. Mirrors\r\n // .NET's `Handshaking` phase of `RpcPeerConnectionState`.\r\n this._setConnectionState(RpcConnectionState.Handshaking);\r\n\r\n // Send our handshake, then wait for the server's response.\r\n try {\r\n this.hub.systemCallSender.handshake(\r\n conn,\r\n this.serializationFormat,\r\n this.id,\r\n this.hub.hubId,\r\n ++this._handshakeIndex\r\n );\r\n }\r\n catch (e) {\r\n // Mirrors RpcPeer.cs:305 — \"Failed to send handshake\".\r\n warnLog?.log(`'${this.ref}': Failed to send handshake`, e);\r\n throw e;\r\n }\r\n\r\n // Handshake timeout — mirrors .NET's `Hub.Limits.HandshakeTimeout`.\r\n // Without this, an accepted WS that never replies to $sys.Handshake\r\n // (half-open TCP, mid-restart server holding the socket briefly)\r\n // would block the run loop indefinitely, preventing reconnect.\r\n const handshakeTimeoutMs = this.handshakeTimeoutMs;\r\n let remoteHandshake: RemoteHandshake;\r\n try {\r\n remoteHandshake = await withTimeout(\r\n Promise.race([this._pendingHandshake.promise, closedRejection]),\r\n handshakeTimeoutMs,\r\n 'Handshake timeout');\r\n } catch (e) {\r\n if (e instanceof TimeoutError) {\r\n warnLog?.log(`'${this.ref}': Handshake timed out after ${handshakeTimeoutMs}ms`);\r\n // Force-close the socket so the run loop continues to\r\n // the retry-delay branch instead of leaking the WS.\r\n conn.close();\r\n }\r\n throw e;\r\n } finally {\r\n this._pendingHandshake = undefined;\r\n }\r\n\r\n // Record the server's handshake index for $sys.Reconnect.\r\n this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;\r\n\r\n // Peer change detection (like .NET's RpcHandshake.GetPeerChangeKind)\r\n const remoteHubId = remoteHandshake.RemoteHubId;\r\n let isPeerChanged = false;\r\n if (remoteHubId !== undefined) {\r\n isPeerChanged =\r\n this._lastRemoteHubId !== undefined &&\r\n this._lastRemoteHubId !== remoteHubId;\r\n if (isPeerChanged) {\r\n // Server identity changed — clear inbound state\r\n this.inboundCalls.clear();\r\n // Mirrors RpcPeer.cs:439 (Peer changed branch).\r\n infoLog?.log(`'${this.ref}': Peer changed`);\r\n this.peerChanged.trigger();\r\n }\r\n this._lastRemoteHubId = remoteHubId;\r\n }\r\n // Mirrors RpcPeer.cs:316 — \"Handshake succeeded\".\r\n infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);\r\n\r\n // Activate the connection and re-send outbound calls.\r\n // All of this happens AFTER the handshake, so the server is ready to process calls.\r\n this._setConnectionState(RpcConnectionState.Connected);\r\n this._tryIndex = 0;\r\n // Pass the close signal so `_reconcileReconnect`'s inner\r\n // `$sys.Reconnect` call cancels cleanly if the socket dies\r\n // mid-reconcile — otherwise its pending result.promise would\r\n // deadlock the outer _reconnect() await.\r\n await this._reconnect(isPeerChanged, closedController.signal);\r\n\r\n // Wait until disconnected — use the eager whenClosed promise\r\n // so we don't miss an already-fired close event.\r\n await whenClosed.promise;\r\n } catch (e) {\r\n // connection failed or handshake failed\r\n debugLog?.log(`'${this.ref}': run() iteration failed:`, e);\r\n }\r\n\r\n // Normally redundant — the `conn.closed` handler in\r\n // `setupConnection` already fired `Disconnected`. Kept for\r\n // paths that throw before setupConnection runs.\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n if (this._disposed) break;\r\n\r\n // Server rejected our serialization format — stop reconnecting and notify listeners\r\n if (lastCloseCode === RPC_CLOSE_CODE_UNSUPPORTED_FORMAT) {\r\n // Mirrors RpcPeer.cs:518 — \"Can't (re)connect, will shut down\".\r\n warnLog?.log(`'${this.ref}': Unsupported serialization format - shutting down`);\r\n this.unsupportedFormat.trigger({ reason: lastCloseReason });\r\n this._disposed = true;\r\n break;\r\n }\r\n\r\n this._tryIndex++;\r\n }\r\n } finally {\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n }\r\n }\r\n\r\n /**\r\n * Reconcile outbound calls after reconnection, then flush pending sends.\r\n *\r\n * On peer change (different server hubId) or with binary wire format —\r\n * we blind-resend every eligible call.\r\n *\r\n * On a same-peer reconnect with JSON transport, first call\r\n * `$sys.Reconnect` to ask the server which call IDs it no longer\r\n * recognizes (e.g. inbound tracker was cleared, or the call never\r\n * reached the server). Only those get resent; the rest stay in the\r\n * tracker awaiting their $sys.Ok. This prevents duplicate server-side\r\n * invocation of in-flight streaming calls on every transient\r\n * reconnect — mirrors .NET `RpcOutboundCallTracker.Reconnect` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcCallTrackers.cs:209-279.\r\n *\r\n * Stage-3 compute calls (result already resolved, awaiting invalidation)\r\n * are always self-invalidated — TS doesn't track compute-call stages\r\n * across reconnect, so the safest behavior is to force a fresh compute.\r\n */\r\n private async _reconnect(isPeerChanged: boolean, closedSignal?: AbortSignal): Promise<void> {\r\n if (this._connection === undefined) return;\r\n\r\n // Handle remote and shared objects on reconnect.\r\n // On peer change, the remote server is gone — both remote objects\r\n // (proxies registered here for server-owned objects) and shared objects\r\n // (e.g. RpcStreamSender instances this peer owns and is pushing to the\r\n // server) must be disposed: the new server has no corresponding entries,\r\n // so they would hang forever waiting for ACKs that never arrive.\r\n // Mirrors .NET RpcPeer.Reset (src/ActualLab.Rpc/RpcPeer.cs:430-440).\r\n if (isPeerChanged) {\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n } else {\r\n this.remoteObjects.reconnectAll();\r\n }\r\n\r\n // Filter calls by RemoteExecutionMode (same logic as before).\r\n const eligible: RpcOutboundCall[] = [];\r\n for (const call of [...this.outboundCalls.values()]) {\r\n const mode = call.remoteExecutionMode;\r\n if (!(mode & 2)) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\r\n 'Outbound call failed: disconnected and AllowReconnect is not set.'));\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n if (isPeerChanged && !(mode & 4)) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\r\n 'Outbound call failed: reconnected to a different peer and AllowResend is not set.'));\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n eligible.push(call);\r\n }\r\n\r\n // Reconcile with the server on same-peer reconnects. On peer change\r\n // we skip the round-trip (the new peer knows nothing) and blind-resend.\r\n //\r\n // Wire format: the `completedStages` dict uses plain JS-object shape\r\n // with string keys and base64-encoded `Uint8Array` values. This is\r\n // JSON-compatible with .NET's `System.Text.Json` serialization of\r\n // `Dictionary<int, byte[]>` and also works for TS-to-TS msgpack\r\n // (serializes as map<str, str>). Full .NET-msgpack interop for\r\n // $sys.Reconnect — which uses `map<int, bin>` — is a future\r\n // enhancement; the JSON path covers ActualChat's primary use case.\r\n let unknownIds: Set<number> | null = null;\r\n if (!isPeerChanged && eligible.length > 0) {\r\n unknownIds = await this._reconcileReconnect(eligible, closedSignal);\r\n }\r\n\r\n // Yield-batched resend. Eligible can be hundreds after a long\r\n // disconnect; chunking prevents a sync spike on the main thread.\r\n const YIELD_BATCH = 20;\r\n let resentSinceYield = 0;\r\n for (const call of eligible) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call: always self-invalidate to force a\r\n // fresh recompute — TS has no cross-reconnect compute-state\r\n // tracking (unlike .NET's Reliable reconnection).\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n if (unknownIds !== null && !unknownIds.has(call.callId)) {\r\n // Server reports this call is still in flight on its side —\r\n // skip the resend. The pending result promise continues to\r\n // await the original call's $sys.Ok.\r\n continue;\r\n }\r\n this._sendWireData(call.serializedWireData);\r\n resentSinceYield++;\r\n if (resentSinceYield >= YIELD_BATCH) {\r\n resentSinceYield = 0;\r\n await yieldToEventLoop();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Ask the server which call IDs in `eligible` it no longer recognizes\r\n * via `$sys.Reconnect:3`. Returns the set of call IDs that need to be\r\n * resent. On any failure, falls back to \"resend everything\".\r\n *\r\n * Wire format of the `completedStages` argument is chosen per wire format:\r\n * - JSON (json5np): plain object `{\"<stage>\": \"<base64-of-bytes>\"}` —\r\n * matches .NET `System.Text.Json`'s default for `Dictionary<int, byte[]>`.\r\n * - MessagePack (msgpack6/msgpack6c): `Map<number, Uint8Array>` —\r\n * the patched encoder (see `./msgpack-map-patch.ts`) writes this as a\r\n * native msgpack `map<int, bin>`, matching .NET MessagePack-CSharp's\r\n * default for `Dictionary<int, byte[]>`.\r\n */\r\n private async _reconcileReconnect(eligible: RpcOutboundCall[], closedSignal?: AbortSignal): Promise<Set<number>> {\r\n try {\r\n // Group by completedStage, sort IDs, compress per-stage.\r\n const byStage = new Map<number, number[]>();\r\n for (const call of eligible) {\r\n const stage = call.getReconnectStage(false);\r\n if (stage === null) continue;\r\n let arr = byStage.get(stage);\r\n if (!arr) { arr = []; byStage.set(stage, arr); }\r\n arr.push(call.callId);\r\n }\r\n if (byStage.size === 0) {\r\n // Nothing to reconcile — fall back to resend-all.\r\n return new Set(eligible.map(c => c.callId));\r\n }\r\n\r\n const isBinary = this.serializationFormat.isBinary;\r\n const stagesArg: Map<number, Uint8Array> | Record<string, string> = isBinary\r\n ? new Map<number, Uint8Array>()\r\n : {};\r\n for (const [stage, ids] of byStage) {\r\n ids.sort((a, b) => a - b);\r\n const bytes = IncreasingSeqCompressor.serialize(ids);\r\n if (isBinary) {\r\n (stagesArg as Map<number, Uint8Array>).set(stage, bytes);\r\n } else {\r\n (stagesArg as Record<string, string>)[String(stage)] = base64Encode(bytes);\r\n }\r\n }\r\n\r\n // Send $sys.Reconnect via the regular call path so the server's\r\n // $sys.Ok reply resolves a PromiseSource we can await. The\r\n // first arg is the server's OWN handshake index (captured from\r\n // the remote handshake we just received), not ours — this\r\n // matches .NET's protocol, where the server validates\r\n // `ownHandshake.Index == handshakeIndex`.\r\n const reconnectCall = this.call(\r\n RpcSystemCalls.reconnect,\r\n [this._remoteHandshakeIndex, stagesArg],\r\n {\r\n remoteExecutionMode: 1, /* AwaitForConnection only; must not itself reconnect */\r\n signal: closedSignal,\r\n },\r\n );\r\n const result = await reconnectCall.result.promise;\r\n const bytes = _toBytes(result);\r\n if (bytes === null) return new Set(eligible.map(c => c.callId));\r\n return new Set(IncreasingSeqCompressor.deserialize(bytes));\r\n } catch {\r\n // Reconciliation failed — resend everything.\r\n return new Set(eligible.map(c => c.callId));\r\n }\r\n }\r\n\r\n protected override _onHandshakeReceived(handshake: RemoteHandshake): void {\r\n this._pendingHandshake?.resolve(handshake);\r\n }\r\n\r\n override close(): void {\r\n this._disposed = true;\r\n super.close();\r\n }\r\n}\r\n\r\n/** Server-side RPC peer — wraps an accepted connection. ref = \"server://{uuid}\". */\r\nexport class RpcServerPeer extends RpcPeer {\r\n // Needed to widen the base `protected constructor` to public.\r\n // eslint-disable-next-line @typescript-eslint/no-useless-constructor\r\n constructor(hub: RpcHub, ref: string) {\r\n super(hub, ref);\r\n }\r\n\r\n /** Accept an incoming connection — sets up message handling and handshake response. */\r\n accept(conn: RpcConnection): void {\r\n this._setConnectionState(RpcConnectionState.Connecting);\r\n this.setupConnection(conn);\r\n // Server starts in Handshaking immediately on accept — there's no\r\n // separate \"WS open\" phase since the WS is already up by the time\r\n // we accept. Stay in Handshaking until the client's handshake\r\n // arrives in `_onHandshakeReceived` below.\r\n this._setConnectionState(RpcConnectionState.Handshaking);\r\n // When the client connection drops, clean up shared objects (stream senders).\r\n // Server peers don't reconnect — once the connection is gone, senders must be\r\n // terminated so their source iterators release resources promptly.\r\n conn.closed.add(() => {\r\n // Bail if this conn has already been replaced by a subsequent accept().\r\n // Without this guard, a late-firing close from the old socket would\r\n // disconnect shared objects that belong to the live session.\r\n if (this._connection !== conn && this._connection !== undefined) return;\r\n this.sharedObjects.disconnectAll();\r\n });\r\n }\r\n\r\n protected override _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Client sent its handshake → respond with our own and transition to Connected.\r\n if (this._connection !== undefined) {\r\n this.hub.systemCallSender.handshake(\r\n this._connection,\r\n this.serializationFormat,\r\n this.id,\r\n this.hub.hubId,\r\n 0\r\n );\r\n this._setConnectionState(RpcConnectionState.Connected);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Convert a UUID string to base64url — matches .NET's Guid.ToBase64Url().\r\n * .NET Guid stores the first 3 groups in little-endian byte order.\r\n */\r\nfunction guidToBase64Url(uuid: string): string {\r\n const hex = uuid.replace(/-/g, '');\r\n const bytes = new Uint8Array(16);\r\n\r\n // Group 1 (bytes 0-3): little-endian\r\n bytes[0] = parseInt(hex.slice(6, 8), 16);\r\n bytes[1] = parseInt(hex.slice(4, 6), 16);\r\n bytes[2] = parseInt(hex.slice(2, 4), 16);\r\n bytes[3] = parseInt(hex.slice(0, 2), 16);\r\n // Group 2 (bytes 4-5): little-endian\r\n bytes[4] = parseInt(hex.slice(10, 12), 16);\r\n bytes[5] = parseInt(hex.slice(8, 10), 16);\r\n // Group 3 (bytes 6-7): little-endian\r\n bytes[6] = parseInt(hex.slice(14, 16), 16);\r\n bytes[7] = parseInt(hex.slice(12, 14), 16);\r\n // Groups 4-5 (bytes 8-15): big-endian\r\n for (let i = 8; i < 16; i++)\r\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\r\n\r\n const binary = String.fromCharCode(...bytes);\r\n return btoa(binary)\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=+$/, '');\r\n}\r\n\r\n/** Coerce a `$sys.Ok` result carrying a byte[] into a Uint8Array. */\r\nfunction _toBytes(value: unknown): Uint8Array | null {\r\n if (value instanceof Uint8Array) return value;\r\n if (typeof value === 'string') {\r\n try { return base64Decode(value); } catch { return null; }\r\n }\r\n return null;\r\n}\r\n","// RPC serialization format definitions — mirrors .NET RpcSerializationFormat.\r\n//\r\n// Each format knows how to serialize/deserialize RPC messages on the wire.\r\n// Format instances are immutable singletons — state like method registries\r\n// is passed in via parameters, not held by the format.\r\n\r\nimport { Encoder, Decoder } from '@msgpack/msgpack';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport {\r\n serializeMessage,\r\n deserializeMessage,\r\n serializeBinaryMessage,\r\n splitBinaryFrame,\r\n serializeBinaryFrame,\r\n createBinaryEncoder,\r\n serializeCompactBinaryMessage,\r\n splitCompactBinaryFrame,\r\n} from './rpc-serialization.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\n/** Parsed inbound message — result of deserialization. */\r\nexport interface RpcDeserializedMessage {\r\n message: RpcMessage;\r\n args: unknown[];\r\n}\r\n\r\n/** Wire data produced by serialization — either text or binary. */\r\nexport type RpcWireData = string | Uint8Array;\r\n\r\n/**\r\n * Base class for RPC serialization formats.\r\n * Instances are immutable singletons — one per format key.\r\n * State (like method registries) is passed via parameters.\r\n */\r\nexport abstract class RpcSerializationFormat {\r\n abstract readonly key: string;\r\n abstract readonly isBinary: boolean;\r\n /** Whether this format uses compact method hashes (needs RpcMethodRegistry). */\r\n abstract readonly isCompact: boolean;\r\n\r\n /** Serialize an outbound message. */\r\n abstract serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcWireData;\r\n\r\n /** Deserialize a single text message (only called for text formats). */\r\n abstract deserializeTextMessage(raw: string): RpcDeserializedMessage;\r\n\r\n /** Split + deserialize a binary frame (only called for binary formats). */\r\n abstract splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcDeserializedMessage[];\r\n\r\n /** Concatenate multiple serialized binary messages into one frame. */\r\n abstract serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;\r\n\r\n /** Create a reusable binary encoder for this format. Returns undefined for text formats. */\r\n createEncoder(): Encoder | undefined {\r\n return undefined;\r\n }\r\n\r\n /** Create a reusable binary decoder for this format. Returns undefined for text formats. */\r\n createDecoder(): Decoder | undefined {\r\n return undefined;\r\n }\r\n\r\n // Named format instances — initialized in initFormats() below (after subclasses are defined)\r\n static SystemJsonV5: RpcSerializationFormat;\r\n static SystemJsonV5NP: RpcSerializationFormat;\r\n static NewtonsoftJsonV5: RpcSerializationFormat;\r\n static NewtonsoftJsonV5NP: RpcSerializationFormat;\r\n static MessagePackV6: RpcSerializationFormat;\r\n static MemoryPackV6: RpcSerializationFormat;\r\n static MessagePackV6C: RpcSerializationFormat;\r\n static MemoryPackV6C: RpcSerializationFormat;\r\n static All: readonly RpcSerializationFormat[];\r\n\r\n /** Convenience shortcut for RpcSerializationFormatResolver.Default.get(key). */\r\n static get(key: string): RpcSerializationFormat {\r\n return RpcSerializationFormatResolver.Default.get(key);\r\n }\r\n\r\n /** Convenience shortcut for RpcSerializationFormatResolver.Default.tryGet(key). */\r\n static tryGet(key: string): RpcSerializationFormat | undefined {\r\n return RpcSerializationFormatResolver.Default.tryGet(key);\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Concrete formats (immutable singletons)\r\n// ============================================================\r\n\r\n/**\r\n * JSON text format — \"json5np\" (System.Text.Json, no polymorphism).\r\n */\r\nexport class RpcJsonSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = false;\r\n readonly isCompact = false;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(message: RpcMessage, args?: unknown[]): string {\r\n return serializeMessage(message, args);\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(): RpcDeserializedMessage[] {\r\n throw new Error('Text format does not support binary frames');\r\n }\r\n\r\n serializeBinaryFrame(): Uint8Array {\r\n throw new Error('Text format does not support binary frames');\r\n }\r\n}\r\n\r\n/**\r\n * MessagePack binary format — \"msgpack6\" / \"mempack6\" (V5 wire format).\r\n */\r\nexport class RpcMessagePackSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = true;\r\n readonly isCompact = false;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder\r\n ): Uint8Array {\r\n return serializeBinaryMessage(message, args, encoder);\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder\r\n ): RpcDeserializedMessage[] {\r\n return splitBinaryFrame(frame, decoder);\r\n }\r\n\r\n serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return serializeBinaryFrame(messages);\r\n }\r\n\r\n override createEncoder(): Encoder {\r\n return createBinaryEncoder();\r\n }\r\n\r\n override createDecoder(): Decoder {\r\n return new Decoder();\r\n }\r\n}\r\n\r\n/**\r\n * MessagePack compact binary format — \"msgpack6c\" / \"mempack6c\" (V5Compact).\r\n * Uses 4-byte method hash instead of full method name.\r\n * Registry is passed via parameters — format instance is immutable.\r\n */\r\nexport class RpcMessagePackCompactSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = true;\r\n readonly isCompact = true;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder,\r\n registry?: RpcMethodRegistry\r\n ): Uint8Array {\r\n return serializeCompactBinaryMessage(\r\n message,\r\n args,\r\n registry,\r\n encoder\r\n );\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcDeserializedMessage[] {\r\n return splitCompactBinaryFrame(frame, registry, decoder);\r\n }\r\n\r\n serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return serializeBinaryFrame(messages);\r\n }\r\n\r\n override createEncoder(): Encoder {\r\n return createBinaryEncoder();\r\n }\r\n\r\n override createDecoder(): Decoder {\r\n return new Decoder();\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Initialize named format instances (after subclasses are defined)\r\n// ============================================================\r\n\r\n// Text: System.Text.Json\r\nRpcSerializationFormat.SystemJsonV5 = new RpcJsonSerializationFormat('json5');\r\nRpcSerializationFormat.SystemJsonV5NP = new RpcJsonSerializationFormat('json5np');\r\n// Text: Newtonsoft.Json\r\nRpcSerializationFormat.NewtonsoftJsonV5 = new RpcJsonSerializationFormat('njson5');\r\nRpcSerializationFormat.NewtonsoftJsonV5NP = new RpcJsonSerializationFormat('njson5np');\r\n// Binary: MessagePack (non-compact)\r\nRpcSerializationFormat.MessagePackV6 = new RpcMessagePackSerializationFormat('msgpack6');\r\nRpcSerializationFormat.MemoryPackV6 = new RpcMessagePackSerializationFormat('mempack6');\r\n// Binary: MessagePack (compact)\r\nRpcSerializationFormat.MessagePackV6C = new RpcMessagePackCompactSerializationFormat('msgpack6c');\r\nRpcSerializationFormat.MemoryPackV6C = new RpcMessagePackCompactSerializationFormat('mempack6c');\r\n\r\nRpcSerializationFormat.All = [\r\n RpcSerializationFormat.SystemJsonV5,\r\n RpcSerializationFormat.SystemJsonV5NP,\r\n RpcSerializationFormat.NewtonsoftJsonV5,\r\n RpcSerializationFormat.NewtonsoftJsonV5NP,\r\n RpcSerializationFormat.MessagePackV6,\r\n RpcSerializationFormat.MemoryPackV6,\r\n RpcSerializationFormat.MessagePackV6C,\r\n RpcSerializationFormat.MemoryPackV6C,\r\n];\r\n\r\n// ============================================================\r\n// RpcSerializationFormatResolver — mirrors .NET RpcSerializationFormatResolver\r\n// ============================================================\r\n\r\n/**\r\n * Resolves RpcSerializationFormat instances by key, with a configurable default.\r\n * Mirrors .NET RpcSerializationFormatResolver.\r\n */\r\nexport class RpcSerializationFormatResolver {\r\n static DefaultFormats: readonly RpcSerializationFormat[] = RpcSerializationFormat.All;\r\n static Default = new RpcSerializationFormatResolver('json5np');\r\n\r\n readonly defaultFormatKey: string;\r\n readonly formats: ReadonlyMap<string, RpcSerializationFormat>;\r\n\r\n get defaultFormat(): RpcSerializationFormat {\r\n return this.get(this.defaultFormatKey);\r\n }\r\n\r\n constructor(defaultFormatKey: string, formats?: Iterable<RpcSerializationFormat>) {\r\n const map = new Map<string, RpcSerializationFormat>();\r\n for (const f of formats ?? RpcSerializationFormatResolver.DefaultFormats)\r\n map.set(f.key, f);\r\n\r\n if (!map.has(defaultFormatKey))\r\n throw new Error(`No format with key '${defaultFormatKey}'.`);\r\n\r\n this.defaultFormatKey = defaultFormatKey;\r\n this.formats = map;\r\n }\r\n\r\n get(key: string): RpcSerializationFormat {\r\n const format = this.tryGet(key);\r\n if (!format)\r\n throw new Error(`No format with key '${key}'.`);\r\n return format;\r\n }\r\n\r\n tryGet(key: string): RpcSerializationFormat | undefined {\r\n if (!key) key = this.defaultFormatKey;\r\n return this.formats.get(key);\r\n }\r\n}\r\n","/**\n * Configurable timing limits for RPC connection lifecycle. Mirrors .NET's\n * `RpcLimits` (src/ActualLab.Rpc/Configuration/RpcLimits.cs).\n *\n * Override paths, from broadest to narrowest:\n * - **Process-wide**: mutate fields on {@link RpcLimits.Default} (affects\n * every hub that hasn't been given its own instance — hubs default to\n * using the shared `Default`, so mutations propagate live), or replace\n * `RpcLimits.Default = new RpcLimits({ … })` (affects hubs constructed\n * *after* the replacement).\n * - **Per-hub**: assign `hub.limits = new RpcLimits({ … })` before peers\n * are created from that hub.\n * - **Per-peer**: set the matching `*Ms` field on the peer directly\n * (e.g. `peer.keepAliveTimeoutMs = 60_000`) before `start()`.\n *\n * Peers snapshot the relevant values from `hub.limits` at construction —\n * later mutations to `hub.limits` don't retroactively affect already-built\n * peers; that's by design and matches the .NET semantics.\n */\nexport class RpcLimits {\n /** Process-wide default. Hubs read from this when they're constructed\n * unless caller assigns a custom instance to `hub.limits`. */\n static Default: RpcLimits = new RpcLimits();\n\n /** Max time to wait for the WebSocket to enter the OPEN state. On a\n * hung connect (mobile after network change, half-open after sleep)\n * the browser can take ~2 min to emit `onerror`/`onclose`; without\n * this cap the reconnect loop blocks for that whole window. */\n connectTimeoutMs = 10_000;\n\n /** Max time to wait for the server's handshake response after WS opens. */\n handshakeTimeoutMs = 10_000;\n\n /** Outbound `$sys.KeepAlive` send period. */\n keepAlivePeriodMs = 10_000;\n\n /** Reaper threshold — connection is force-closed (and the reconnect\n * loop takes over) if no inbound `$sys.KeepAlive` has been seen for\n * this long. Sized to tolerate a complete server stall plus most of\n * one keepalive cycle: the worst-case age of `_lastKeepAliveAt` is\n * `keepAlivePeriodMs + stall_duration`, so with these defaults the\n * tolerated stall is ~15 s. */\n keepAliveTimeoutMs = 25_000;\n\n constructor(overrides?: Partial<RpcLimits>) {\n if (overrides) Object.assign(this, overrides);\n }\n}\n","// .NET counterpart: ActualLab.Rpc.RpcClientPeerReconnectDelayer\n\nimport { RetryDelaySeq, RetryDelayer } from '@actuallab/core';\n\nexport class RpcClientPeerReconnectDelayer extends RetryDelayer {\n constructor() {\n super();\n // Fixed 0.1s — callers gate *whether* to reconnect via app-level\n // signals (see Api / ApiReconnectDelayer). Exponential backoff would\n // just add latency on a flaky drop once the gate says \"go\".\n this.delays = RetryDelaySeq.fixed(100);\n }\n}\n","// .NET counterparts:\r\n// RpcServiceBase (17 lines) — abstract base class providing DI access (Services,\r\n// Hub, Log). All RPC service implementations inherit from it.\r\n// RpcServiceRegistry — builds the full map of all services and their method defs\r\n// at startup; provides GetServerMethodResolver() for version-aware method\r\n// lookup. Inbound call dispatch is handled by RpcInboundCall.Process() which\r\n// invokes the method via a compiled delegate chain (MethodDef.InboundCallInvoker).\r\n//\r\n// Omitted from .NET:\r\n// - Compiled delegate pipeline — .NET pre-compiles per-method invoker delegates\r\n// (InboundCallServerInvokerFactory<T>, InboundCallMiddlewareInvokerFactory<T>)\r\n// using generic type instantiation. These invokers handle deserialization,\r\n// middleware chain, server method invocation, and result serialization — all\r\n// as a strongly-typed Task<TResult>. TS uses a simple async function dispatch\r\n// via fn(...args) because there's no middleware pipeline and JSON handles any\r\n// type.\r\n// - ServiceResolver (DI-based lazy service resolution) — .NET resolves the\r\n// service implementation from DI on first use. TS receives the impl object\r\n// directly in register().\r\n// - RpcServiceMode (Local/Client/Server/Hybrid) — .NET classifies each service\r\n// registration. TS has no mode; services are either registered (server) or\r\n// proxied (client).\r\n// - Version-aware method resolution (ServerMethodResolver with legacy names) —\r\n// .NET can resolve methods by legacy names for rolling-upgrade compatibility.\r\n// TS has no versioning.\r\n// - RpcInboundContext / AsyncLocal — .NET sets thread-local inbound context\r\n// so system call handlers can access the current peer/message. TS passes\r\n// context explicitly as a function argument (RpcDispatchContext).\r\n\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcMethodDef } from './rpc-service-def.js';\r\nimport { wireMethodName } from './rpc-service-def.js';\r\nimport type { RpcServiceDef } from './rpc-service-def.js';\r\nimport type { RpcConnection } from './rpc-connection.js';\r\n\r\nconst { warnLog } = getLogs('RpcServiceHost');\r\n\r\nexport type RpcServiceImpl = Record<string, ((...args: unknown[]) => unknown) | undefined>;\r\n\r\n/** Context passed to service dispatch — carries callId and connection for compute tracking. */\r\nexport interface RpcDispatchContext {\r\n __rpcDispatch: true;\r\n callId: number;\r\n connection: RpcConnection;\r\n}\r\n\r\n/** Dispatches inbound RPC calls to registered service implementations. */\r\nexport class RpcServiceHost {\r\n private _methods = new Map<\r\n string,\r\n { def: RpcMethodDef; fn: (...args: unknown[]) => unknown }\r\n >();\r\n\r\n register(def: RpcServiceDef, impl: RpcServiceImpl): void {\r\n for (const methodDef of def.methods.values()) {\r\n const fn = impl[methodDef.name];\r\n if (!fn) continue;\r\n this._methods.set(wireMethodName(methodDef), {\r\n def: methodDef,\r\n fn,\r\n });\r\n }\r\n }\r\n\r\n async dispatch(\r\n wireMethod: string,\r\n args: unknown[],\r\n context?: RpcDispatchContext\r\n ): Promise<unknown> {\r\n const entry = this._methods.get(wireMethod);\r\n if (entry === undefined) {\r\n // Mirrors .NET: missing method is sent back as $sys.Error; warn for visibility.\r\n warnLog?.log(`Method not found: ${wireMethod}`);\r\n throw new Error(`Method not found: ${wireMethod}`);\r\n }\r\n\r\n // Pass context as the last arg only for custom call types (e.g., compute) — their\r\n // wrapped functions use it; regular methods should not see it to avoid polluting ...args.\r\n if (context !== undefined && entry.def.callTypeId !== 0) {\r\n return await entry.fn(...args, context);\r\n }\r\n return await entry.fn(...args);\r\n }\r\n\r\n getMethodDef(wireMethod: string): RpcMethodDef | undefined {\r\n return this._methods.get(wireMethod)?.def;\r\n }\r\n}\r\n","// .NET counterpart:\r\n// RpcMethodAttribute — optional attribute that can override a method's wire name.\r\n// Method metadata (arg count, return type, NoWait, stream, etc.) is discovered\r\n// automatically via reflection on the service interface.\r\n// Interface-based metadata — .NET discovers all methods by reflecting on the\r\n// service interface type. NoWait is determined by return type == Task<RpcNoWait>.\r\n// Stream is determined by return type being IAsyncEnumerable<T>.\r\n//\r\n// Omitted from .NET:\r\n// - Reflection-based auto-discovery — .NET scans interface methods, parameter\r\n// types, and return types at startup. TS uses explicit decorator metadata\r\n// because TypeScript erases type information at runtime.\r\n// - RpcMethodAttribute.Name — custom wire name override. TS decorators don't\r\n// support custom wire names (always \"ServiceName.methodName\").\r\n// - RpcServiceAttribute — .NET has no class-level decorator; the service name\r\n// is configured in RpcServiceBuilder via DI. TS uses @rpcService(\"name\")\r\n// class decorator as a convenient alternative to DI configuration.\r\n// - Automatic NoWait detection from return type — .NET checks if return type\r\n// is Task<RpcNoWait>. TS requires explicit { returns: RpcType.noWait }\r\n// because return types are erased.\r\n// - Automatic stream detection from return type — .NET checks for\r\n// IAsyncEnumerable<T>. TS requires explicit { returns: RpcType.stream }.\r\n// - compute flag in method metadata — stored here for FusionHub's\r\n// addServiceFromContract() which reads decorator metadata to build a\r\n// RpcServiceDef. .NET determines this via the service interface hierarchy\r\n// (IComputeService marker interface).\r\n\r\nconst METHODS_META = Symbol.for('actuallab.methods');\r\nconst SERVICE_META = Symbol.for('actuallab.service');\r\n\r\nexport interface MethodMeta {\r\n argCount: number;\r\n returns?: symbol;\r\n /** Bitfield of RpcRemoteExecutionMode flags. */\r\n remoteExecutionMode?: number;\r\n [key: symbol]: unknown;\r\n}\r\n\r\nexport interface ServiceMeta {\r\n name: string;\r\n}\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- constructor type requires any for maximum compatibility\r\nexport type AnyConstructor = abstract new (...args: any[]) => any;\r\n\r\n/** Read method metadata from a decorated class. */\r\nexport function getMethodsMeta(\r\n cls: AnyConstructor,\r\n): Record<string, MethodMeta> | undefined {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- Symbol.metadata is not in standard typings\r\n return (cls as any)[Symbol.metadata]?.[METHODS_META] as Record<string, MethodMeta> | undefined;\r\n}\r\n\r\n/** Read service metadata from a decorated class. */\r\nexport function getServiceMeta(\r\n cls: AnyConstructor,\r\n): ServiceMeta | undefined {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- Symbol.metadata is not in standard typings\r\n return (cls as any)[Symbol.metadata]?.[SERVICE_META] as ServiceMeta | undefined;\r\n}\r\n\r\n/** Class decorator — stores the RPC service wire name. */\r\nexport function rpcService(serviceName: string) {\r\n return function <T extends AnyConstructor>(\r\n _target: T,\r\n context: ClassDecoratorContext<T>,\r\n ): void {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- decorator metadata requires untyped access\r\n const meta: ServiceMeta = ((context.metadata as any)[SERVICE_META] ??=\r\n {} as ServiceMeta);\r\n meta.name = serviceName;\r\n };\r\n}\r\n\r\n/** Method decorator — stores RPC method metadata (argCount, returns, remoteExecutionMode). Does NOT wrap the method. */\r\nexport function rpcMethod(options?: { returns?: symbol; remoteExecutionMode?: number }) {\r\n return function <This, Args extends unknown[], Return>(\r\n target: (this: This, ...args: Args) => Return,\r\n context: ClassMethodDecoratorContext<\r\n This,\r\n (this: This, ...args: Args) => Return\r\n >,\r\n ): (this: This, ...args: Args) => Return {\r\n const methodName = String(context.name);\r\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- decorator metadata requires untyped access */\r\n const methods: Record<string, MethodMeta> = ((context.metadata as any)[\r\n METHODS_META\r\n ] ??= {});\r\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any */\r\n methods[methodName] = {\r\n ...methods[methodName],\r\n argCount: target.length,\r\n returns: options?.returns,\r\n remoteExecutionMode: options?.remoteExecutionMode,\r\n };\r\n return target; // no wrapping — RPC proxy handles invocation\r\n };\r\n}\r\n","// Pure TypeScript XXH3-64 implementation using BigInt.\r\n// Sync, zero dependencies. Supports inputs of any length.\r\n//\r\n// Reference: https://github.com/Cyan4973/xxHash/blob/dev/xxhash.h\r\n// Verified against .NET System.IO.Hashing.XxHash3.HashToUInt64.\r\n\r\n \r\n\r\nconst U64_MASK = 0xffff_ffff_ffff_ffffn;\r\nconst U32_MASK = 0xffff_ffffn;\r\n\r\nconst PRIME32_1 = 0x9e3779b1;\r\nconst PRIME32_2 = 0x85ebca77;\r\nconst PRIME32_3 = 0xc2b2ae3d;\r\n\r\nconst PRIME64_1 = 0x9e3779b185ebca87n;\r\nconst PRIME64_2 = 0xc2b2ae3d27d4eb4fn;\r\nconst PRIME64_3 = 0x165667b19e3779f9n;\r\nconst PRIME64_4 = 0x85ebca77c2b2ae63n;\r\nconst PRIME64_5 = 0x27d4eb2f165667c5n;\r\n\r\nconst XXH3_SECRET_SIZE_MIN = 136;\r\nconst XXH_SECRET_DEFAULT_SIZE = 192;\r\n\r\nconst STRIPE_LEN = 64;\r\nconst XXH_SECRET_CONSUME_RATE = 8;\r\nconst ACC_NB = STRIPE_LEN / 8;\r\n\r\nconst XXH3_MIDSIZE_MAX = 240;\r\nconst XXH3_MIDSIZE_STARTOFFSET = 3;\r\nconst XXH3_MIDSIZE_LASTOFFSET = 17;\r\nconst XXH_SECRET_LASTACC_START = 7;\r\nconst XXH_SECRET_MERGEACCS_START = 11;\r\n\r\nconst RRMXMX = 0x9fb21c651e98df25n;\r\n\r\n// prettier-ignore\r\nconst DEFAULT_SECRET = new Uint8Array([\r\n 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,\r\n 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,\r\n 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,\r\n 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,\r\n 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,\r\n 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,\r\n 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,\r\n 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,\r\n 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,\r\n 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,\r\n 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,\r\n 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,\r\n]);\r\n\r\nfunction u64(x: bigint): bigint {\r\n return x & U64_MASK;\r\n}\r\n\r\nfunction read32LE(buf: Uint8Array, off: number): number {\r\n return (\r\n (buf[off] |\r\n (buf[off + 1] << 8) |\r\n (buf[off + 2] << 16) |\r\n (buf[off + 3] << 24)) >>>\r\n 0\r\n );\r\n}\r\n\r\nfunction read64LE(buf: Uint8Array, off: number): bigint {\r\n return (\r\n BigInt(buf[off]) |\r\n (BigInt(buf[off + 1]) << 8n) |\r\n (BigInt(buf[off + 2]) << 16n) |\r\n (BigInt(buf[off + 3]) << 24n) |\r\n (BigInt(buf[off + 4]) << 32n) |\r\n (BigInt(buf[off + 5]) << 40n) |\r\n (BigInt(buf[off + 6]) << 48n) |\r\n (BigInt(buf[off + 7]) << 56n)\r\n );\r\n}\r\n\r\nfunction write64LE(buf: Uint8Array, off: number, value: bigint): void {\r\n let x = u64(value);\r\n for (let i = 0; i < 8; i++) {\r\n buf[off + i] = Number(x & 0xffn);\r\n x >>= 8n;\r\n }\r\n}\r\n\r\nfunction swap32(x: number): number {\r\n return (\r\n (((x << 24) & 0xff000000) |\r\n ((x << 8) & 0x00ff0000) |\r\n ((x >>> 8) & 0x0000ff00) |\r\n ((x >>> 24) & 0x000000ff)) >>>\r\n 0\r\n );\r\n}\r\n\r\nfunction swap64(x: bigint): bigint {\r\n x = u64(x);\r\n return (\r\n ((x << 56n) & 0xff00000000000000n) |\r\n ((x << 40n) & 0x00ff000000000000n) |\r\n ((x << 24n) & 0x0000ff0000000000n) |\r\n ((x << 8n) & 0x000000ff00000000n) |\r\n ((x >> 8n) & 0x00000000ff000000n) |\r\n ((x >> 24n) & 0x0000000000ff0000n) |\r\n ((x >> 40n) & 0x000000000000ff00n) |\r\n ((x >> 56n) & 0x00000000000000ffn)\r\n );\r\n}\r\n\r\nfunction rotl64(x: bigint, amount: number): bigint {\r\n const r = BigInt(amount & 63);\r\n return u64((x << r) | (x >> (64n - r)));\r\n}\r\n\r\nfunction mul128Fold64(lhs: bigint, rhs: bigint): bigint {\r\n const product = u64(lhs) * u64(rhs);\r\n return u64(product) ^ u64(product >> 64n);\r\n}\r\n\r\n// XXH64_avalanche — used by len_0 and len_1to3\r\nfunction xxh64Avalanche(hash: bigint): bigint {\r\n let h = u64(hash);\r\n h ^= h >> 33n;\r\n h = u64(h * PRIME64_2);\r\n h ^= h >> 29n;\r\n h = u64(h * PRIME64_3);\r\n h ^= h >> 32n;\r\n return u64(h);\r\n}\r\n\r\n// XXH3_avalanche — used by len_9to16, len_17to128, len_129to240\r\nfunction xxh3Avalanche(hash: bigint): bigint {\r\n let h = u64(hash);\r\n h ^= h >> 37n;\r\n h = u64(h * 0x165667919e3779f9n);\r\n h ^= h >> 32n;\r\n return u64(h);\r\n}\r\n\r\nfunction len0(secret: Uint8Array, seed: bigint): bigint {\r\n const acc = u64(seed ^ read64LE(secret, 56) ^ read64LE(secret, 64));\r\n return xxh64Avalanche(acc);\r\n}\r\n\r\nfunction len1to3(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n const byte1 = input[0];\r\n const byte2 = length > 1 ? input[1] : input[0];\r\n const byte3 = input[length - 1];\r\n const combined =\r\n ((byte1 << 16) | (byte2 << 24) | byte3 | (length << 8)) >>> 0;\r\n const bitflip = u64(\r\n BigInt((read32LE(secret, 0) ^ read32LE(secret, 4)) >>> 0) + seed\r\n );\r\n const value64 = u64(BigInt(combined) ^ bitflip);\r\n return xxh64Avalanche(value64);\r\n}\r\n\r\nfunction len4to8(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n const inputHi = read32LE(input, 0);\r\n const inputLo = read32LE(input, length - 4);\r\n const input64 = BigInt(inputLo) | (BigInt(inputHi) << 32n);\r\n let acc = read64LE(secret, 8) ^ read64LE(secret, 16);\r\n seed ^= BigInt(swap32(Number(seed & U32_MASK))) << 32n;\r\n acc = u64(acc - seed);\r\n acc ^= input64;\r\n acc ^= rotl64(acc, 49) ^ rotl64(acc, 24);\r\n acc = u64(acc * RRMXMX);\r\n acc ^= (acc >> 35n) + BigInt(length);\r\n acc = u64(acc * RRMXMX);\r\n acc ^= acc >> 28n;\r\n return u64(acc);\r\n}\r\n\r\nfunction len9to16(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let inputLo = read64LE(secret, 24) ^ read64LE(secret, 32);\r\n let inputHi = read64LE(secret, 40) ^ read64LE(secret, 48);\r\n let acc = BigInt(length);\r\n inputLo = u64(inputLo + seed);\r\n inputHi = u64(inputHi - seed);\r\n inputLo ^= read64LE(input, 0);\r\n inputHi ^= read64LE(input, length - 8);\r\n acc = u64(acc + swap64(inputLo));\r\n acc = u64(acc + inputHi);\r\n acc = u64(acc + mul128Fold64(inputLo, inputHi));\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction mix16B(\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number,\r\n seed: bigint\r\n): bigint {\r\n let lhs = seed;\r\n let rhs = u64(0n - seed);\r\n lhs = u64(lhs + read64LE(secret, secretOff));\r\n rhs = u64(rhs + read64LE(secret, secretOff + 8));\r\n lhs ^= read64LE(input, inputOff);\r\n rhs ^= read64LE(input, inputOff + 8);\r\n return mul128Fold64(lhs, rhs);\r\n}\r\n\r\nfunction len17to128(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let i = Math.floor((length - 1) / 32);\r\n let acc = u64(BigInt(length) * PRIME64_1);\r\n for (; i >= 0; i--) {\r\n acc = u64(acc + mix16B(input, 16 * i, secret, 32 * i, seed));\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n length - 16 * (i + 1),\r\n secret,\r\n 32 * i + 16,\r\n seed\r\n )\r\n );\r\n }\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction len129to240(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let acc = u64(BigInt(length) * PRIME64_1);\r\n const nbRounds = Math.floor(length / 16);\r\n for (let i = 0; i < 8; i++) {\r\n acc = u64(acc + mix16B(input, 16 * i, secret, 16 * i, seed));\r\n }\r\n acc = xxh3Avalanche(acc);\r\n for (let i = 8; i < nbRounds; i++) {\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n 16 * i,\r\n secret,\r\n 16 * (i - 8) + XXH3_MIDSIZE_STARTOFFSET,\r\n seed\r\n )\r\n );\r\n }\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n length - 16,\r\n secret,\r\n XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET,\r\n seed\r\n )\r\n );\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction hashShort(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n if (length <= 16) {\r\n if (length > 8) return len9to16(input, length, secret, seed);\r\n if (length >= 4) return len4to8(input, length, secret, seed);\r\n if (length !== 0) return len1to3(input, length, secret, seed);\r\n return len0(secret, seed);\r\n }\r\n if (length <= 128) return len17to128(input, length, secret, seed);\r\n return len129to240(input, length, secret, seed);\r\n}\r\n\r\n// --- Long input (> 240 bytes) ---\r\n\r\nfunction accumulate512(\r\n acc: bigint[],\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number\r\n): void {\r\n for (let i = 0; i < ACC_NB; i++) {\r\n const dataVal = read64LE(input, inputOff + 8 * i);\r\n const dataKey = dataVal ^ read64LE(secret, secretOff + 8 * i);\r\n acc[i ^ 1] = u64(acc[i ^ 1] + dataVal);\r\n const low32 = dataKey & U32_MASK;\r\n const high32 = dataKey >> 32n;\r\n acc[i] = u64(acc[i] + low32 * high32);\r\n }\r\n}\r\n\r\nfunction scrambleAcc(\r\n acc: bigint[],\r\n secret: Uint8Array,\r\n secretOff: number\r\n): void {\r\n for (let i = 0; i < ACC_NB; i++) {\r\n let x = acc[i];\r\n x ^= x >> 47n;\r\n x ^= read64LE(secret, secretOff + 8 * i);\r\n x = u64(x * BigInt(PRIME32_1));\r\n acc[i] = x;\r\n }\r\n}\r\n\r\nfunction accumulateLoop(\r\n acc: bigint[],\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number,\r\n nbStripes: number\r\n): void {\r\n for (let n = 0; n < nbStripes; n++) {\r\n accumulate512(\r\n acc,\r\n input,\r\n inputOff + n * STRIPE_LEN,\r\n secret,\r\n secretOff + 8 * n\r\n );\r\n }\r\n}\r\n\r\nfunction mix2Accs(\r\n acc: bigint[],\r\n accOff: number,\r\n secret: Uint8Array,\r\n secretOff: number\r\n): bigint {\r\n return mul128Fold64(\r\n acc[accOff] ^ read64LE(secret, secretOff),\r\n acc[accOff + 1] ^ read64LE(secret, secretOff + 8)\r\n );\r\n}\r\n\r\nfunction mergeAccs(\r\n acc: bigint[],\r\n key: Uint8Array,\r\n keyOff: number,\r\n start: bigint\r\n): bigint {\r\n let result = start;\r\n for (let i = 0; i < 4; i++) {\r\n result = u64(result + mix2Accs(acc, 2 * i, key, keyOff + 16 * i));\r\n }\r\n return xxh3Avalanche(result);\r\n}\r\n\r\nfunction hashLong(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n secretSize: number\r\n): bigint {\r\n const nbRounds = Math.floor(\r\n (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE\r\n );\r\n const blockLen = STRIPE_LEN * nbRounds;\r\n const nbBlocks = Math.floor((length - 1) / blockLen);\r\n const nbStripes = Math.floor(\r\n ((length - 1) - blockLen * nbBlocks) / STRIPE_LEN\r\n );\r\n\r\n const acc: bigint[] = [\r\n BigInt(PRIME32_3),\r\n PRIME64_1,\r\n PRIME64_2,\r\n PRIME64_3,\r\n PRIME64_4,\r\n BigInt(PRIME32_2),\r\n PRIME64_5,\r\n BigInt(PRIME32_1),\r\n ];\r\n\r\n for (let n = 0; n < nbBlocks; n++) {\r\n accumulateLoop(acc, input, n * blockLen, secret, 0, nbRounds);\r\n scrambleAcc(acc, secret, secretSize - STRIPE_LEN);\r\n }\r\n\r\n accumulateLoop(acc, input, nbBlocks * blockLen, secret, 0, nbStripes);\r\n\r\n // Last stripe — always applied (C reference: unconditional)\r\n accumulate512(\r\n acc,\r\n input,\r\n length - STRIPE_LEN,\r\n secret,\r\n secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START\r\n );\r\n\r\n return mergeAccs(\r\n acc,\r\n secret,\r\n XXH_SECRET_MERGEACCS_START,\r\n u64(BigInt(length) * PRIME64_1)\r\n );\r\n}\r\n\r\nfunction hashLongWithSeed(\r\n input: Uint8Array,\r\n length: number,\r\n seed: bigint\r\n): bigint {\r\n const secret = new Uint8Array(XXH_SECRET_DEFAULT_SIZE);\r\n for (let i = 0; i < XXH_SECRET_DEFAULT_SIZE / 16; i++) {\r\n const off = 16 * i;\r\n write64LE(secret, off, u64(read64LE(DEFAULT_SECRET, off) + seed));\r\n write64LE(\r\n secret,\r\n off + 8,\r\n u64(read64LE(DEFAULT_SECRET, off + 8) - seed)\r\n );\r\n }\r\n return hashLong(input, length, secret, secret.length);\r\n}\r\n\r\n// --- Public API ---\r\n\r\nconst textEncoder = new TextEncoder();\r\n\r\n/**\r\n * Compute XXH3-64 hash of a byte array. Returns a BigInt (unsigned 64-bit).\r\n * Pure TypeScript, synchronous, supports any input length.\r\n */\r\nexport function xxh3_64(input: Uint8Array, seed = 0n): bigint {\r\n const len = input.length;\r\n if (len <= XXH3_MIDSIZE_MAX) {\r\n return hashShort(input, len, DEFAULT_SECRET, u64(seed));\r\n }\r\n return hashLongWithSeed(input, len, u64(seed));\r\n}\r\n\r\n/**\r\n * Compute XXH3-64 hash of a UTF-8 string. Returns a BigInt (unsigned 64-bit).\r\n */\r\nexport function xxh3_64str(input: string, seed = 0n): bigint {\r\n return xxh3_64(textEncoder.encode(input), seed);\r\n}\r\n\r\n/**\r\n * Compute the RPC method hash matching .NET's RpcMethodRef.ComputeHashCode.\r\n * Returns a 32-bit signed integer (lower 32 bits of XXH3-64 of [prefix + utf8Name]).\r\n *\r\n * The prefix is uint32 LE of (67211 * utf8Name.length), which ensures methods\r\n * with different name lengths but similar text produce different hashes.\r\n *\r\n * Computed on demand — caller should cache the result per method name.\r\n */\r\nexport function computeMethodHash(methodName: string): number {\r\n const utf8 = textEncoder.encode(methodName);\r\n // Prefix: uint32 LE of (67211 * utf8.length)\r\n const prefixValue = Math.imul(67211, utf8.length) >>> 0;\r\n const prefixed = new Uint8Array(4 + utf8.length);\r\n prefixed[0] = prefixValue & 0xff;\r\n prefixed[1] = (prefixValue >>> 8) & 0xff;\r\n prefixed[2] = (prefixValue >>> 16) & 0xff;\r\n prefixed[3] = (prefixValue >>> 24) & 0xff;\r\n prefixed.set(utf8, 4);\r\n const hash64 = xxh3_64(prefixed);\r\n // Truncate to signed int32 (same as C#'s unchecked((int)hash64))\r\n return Number(hash64 & 0xffffffffn) | 0;\r\n}\r\n","// Method name ↔ hash registry for compact binary serialization.\r\n//\r\n// .NET counterpart: RpcMethodResolver — maintains MethodByHashCode dictionary\r\n// for resolving compact 4-byte method hashes back to full method names.\r\n//\r\n// Hashes are computed on demand (first use) and cached. The computeMethodHash\r\n// function uses BigInt-based XXH3-64, which is pure sync TypeScript.\r\n\r\nimport { getLogs } from './logging.js';\r\nimport { computeMethodHash } from './rpc-xxhash3.js';\r\n\r\nconst { warnLog } = getLogs('RpcMethodRegistry');\r\n\r\n/**\r\n * Registry mapping RPC method wire names to/from their 4-byte XXH3 hashes.\r\n * Hashes match .NET's RpcMethodRef.ComputeHashCode.\r\n * Hashes are computed lazily on first access and cached.\r\n */\r\nexport class RpcMethodRegistry {\r\n private _nameToHash = new Map<string, number>();\r\n private _hashToName = new Map<number, string>();\r\n\r\n /** Register a method name and compute its hash. */\r\n register(methodName: string): void {\r\n if (this._nameToHash.has(methodName)) return;\r\n const hash = computeMethodHash(methodName);\r\n\r\n // Check for hash collisions\r\n const existing = this._hashToName.get(hash);\r\n if (existing !== undefined && existing !== methodName) {\r\n warnLog?.log(`hash collision: \"${methodName}\" and \"${existing}\" both hash to ${hash}`);\r\n return;\r\n }\r\n\r\n this._nameToHash.set(methodName, hash);\r\n this._hashToName.set(hash, methodName);\r\n }\r\n\r\n /** Register all wire method names from a service definition. */\r\n registerService(\r\n serviceName: string,\r\n methods: ReadonlyMap<string, { name: string; wireArgCount: number }>\r\n ): void {\r\n for (const [, def] of methods) {\r\n const wireName = `${serviceName}.${def.name}:${def.wireArgCount}`;\r\n this.register(wireName);\r\n }\r\n }\r\n\r\n /** Get the hash for a method name. Computes on demand if not registered. */\r\n getHash(methodName: string): number {\r\n let hash = this._nameToHash.get(methodName);\r\n if (hash === undefined) {\r\n this.register(methodName);\r\n hash = this._nameToHash.get(methodName)!;\r\n }\r\n return hash;\r\n }\r\n\r\n /** Get the method name for a hash. Returns undefined if not registered. */\r\n getName(hash: number): string | undefined {\r\n return this._hashToName.get(hash);\r\n }\r\n\r\n /** Get the hash for a method name, throwing if not registered. */\r\n requireHash(methodName: string): number {\r\n return this.getHash(methodName);\r\n }\r\n}\r\n","// .NET counterpart:\n// RpcHub (141 lines) — a sealed ProcessorBase (IAsyncDisposable) that owns:\n// DI-resolved configuration (RpcRegistryOptions, PeerOptions, InboundCallOptions,\n// OutboundCallOptions, DiagnosticsOptions), SerializationFormats, Middlewares,\n// ClientPeerReconnectDelayer, Limits, SystemClock.\n// Manages peers via ConcurrentDictionary<RpcPeerRef, RpcPeer> with GetPeer()\n// that lazily creates + starts peers. Also has GetClient<T>() / GetServer<T>()\n// that look up services in ServiceRegistry.\n//\n// Omitted from .NET:\n// - DI / IServiceProvider integration — .NET resolves all options, serializers,\n// middleware, delayers, etc. from DI. TS constructs everything directly.\n// - RpcServiceRegistry — .NET builds a full registry of all RPC services at\n// startup via reflection + DI, mapping Type → RpcServiceDef. TS uses explicit\n// addService() / addClient() calls.\n// - GetClient<T>() / GetServer<T>() — .NET resolves typed client/server proxies\n// via the ServiceRegistry + DI. TS uses addClient<T>(peer, def).\n// - RpcPeerRef-keyed peer dictionary — .NET peers are keyed by RpcPeerRef (rich\n// value object encoding route, version, serialization format). TS uses string\n// refs (URL for clients, \"server://{uuid}\" for servers).\n// - Lazy peer creation + Start() — .NET's GetPeer auto-creates and starts a\n// peer's reconnection loop. TS requires explicit addPeer() + run().\n// - DisposeAsync — .NET disposes all peers on hub shutdown. TS has close().\n// - Route change detection (RouteState.WhenChanged → Dispose peer) — .NET\n// watches for load-balancer route changes and auto-disposes stale peers.\n// TS has no routing layer.\n// - Middlewares (IRpcMiddleware[]) — ordered middleware pipeline for inbound\n// calls. TS dispatches directly to service implementations.\n// - RpcConfiguration / Configuration.Freeze() — frozen config snapshot.\n// - HostId / SystemClock — infrastructure services.\n// - DefaultPeer / LoopbackPeer / LocalPeer / NonePeer — cached well-known peers.\n\nimport { getLogs } from './logging.js';\nimport {\n RpcClientPeer,\n RpcServerPeer,\n type RpcPeer,\n type RpcCallOptions,\n} from './rpc-peer.js';\nimport { RpcLimits } from './rpc-limits.js';\nimport { RpcClientPeerReconnectDelayer } from './rpc-client-peer-reconnect-delayer.js';\nimport { RpcServiceHost, type RpcServiceImpl } from './rpc-service-host.js';\nimport type { RpcServiceDef, RpcMethodDef } from './rpc-service-def.js';\nimport { wireMethodName, RpcType, RpcRemoteExecutionMode } from './rpc-service-def.js';\nimport { getServiceMeta, getMethodsMeta, type AnyConstructor } from './rpc-decorators.js';\nimport { RpcSystemCallSender } from './rpc-system-call-sender.js';\nimport { RpcSystemCallHandler } from './rpc-system-call-handler.js';\nimport { RpcStream, parseStreamRef, resolveStreamRefs } from './rpc-stream.js';\nimport { RpcMethodRegistry } from './rpc-method-registry.js';\nimport { RpcSystemCalls } from './rpc-message.js';\n\nconst { warnLog } = getLogs('RpcHub');\n\n/** Factory signature shared by all peer lookup/creation methods. Serialization\n * format is encoded in the URL via `?f=...` (see {@link RpcPeerRefBuilder}),\n * so the factory doesn't need an extra parameter for it. `getClientPeer` /\n * `getServerPeer` cast the result, so the factory returns RpcPeer. */\nexport type RpcPeerFactory = (hub: RpcHub, ref: string) => RpcPeer;\n\n/** Central RPC coordinator — manages peers, services, and configuration. */\nexport class RpcHub {\n readonly hubId: string;\n readonly peers = new Map<string, RpcPeer>();\n readonly serviceHost: RpcServiceHost;\n readonly systemCallSender = new RpcSystemCallSender();\n systemCallHandler: RpcSystemCallHandler = new RpcSystemCallHandler();\n\n /** Method registry for compact format hash ↔ name resolution.\n * Created lazily, populated by addService/addClient. */\n readonly registry = new RpcMethodRegistry();\n\n /** Shared reconnect delayer used by every client peer in this hub. Swap in\n * a custom subclass (e.g. app-level signal-gated) before peers start. */\n reconnectDelayer: RpcClientPeerReconnectDelayer = new RpcClientPeerReconnectDelayer();\n\n /** Connection-lifecycle timing limits. Peers constructed against this hub\n * read their initial `*Ms` field values from this instance; later\n * mutations affect only peers built afterward (existing peers can be\n * retuned by setting their `*Ms` fields directly). Defaults to the\n * process-wide {@link RpcLimits.Default} so a single global override\n * propagates to every hub that hasn't replaced this. */\n limits: RpcLimits = RpcLimits.Default;\n\n /** URL used by {@link defaultPeer} to resolve / create the default client\n * peer. Must be set before the first {@link defaultPeer} access. */\n defaultPeerUrl: string | undefined;\n /** Factory used by {@link defaultPeer} when the peer needs to be created.\n * If left undefined, `getPeer`'s built-in default (RpcClientPeer with\n * format derived from URL or resolver default) is used. */\n defaultPeerFactory: RpcPeerFactory | undefined;\n\n constructor(hubId?: string) {\n this.hubId = hubId ?? crypto.randomUUID();\n this.serviceHost = new RpcServiceHost();\n this.systemCallSender.registry = this.registry;\n // Pre-register system call method names for compact format hash resolution\n for (const methodName of Object.values(RpcSystemCalls)) {\n this.registry.register(methodName);\n }\n }\n\n /** Shortcut for the hub's default client peer. Fails if\n * {@link defaultPeerUrl} is not set. */\n get defaultPeer(): RpcClientPeer {\n if (this.defaultPeerUrl === undefined)\n throw new Error('RpcHub.defaultPeerUrl is not set.');\n return this.getClientPeer(this.defaultPeerUrl, this.defaultPeerFactory);\n }\n\n /** Register a peer under its ref. If a different peer is already registered\n * at the same ref, close it first — the hub guarantees at most one live\n * peer per ref, and no peer exists outside the hub. */\n addPeer(peer: RpcPeer): void {\n const existing = this.peers.get(peer.ref);\n if (existing !== undefined && existing !== peer)\n existing.close(); // close() removes itself from this.peers\n this.peers.set(peer.ref, peer);\n }\n\n removePeer(ref: string): void {\n const peer = this.peers.get(ref);\n if (peer !== undefined) {\n peer.close();\n this.peers.delete(ref);\n // Mirrors RpcHub.cs:137 — \"peer is removed from RpcHub\".\n warnLog?.log(`'${ref}': peer is removed from RpcHub`);\n }\n }\n\n /** Get or create a peer by ref. If the peer does not exist:\n * - with `factory` provided: it is constructed via `factory(this, ref)`;\n * - otherwise: server peers (ref starts with `server://`) get a plain\n * RpcServerPeer, anything else gets a default-configured RpcClientPeer.\n * The created peer is always registered in this hub. */\n getPeer(ref: string, factory?: RpcPeerFactory): RpcPeer {\n const existing = this.peers.get(ref);\n if (existing) return existing;\n\n const peer = factory\n ? factory(this, ref)\n : ref.startsWith('server://')\n ? new RpcServerPeer(this, ref)\n : new RpcClientPeer(this, ref);\n this.addPeer(peer);\n return peer;\n }\n\n /** Get or create a client peer for the given URL. See {@link getPeer} for\n * the factory semantics. */\n getClientPeer(ref: string, factory?: RpcPeerFactory): RpcClientPeer {\n return this.getPeer(ref, factory) as RpcClientPeer;\n }\n\n /** Get or create a server peer for the given ref. See {@link getPeer} for\n * the factory semantics. */\n getServerPeer(ref: string, factory?: RpcPeerFactory): RpcServerPeer {\n return this.getPeer(ref, factory) as RpcServerPeer;\n }\n\n /** Register a service with optional server method wrapping for custom call types. */\n addService(\n defOrContract: RpcServiceDef | (AnyConstructor),\n impl: RpcServiceImpl\n ): void {\n const def = this._resolveServiceDef(defOrContract);\n const wrappedImpl: RpcServiceImpl = {};\n for (const methodDef of def.methods.values()) {\n const fn = impl[methodDef.name];\n if (!fn) continue;\n wrappedImpl[methodDef.name] =\n methodDef.callTypeId !== 0\n ? this._wrapServerMethod(methodDef, fn, impl as object)\n : fn;\n }\n this.serviceHost.register(def, wrappedImpl);\n this.registry.registerService(def.name, def.methods);\n }\n\n /** Create a typed client proxy for a service on a remote peer. */\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type\n addClient<T extends object>(\n peer: RpcPeer,\n defOrContract: RpcServiceDef | (AnyConstructor)\n ): T {\n const def = this._resolveServiceDef(defOrContract);\n this.registry.registerService(def.name, def.methods);\n\n type RpcClientFn = (...args: unknown[]) => unknown;\n\n // Group methods by clean name, indexed by argCount for overload resolution\n const overloads = new Map<string, Map<number, RpcClientFn>>();\n for (const methodDef of def.methods.values()) {\n let byArgCount = overloads.get(methodDef.name);\n if (!byArgCount) {\n byArgCount = new Map<number, RpcClientFn>();\n overloads.set(methodDef.name, byArgCount);\n }\n byArgCount.set(\n methodDef.argCount,\n this._createClientMethod(peer, methodDef)\n );\n }\n\n // Build final proxy methods — single overload: use directly; multiple: resolve by args.length\n const methods = new Map<string, RpcClientFn>();\n for (const [name, byArgCount] of overloads) {\n if (byArgCount.size === 1) {\n const [[, singleFn]] = byArgCount;\n methods.set(name, singleFn);\n } else {\n methods.set(name, (...args: unknown[]) => {\n const fn = byArgCount.get(args.length);\n if (!fn)\n throw new Error(\n `No overload of ${name} accepts ${args.length} arguments`\n );\n return fn(...args);\n });\n }\n }\n\n return new Proxy({} as T, {\n get: (_target, prop) =>\n typeof prop === 'string' ? methods.get(prop) : undefined,\n });\n }\n\n close(): void {\n for (const peer of this.peers.values()) peer.close();\n this.peers.clear();\n }\n\n /** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */\n protected _buildServiceDef(\n cls: AnyConstructor\n ): RpcServiceDef {\n const svcMeta = getServiceMeta(cls);\n if (svcMeta === undefined)\n throw new Error('Contract class missing @rpcService metadata');\n\n const methodsMeta = getMethodsMeta(cls) ?? {};\n const methods = new Map<string, RpcMethodDef>();\n\n for (const [name, meta] of Object.entries(methodsMeta)) {\n const wireArgCount = meta.argCount + 1;\n const mapKey = `${name}:${wireArgCount}`;\n const isNoWait = meta.returns === RpcType.noWait;\n methods.set(mapKey, {\n name,\n serviceName: svcMeta.name,\n argCount: meta.argCount,\n wireArgCount,\n callTypeId: 0,\n stream: meta.returns === RpcType.stream,\n noWait: isNoWait,\n remoteExecutionMode: isNoWait\n ? 0\n : (meta.remoteExecutionMode ?? RpcRemoteExecutionMode.Default),\n });\n }\n\n return { name: svcMeta.name, methods };\n }\n\n /** Resolve a service def from either a plain RpcServiceDef or a contract class. */\n protected _resolveServiceDef(\n defOrContract: RpcServiceDef | (AnyConstructor)\n ): RpcServiceDef {\n if (typeof defOrContract === 'function')\n return this._buildServiceDef(defOrContract);\n return defOrContract;\n }\n\n /** Override in FusionHub — default is pass-through (for methods with callTypeId !== 0). */\n protected _wrapServerMethod(\n _methodDef: RpcMethodDef,\n fn: (...args: unknown[]) => unknown,\n _impl: object\n ): (...args: unknown[]) => unknown {\n return fn;\n }\n\n /** Create a client method for the given methodDef. Override in FusionHub for custom call types. */\n protected _createClientMethod(\n peer: RpcPeer,\n methodDef: RpcMethodDef\n ): (...args: unknown[]) => unknown {\n const wireName = wireMethodName(methodDef);\n const mode = methodDef.remoteExecutionMode;\n const mustCheckConnection = !(mode & RpcRemoteExecutionMode.AwaitForConnection);\n\n // NoWait methods — remoteExecutionMode is always 0, silently drop if disconnected\n if (methodDef.noWait) {\n return (...args: unknown[]) => {\n const callArgs = args.slice(0, methodDef.argCount);\n peer.callNoWait(wireName, callArgs);\n };\n }\n\n // Stream methods — call returns an RpcStream<T> (AsyncIterable)\n if (methodDef.stream) {\n return async (...args: unknown[]) => {\n if (mustCheckConnection && !peer.isConnected)\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\n const callArgs = args.slice(0, methodDef.argCount);\n const outboundCall = peer.call(wireName, callArgs);\n const result = await outboundCall.result.promise;\n const ref = parseStreamRef(result);\n if (!ref)\n throw new Error(\n `Expected stream reference, got: ${JSON.stringify(result)}`\n );\n const stream = new RpcStream(ref, peer);\n peer.remoteObjects.register(stream);\n return stream;\n };\n }\n\n // Regular methods (including non-zero callTypeId — subclass can override for custom behavior)\n const callOptions: RpcCallOptions | undefined =\n (methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default)\n ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode }\n : undefined;\n\n return async (...args: unknown[]) => {\n if (mustCheckConnection && !peer.isConnected)\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\n const callArgs = args.slice(0, methodDef.argCount);\n const outboundCall = peer.call(wireName, callArgs, callOptions);\n const result = await outboundCall.result.promise;\n return resolveStreamRefs(result, peer);\n };\n }\n}\n","// Ref composition helpers for RPC peers.\n//\n// `RpcClientPeer`'s ref is its full WebSocket URL. The serialization format is\n// encoded as the `f=` query parameter (see RpcClientPeer ctor).\n// `RpcServerPeer`'s ref is `server://{id}`.\n//\n// Typical use:\n// rpcHub.defaultPeerUrl = RpcPeerRefBuilder.forClient('wss://host/rpc/ws', 'msgpack6');\n// const serverRef = RpcPeerRefBuilder.forServer(crypto.randomUUID());\n\nexport class RpcPeerRefBuilder {\n /** Build a client peer ref from a WebSocket URL, optionally baking in the\n * serialization format as a `f=` query parameter.\n * @param url Base WebSocket URL, e.g. `wss://host/rpc/ws`.\n * @param serializationFormat Optional format key, e.g. `msgpack6`. When\n * provided, replaces any existing `f=` in the URL. */\n static forClient(url: string, serializationFormat?: string): string {\n if (serializationFormat === undefined) return url;\n const u = new URL(url);\n u.searchParams.set('f', serializationFormat);\n return u.toString();\n }\n\n /** Build a server peer ref from a peer id — returns `server://{id}`. */\n static forServer(id: string): string {\n return `server://${id}`;\n }\n}\n","// .NET counterpart:\r\n// RpcClient — a DI-registered service used by generated interceptor proxies.\r\n// The proxy classes (generated by Interceptor/ProxyGenerator) intercept method\r\n// calls on the service interface, create an RpcOutboundContext, resolve the\r\n// target peer via OutboundCallRouter, and invoke the outbound call pipeline.\r\n//\r\n// Omitted from .NET:\r\n// - Interceptor-based proxy generation — .NET generates proxy classes at startup\r\n// (via Castle DynamicProxy / source generators) that implement the service\r\n// interface. TS uses a runtime Proxy object instead, which achieves the same\r\n// result without code generation.\r\n// - RpcOutboundContext — .NET creates a rich context per call with CancellationToken,\r\n// Headers, CacheInfoCapture, Trace, RoutingMode. TS passes arguments directly.\r\n// - OutboundCallRouter — .NET routes each call to the appropriate peer based on\r\n// sharding/load-balancing rules. TS passes the peer explicitly.\r\n// - This standalone function is superseded by RpcHub.addClient() which adds\r\n// noWait and compute-aware dispatch. Kept for backward compatibility.\r\n\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport type { RpcServiceDef, RpcMethodDef } from './rpc-service-def.js';\r\nimport { wireMethodName, RpcRemoteExecutionMode } from './rpc-service-def.js';\r\n\r\ntype RpcClientFn = (...args: unknown[]) => unknown;\r\n\r\n/** Creates a typed RPC client proxy — intercepts method calls and sends them over RPC. */\r\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type\r\nexport function createRpcClient<T extends object>(\r\n peer: RpcPeer,\r\n serviceDef: RpcServiceDef\r\n): T {\r\n // Group methods by clean name, indexed by argCount for overload resolution\r\n const overloads = new Map<string, Map<number, RpcMethodDef>>();\r\n for (const methodDef of serviceDef.methods.values()) {\r\n let byArgCount = overloads.get(methodDef.name);\r\n if (!byArgCount) {\r\n byArgCount = new Map<number, RpcMethodDef>();\r\n overloads.set(methodDef.name, byArgCount);\r\n }\r\n byArgCount.set(methodDef.argCount, methodDef);\r\n }\r\n\r\n // Build proxy methods\r\n const methods = new Map<string, RpcClientFn>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n const [[, methodDef]] = byArgCount;\r\n methods.set(name, createMethod(peer, methodDef));\r\n } else {\r\n const fns = new Map<number, RpcClientFn>();\r\n for (const [argCount, methodDef] of byArgCount) {\r\n fns.set(argCount, createMethod(peer, methodDef));\r\n }\r\n methods.set(name, (...args: unknown[]) => {\r\n const fn = fns.get(args.length);\r\n if (!fn)\r\n throw new Error(\r\n `No overload of ${name} accepts ${args.length} arguments`\r\n );\r\n return fn(...args);\r\n });\r\n }\r\n }\r\n\r\n return new Proxy({} as T, {\r\n get(_target, prop) {\r\n if (typeof prop !== 'string') return undefined;\r\n return methods.get(prop);\r\n },\r\n });\r\n}\r\n\r\nfunction createMethod(peer: RpcPeer, methodDef: RpcMethodDef): RpcClientFn {\r\n const mode = methodDef.remoteExecutionMode;\r\n const mustCheckConnection = !(mode & RpcRemoteExecutionMode.AwaitForConnection);\r\n return async (...args: unknown[]) => {\r\n if (mustCheckConnection && !peer.isConnected)\r\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const wireName = wireMethodName(methodDef);\r\n const options =\r\n (methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default)\r\n ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode }\r\n : undefined;\r\n const outboundCall = peer.call(wireName, callArgs, options);\r\n return outboundCall.result.promise;\r\n };\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerState\r\n\r\nexport const enum RpcPeerStateKind {\r\n Connected = 0,\r\n JustDisconnected = 1,\r\n Disconnected = 2,\r\n JustConnected = 3,\r\n}\r\n\r\nexport interface RpcPeerState {\r\n readonly kind: RpcPeerStateKind;\r\n readonly lastError?: Error;\r\n readonly reconnectsIn: number; // ms, 0 = no countdown\r\n}\r\n\r\nexport function isConnected(state: RpcPeerState): boolean {\r\n return (\r\n state.kind === RpcPeerStateKind.Connected ||\r\n state.kind === RpcPeerStateKind.JustConnected\r\n );\r\n}\r\n\r\nexport function likelyConnected(state: RpcPeerState): boolean {\r\n return state.kind !== RpcPeerStateKind.Disconnected;\r\n}\r\n\r\nexport function getStateDescription(\r\n state: RpcPeerState,\r\n useLastError = false\r\n): string {\r\n switch (state.kind) {\r\n case RpcPeerStateKind.JustConnected:\r\n return 'Just connected.';\r\n case RpcPeerStateKind.Connected:\r\n return 'Connected.';\r\n case RpcPeerStateKind.JustDisconnected:\r\n return 'Just disconnected, reconnecting...';\r\n case RpcPeerStateKind.Disconnected:\r\n if (state.reconnectsIn > 0) break; // fall through to error message below\r\n return 'Reconnecting...';\r\n }\r\n if (state.lastError == null || !useLastError) return 'Disconnected.';\r\n\r\n let message = state.lastError.message.trim();\r\n if (\r\n !(\r\n message.endsWith('.') ||\r\n message.endsWith('!') ||\r\n message.endsWith('?')\r\n )\r\n )\r\n message += '.';\r\n return 'Disconnected: ' + message;\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerStateMonitor\n// Simplified TS version using EventHandlerSet + setTimeout (no Fusion computed state).\n\nimport { EventHandlerSet } from '@actuallab/core';\nimport { getLogs } from './logging.js';\nimport { RpcConnectionState, type RpcClientPeer } from './rpc-peer.js';\nimport { type RpcPeerState, RpcPeerStateKind } from './rpc-peer-state.js';\n\nconst { infoLog } = getLogs('RpcPeerStateMonitor');\n\nconst JustConnectedPeriodMs = 1500;\nconst JustDisconnectedPeriodMs = 3000;\nconst MinReconnectsInMs = 1000;\n\nexport class RpcPeerStateMonitor {\n readonly peer: RpcClientPeer;\n readonly stateChanged = new EventHandlerSet<RpcPeerState>();\n\n private _rawConnected: boolean;\n private _connectedAt = 0;\n private _disconnectedAt = 0;\n private _lastError: Error | undefined;\n private _state: RpcPeerState;\n private _timer: ReturnType<typeof setTimeout> | undefined;\n\n // Arrow functions to preserve `this` when used as event handlers\n private _onConnectionStateChanged = (state: RpcConnectionState): void => {\n if (state === RpcConnectionState.Connected) {\n this._rawConnected = true;\n this._connectedAt = Date.now();\n this._lastError = undefined;\n } else {\n const wasConnected = this._rawConnected;\n this._rawConnected = false;\n // We no longer receive a close code/reason — only the state.\n // Retain a generic error for UI hints.\n this._lastError = new Error('Connection closed');\n if (wasConnected) {\n // True connected→disconnected transition — start JustDisconnected period\n this._disconnectedAt = Date.now();\n }\n // When wasConnected=false, this is a failed reconnect attempt —\n // keep the original _disconnectedAt so the countdown isn't reset.\n }\n this._recompute();\n };\n\n private _onReconnectsAtChanged = (): void => {\n this._recompute();\n };\n\n private _onCancelDelaysChanged = (): void => {\n this._recompute();\n };\n\n constructor(peer: RpcClientPeer) {\n this.peer = peer;\n this._rawConnected = peer.isConnected;\n\n if (this._rawConnected) {\n this._connectedAt = Date.now();\n this._state = {\n kind: RpcPeerStateKind.JustConnected,\n reconnectsIn: 0,\n };\n } else {\n this._disconnectedAt = Date.now();\n this._state = {\n kind: RpcPeerStateKind.JustDisconnected,\n reconnectsIn: 0,\n };\n }\n\n peer.connectionStateChanged.add(this._onConnectionStateChanged);\n peer.reconnectsAtChanged.add(this._onReconnectsAtChanged);\n peer.hub.reconnectDelayer.cancelDelaysChanged.add(\n this._onCancelDelaysChanged\n );\n\n // Schedule initial transition from Just* states\n this._scheduleRecompute(\n this._rawConnected\n ? JustConnectedPeriodMs\n : JustDisconnectedPeriodMs\n );\n // Mirrors RpcPeerStateMonitor.cs:87 — \"monitor (re)started\".\n infoLog?.log(`'${peer.ref}': monitor started`);\n }\n\n get state(): RpcPeerState {\n return this._state;\n }\n\n dispose(): void {\n if (this._timer !== undefined) {\n clearTimeout(this._timer);\n this._timer = undefined;\n }\n this.peer.connectionStateChanged.remove(this._onConnectionStateChanged);\n this.peer.reconnectsAtChanged.remove(this._onReconnectsAtChanged);\n this.peer.hub.reconnectDelayer.cancelDelaysChanged.remove(\n this._onCancelDelaysChanged\n );\n // Mirrors RpcPeerStateMonitor.cs:125 — \"monitor stopped\".\n infoLog?.log(`'${this.peer.ref}': monitor stopped`);\n }\n\n private _recompute(): void {\n if (this._timer !== undefined) {\n clearTimeout(this._timer);\n this._timer = undefined;\n }\n\n const now = Date.now();\n let newState: RpcPeerState;\n\n if (this._rawConnected) {\n const connectedFor = now - this._connectedAt;\n if (connectedFor < JustConnectedPeriodMs) {\n newState = {\n kind: RpcPeerStateKind.JustConnected,\n reconnectsIn: 0,\n };\n this._scheduleRecompute(JustConnectedPeriodMs - connectedFor);\n } else {\n newState = {\n kind: RpcPeerStateKind.Connected,\n reconnectsIn: 0,\n };\n }\n } else {\n const disconnectedFor = now - this._disconnectedAt;\n if (disconnectedFor < JustDisconnectedPeriodMs) {\n newState = {\n kind: RpcPeerStateKind.JustDisconnected,\n lastError: this._lastError,\n reconnectsIn: 0,\n };\n this._scheduleRecompute(\n JustDisconnectedPeriodMs - disconnectedFor\n );\n } else {\n const reconnectsAt = this.peer.reconnectsAt;\n const reconnectsIn =\n reconnectsAt > 0 ? Math.max(0, reconnectsAt - now) : 0;\n if (reconnectsIn < MinReconnectsInMs) {\n newState = {\n kind: RpcPeerStateKind.Disconnected,\n lastError: this._lastError,\n reconnectsIn: 0,\n };\n } else {\n newState = {\n kind: RpcPeerStateKind.Disconnected,\n lastError: this._lastError,\n reconnectsIn,\n };\n // Recompute periodically to update countdown\n this._scheduleRecompute(\n Math.min(1000, reconnectsIn - MinReconnectsInMs)\n );\n }\n }\n }\n\n this._setState(newState);\n }\n\n private _setState(newState: RpcPeerState): void {\n const old = this._state;\n if (\n old.kind === newState.kind &&\n old.lastError === newState.lastError &&\n old.reconnectsIn === newState.reconnectsIn\n )\n return;\n\n this._state = newState;\n this.stateChanged.trigger(newState);\n }\n\n private _scheduleRecompute(delayMs: number): void {\n if (this._timer !== undefined) clearTimeout(this._timer);\n this._timer = setTimeout(\n () => {\n this._timer = undefined;\n this._recompute();\n },\n Math.max(0, delayMs)\n );\n }\n}\n"],"mappings":"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;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;A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","names":["RpcCallTypeId","Encoder","Encoder","Decoder","warnLog","Decoder","EventHandlerSet","EventHandlerSet","PromiseSource","PromiseSource","b","PromiseSource","PromiseSource","warnLog","PromiseSource","warnLog","DEFAULT_ACK_PERIOD","DEFAULT_ACK_ADVANCE","PromiseSource","warnLog","EventHandlerSet","PromiseSource","Decoder","Decoder","debugLog","warnLog","errorLog","EventHandlerSet","RpcConnectionState","PromiseSource","bytes","_toBytes","warnLog","textEncoder","warnLog","warnLog","RpcPeerStateKind","EventHandlerSet","infoLog","EventHandlerSet"]}
1
+ {"version":3,"sources":["../src/logging.ts","../src/rpc-message.ts","../src/rpc-serialization.ts","../src/msgpack-map-patch.ts","../src/rpc-connection.ts","../src/rpc-message-channel-connection.ts","../src/rpc-service-def.ts","../src/rpc-call-tracker.ts","../src/rpc-call-stage.ts","../src/rpc-error.ts","../src/increasing-seq-compressor.ts","../src/rpc-stream.ts","../src/rpc-object.ts","../src/rpc-stream-sender.ts","../src/base64.ts","../src/rpc-system-call-handler.ts","../src/rpc-system-call-sender.ts","../src/rpc-remote-object-tracker.ts","../src/rpc-shared-object-tracker.ts","../src/rpc-peer.ts","../src/rpc-serialization-format.ts","../src/rpc-limits.ts","../src/rpc-client-peer-reconnect-delayer.ts","../src/rpc-service-host.ts","../src/rpc-decorators.ts","../src/rpc-xxhash3.ts","../src/rpc-method-registry.ts","../src/rpc-hub.ts","../src/rpc-peer-ref-builder.ts","../src/rpc-client.ts","../src/rpc-peer-state.ts","../src/rpc-peer-state-monitor.ts"],"sourcesContent":["import { createLogProvider, LogLevel } from '@actuallab/core';\n\n// Per-package log scopes. Use createLogProvider to get a typed helper:\n// const { debugLog, warnLog } = getLogs('RpcPeer');\n// Final scope name is 'rpc.RpcPeer' — the prefix isolates this package's\n// scopes from other packages' scopes when overriding levels at runtime.\nexport type RpcLogScope =\n | 'RpcCallTracker'\n | 'RpcClient'\n | 'RpcClientPeerReconnectDelayer'\n | 'RpcConnection'\n | 'RpcHub'\n | 'RpcInterceptor'\n | 'RpcMessageChannelConnection'\n | 'RpcMethodRegistry'\n | 'RpcOutboundCall'\n | 'RpcPeer'\n | 'RpcPeerStateMonitor'\n | 'RpcRemoteObjectTracker'\n | 'RpcSerialization'\n | 'RpcServiceHost'\n | 'RpcSharedObjectTracker'\n | 'RpcSharedStream'\n | 'RpcStream'\n | 'RpcStreamSender'\n | 'RpcSystemCallHandler'\n | 'RpcSystemCallSender'\n | 'RpcWebSocketClient';\n\n// Per-scope defaults. All Warn — output goes to the browser console, so the\n// out-of-the-box experience must be quiet. Users opt in to Info-level\n// connectivity tracing manually:\n// logLevels.override('rpc.*', LogLevel.Info)\n// or for a single scope:\n// logLevels.override('rpc.RpcPeer', LogLevel.Info)\nconst scopeDefaults: Record<RpcLogScope, LogLevel> = {\n RpcCallTracker: LogLevel.Warn,\n RpcClient: LogLevel.Warn,\n RpcClientPeerReconnectDelayer: LogLevel.Warn,\n RpcConnection: LogLevel.Warn,\n RpcHub: LogLevel.Warn,\n RpcInterceptor: LogLevel.Warn,\n RpcMessageChannelConnection: LogLevel.Warn,\n RpcMethodRegistry: LogLevel.Warn,\n RpcOutboundCall: LogLevel.Warn,\n RpcPeer: LogLevel.Info,\n RpcPeerStateMonitor: LogLevel.Warn,\n RpcRemoteObjectTracker: LogLevel.Warn,\n RpcSerialization: LogLevel.Warn,\n RpcServiceHost: LogLevel.Warn,\n RpcSharedObjectTracker: LogLevel.Warn,\n RpcSharedStream: LogLevel.Warn,\n RpcStream: LogLevel.Warn,\n RpcStreamSender: LogLevel.Warn,\n RpcSystemCallHandler: LogLevel.Warn,\n RpcSystemCallSender: LogLevel.Warn,\n RpcWebSocketClient: LogLevel.Warn,\n};\n\nexport const getLogs = createLogProvider<RpcLogScope>('rpc.', scopeDefaults);\n","// .NET counterparts:\n// RpcMessage — .NET has no single \"message\" type; the wire format is built by\n// RpcMessageSerializer / RpcOutboundMessage / RpcInboundMessage. In .NET the\n// envelope carries MethodRef, RelatedId, Headers, CallTypeId, and serialized\n// argument data as binary or text segments. In TS we use a simpler JSON\n// envelope + delimiter-separated args.\n//\n// Omitted from .NET:\n// - RpcCallTypeId Reliable (stage-based reconnection protocol) — the TS client\n// is browser-only; reconnection replays entire calls rather than resuming at\n// a stage, so Reliable call type adds no value.\n// - RpcHeader / RpcHeaders — .NET passes typed header arrays on every message\n// (Hash, ActivityContext, etc.). TS has no RPC caching, OpenTelemetry\n// propagation, or middleware pipeline, so headers aren't needed yet. The\n// Headers field on RpcMessage is reserved for future use.\n// - $sys.M (Match) / $sys.NotFound / $sys.Disconnect / Stream calls\n// (Ack, AckEnd, I, B, End) — Match is for hash-based response caching\n// (bandwidth optimisation); NotFound is a server-side \"service not registered\"\n// response; Disconnect is for RpcSharedObject lifetime management; the stream\n// calls are for RpcStream (server→client IAsyncEnumerable). None of these\n// are needed in the TS client today because:\n// * No response caching → no Match.\n// * Service-not-found is sent as $sys.Error.\n// * No shared-object tracking (server-side concept) → no Disconnect.\n// * No RpcStream yet → no Ack/AckEnd/I/B/End.\n// They can be added when streaming or caching is ported.\n// - Binary serialization variants (RpcByteMessageSerializer) — TS uses\n// text (JSON) exclusively; binary MessagePack/MemoryPack require native\n// codecs that aren't available in browsers.\n\n/** Identifies the type of an RPC call on the wire. */\nexport const enum RpcCallTypeId {\n Regular = 0,\n Reliable = 1,\n}\n\n/** Identifies system call methods — names include :argCount suffix for .NET interop. */\nexport const RpcSystemCalls = {\n handshake: '$sys.Handshake:1',\n ok: '$sys.Ok:1',\n error: '$sys.Error:1',\n cancel: '$sys.Cancel:0',\n keepAlive: '$sys.KeepAlive:1',\n reconnect: '$sys.Reconnect:3',\n ack: '$sys.Ack:2',\n ackEnd: '$sys.AckEnd:1',\n item: '$sys.I:2',\n batch: '$sys.B:2',\n end: '$sys.End:2',\n disconnect: '$sys.Disconnect:1',\n} as const;\n\n/** Wire-format RPC message envelope. */\nexport interface RpcMessage {\n CallType?: number;\n RelatedId?: number;\n Method?: string;\n Headers?: unknown[];\n}\n\n// Delimiters used in the wire format\nexport const ENVELOPE_DELIMITER = '\\n'; // \\x0A between envelope and args\nexport const ARG_DELIMITER = '\\x1F'; // Unit Separator between args\nexport const FRAME_DELIMITER = '\\n\\x1E'; // \\x0A\\x1E between messages in a frame\n","// Serialization for the RPC wire format.\r\n//\r\n// Supports two formats:\r\n// - Text (json5np): JSON envelope + delimiter-separated JSON args (V3 wire format)\r\n// - Binary (msgpack6np): V5 binary envelope + MessagePack-encoded args\r\n//\r\n// The format is selected at connection time via the `f=` query parameter.\r\n\r\nimport {\r\n Encoder,\r\n Decoder,\r\n} from '@msgpack/msgpack';\r\n// Side-effect import: patches Encoder.prototype to handle JS Map instances\r\n// as proper msgpack maps with typed keys (.NET-wire-compatible).\r\nimport './msgpack-map-patch.js';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport {\r\n ENVELOPE_DELIMITER,\r\n ARG_DELIMITER,\r\n FRAME_DELIMITER,\r\n} from './rpc-message.js';\r\n\r\nconst { warnLog } = getLogs('RpcSerialization');\r\n\r\n// ============================================================\r\n// Text format (json5np) — V3 wire format\r\n// ============================================================\r\n\r\n/** Serializes an RpcMessage + args into the json5 wire format. */\r\nexport function serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[]\r\n): string {\r\n const envelope = JSON.stringify(message);\r\n if (args === undefined || args.length === 0) {\r\n return envelope + ENVELOPE_DELIMITER;\r\n }\r\n const argsStr = args.map(a => JSON.stringify(a)).join(ARG_DELIMITER);\r\n return envelope + ENVELOPE_DELIMITER + argsStr;\r\n}\r\n\r\n/** Serializes multiple messages into a single WebSocket frame. */\r\nexport function serializeFrame(messages: string[]): string {\r\n return messages.join(FRAME_DELIMITER);\r\n}\r\n\r\n/** Splits a WebSocket text frame into individual message strings. */\r\nexport function splitFrame(frame: string): string[] {\r\n return frame.split(FRAME_DELIMITER);\r\n}\r\n\r\n/** Deserializes a single message string into envelope + args. */\r\nexport function deserializeMessage(raw: string): {\r\n message: RpcMessage;\r\n args: unknown[];\r\n} {\r\n const nlIndex = raw.indexOf(ENVELOPE_DELIMITER);\r\n if (nlIndex === -1) {\r\n return { message: JSON.parse(raw) as RpcMessage, args: [] };\r\n }\r\n\r\n const envelopeStr = raw.substring(0, nlIndex);\r\n const argsStr = raw.substring(nlIndex + 1);\r\n const message = JSON.parse(envelopeStr) as RpcMessage;\r\n\r\n if (argsStr.length === 0) {\r\n return { message, args: [] };\r\n }\r\n\r\n const args = argsStr.split(ARG_DELIMITER).map(s => {\r\n try {\r\n return JSON.parse(s) as unknown;\r\n } catch {\r\n warnLog?.log(`failed to parse arg as JSON: ${s.substring(0, 100)}`);\r\n return s; // Return raw string if not valid JSON\r\n }\r\n });\r\n\r\n return { message, args };\r\n}\r\n\r\n// ============================================================\r\n// Binary format (msgpack6np) — V5 wire format\r\n// ============================================================\r\n\r\nconst textEncoder = new TextEncoder();\r\nconst textDecoder = new TextDecoder();\r\n\r\n/**\r\n * Default module-level encoder/decoder. RpcWebSocketConnection passes its own\r\n * per-connection instances for the hot path; these defaults cover callers\r\n * that don't bother (tests, one-shot uses).\r\n *\r\n * Reusing an Encoder across calls is critical at high message rates because\r\n * - construction cost (internal resolvers, buffer allocation) is nontrivial;\r\n * - the internal write buffer stays at its largest observed size after the\r\n * first few encodes, eliminating `resizeBuffer` growth churn (which was\r\n * ~5% self time in the 300-pull profile).\r\n *\r\n * NOT thread-safe and NOT re-entrant. Safe under Node's single-threaded event\r\n * loop as long as `serializeBinaryMessage` is called synchronously (which it\r\n * always is — no awaits inside).\r\n *\r\n * Sized large enough to fit a typical video frame (~11 KB) + envelope\r\n * overhead without any `resizeBuffer` growth.\r\n *\r\n * `useBigInt64: true` makes the encoder emit JS `bigint` values as msgpack\r\n * int64/uint64 (0xd3/0xcf) instead of float64. Required for .NET `long`\r\n * fields whose 2026-era values (e.g. `Moment.EpochOffsetTicks` ~1.8×10¹⁶)\r\n * exceed `Number.MAX_SAFE_INTEGER` — a float64 payload would be rejected by\r\n * the server's `ReadInt64` path. Callers must pass a real `bigint` (not a\r\n * `number`) for such fields; `number` values follow the standard\r\n * int-or-float branching regardless of this flag.\r\n */\r\nconst INITIAL_ENCODER_BUFFER_SIZE = 32 * 1024;\r\nexport const defaultBinaryEncoder = new Encoder({ initialBufferSize: INITIAL_ENCODER_BUFFER_SIZE, useBigInt64: true });\r\nexport const defaultBinaryDecoder = new Decoder();\r\n/** Exposed so connection code can instantiate a matching Encoder\r\n * with the same initial buffer size without needing to reach into\r\n * this module's private constants. */\r\nexport function createBinaryEncoder(): Encoder {\r\n return new Encoder({ initialBufferSize: INITIAL_ENCODER_BUFFER_SIZE, useBigInt64: true });\r\n}\r\n\r\n// --- LEB128 VarUInt helpers ---\r\n\r\n/** Number of bytes a VarUint encoding of `value` will occupy. */\r\nfunction varUintByteLen(value: number): number {\r\n if (value < 0) value = 0;\r\n let n = 0;\r\n do {\r\n n++;\r\n value >>>= 7;\r\n } while (value > 0);\r\n return n;\r\n}\r\n\r\n/** Write VarUint directly into a `Uint8Array` at `pos`. Returns new pos. */\r\nfunction writeVarUintInto(out: Uint8Array, pos: number, value: number): number {\r\n if (value < 0) value = 0;\r\n do {\r\n let byte = value & 0x7f;\r\n value >>>= 7;\r\n if (value > 0) byte |= 0x80;\r\n out[pos++] = byte;\r\n } while (value > 0);\r\n return pos;\r\n}\r\n\r\nfunction readVarUint(\r\n data: Uint8Array,\r\n offset: number\r\n): { value: number; bytesRead: number } {\r\n let value = 0,\r\n shift = 0,\r\n bytesRead = 0;\r\n do {\r\n if (offset + bytesRead >= data.length) break;\r\n const byte = data[offset + bytesRead];\r\n value |= (byte & 0x7f) << shift;\r\n bytesRead++;\r\n if ((byte & 0x80) === 0) break;\r\n shift += 7;\r\n } while (shift < 35); // Max 5 bytes for uint32\r\n return { value: value >>> 0, bytesRead };\r\n}\r\n\r\n// --- Binary helpers ---\r\n\r\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\r\n let totalLength = 0;\r\n for (const arr of arrays) totalLength += arr.length;\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const arr of arrays) {\r\n result.set(arr, offset);\r\n offset += arr.length;\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Serializes an RpcMessage + args into V5 binary format (msgpack6np).\r\n *\r\n * Layout (NO frame-level size prefix; V5 with PersistsMessageSize=false):\r\n * envelope + argData\r\n * Envelope:\r\n * [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]\r\n * [LVarSpan methodRef] [4-byte LE argLen]\r\n * ArgData: concatenated MessagePack-encoded arguments\r\n *\r\n * Single-allocation build: pre-encodes args with the supplied reusable\r\n * `Encoder`, computes the total envelope size, then writes everything\r\n * into one `Uint8Array`. Eliminates the previous build's intermediate\r\n * `headerParts` number array + four `Uint8Array` concats per call,\r\n * which showed up as ~2% self time in the 300-pull profile and\r\n * pulled the msgpack `resizeBuffer` + `Encoder` constructor along\r\n * with it for another ~6%.\r\n *\r\n * NOTE: `@msgpack/msgpack` `Encoder.encode()` already returns a fresh\r\n * copy of the internal buffer (there's a separate `encodeSharedRef()`\r\n * for zero-copy access), so we can push the results straight into\r\n * `argBufs` without an extra slice. The benefit of reusing the Encoder\r\n * is that its internal write buffer stays at its high-water mark —\r\n * once the first ~11 KB frame has grown it to the full size, no\r\n * subsequent `resizeBuffer` calls are needed.\r\n */\r\nexport function serializeBinaryMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder: Encoder = defaultBinaryEncoder\r\n): Uint8Array {\r\n // 1. Encode each arg. encode() returns a fresh Uint8Array copy per\r\n // call, so the results are safe to retain across subsequent encodes.\r\n let argsDataLen = 0;\r\n let argBufs: Uint8Array[] | null = null;\r\n if (args && args.length > 0) {\r\n argBufs = new Array<Uint8Array>(args.length);\r\n for (let i = 0; i < args.length; i++) {\r\n const buf = encoder.encode(args[i]);\r\n argBufs[i] = buf;\r\n argsDataLen += buf.length;\r\n }\r\n }\r\n\r\n // 2. Compute header sizes without intermediate allocations.\r\n const callType = message.CallType ?? 0;\r\n const relatedId = message.RelatedId ?? 0;\r\n const methodBytes = textEncoder.encode(message.Method ?? '');\r\n const relatedIdLen = varUintByteLen(relatedId);\r\n const methodLenVarintLen = varUintByteLen(methodBytes.length);\r\n const headerSize =\r\n 1 + relatedIdLen + methodLenVarintLen + methodBytes.length;\r\n const totalSize = headerSize + 4 + argsDataLen;\r\n\r\n // 3. Allocate the final wire buffer once and fill it in place.\r\n const out = new Uint8Array(totalSize);\r\n let pos = 0;\r\n out[pos++] = (callType << 5) & 0xe0;\r\n pos = writeVarUintInto(out, pos, relatedId);\r\n pos = writeVarUintInto(out, pos, methodBytes.length);\r\n out.set(methodBytes, pos);\r\n pos += methodBytes.length;\r\n // argLen as int32 LE\r\n out[pos++] = argsDataLen & 0xff;\r\n out[pos++] = (argsDataLen >>> 8) & 0xff;\r\n out[pos++] = (argsDataLen >>> 16) & 0xff;\r\n out[pos++] = (argsDataLen >>> 24) & 0xff;\r\n if (argBufs !== null) {\r\n for (const buf of argBufs) {\r\n out.set(buf, pos);\r\n pos += buf.length;\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Deserializes a single V5 binary message starting at `offset`.\r\n * Returns parsed message, args, and number of bytes consumed.\r\n *\r\n * Accepts an optional `Decoder` for reuse across calls on the same\r\n * connection — avoids constructing a new `@msgpack/msgpack.Decoder`\r\n * (and its internal resolver/table state) on every inbound message.\r\n */\r\nexport function deserializeBinaryMessage(\r\n data: Uint8Array,\r\n offset: number,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[]; bytesRead: number } {\r\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\r\n\r\n // V5: no frame-level size prefix. Parse envelope directly.\r\n let pos = offset;\r\n\r\n // Byte 0: upper 3 bits = CallTypeId, lower 5 bits = HeaderCount\r\n const byte0 = data[pos++];\r\n const callTypeId = (byte0 >> 5) & 0x7;\r\n const headerCount = byte0 & 0x1f;\r\n\r\n // RelatedId as VarUint\r\n const relId = readVarUint(data, pos);\r\n pos += relId.bytesRead;\r\n\r\n // MethodRef as LVarSpan\r\n const methodLen = readVarUint(data, pos);\r\n pos += methodLen.bytesRead;\r\n const methodBytes = data.subarray(pos, pos + methodLen.value);\r\n const method = textDecoder.decode(methodBytes);\r\n pos += methodLen.value;\r\n\r\n // ArgData length as fixed 4-byte LE\r\n const argDataLen = view.getInt32(pos, true);\r\n pos += 4;\r\n\r\n // Skip headers (if any — TS client doesn't use them)\r\n if (headerCount > 0) {\r\n for (let h = 0; h < headerCount; h++) {\r\n // L1Memory: 1-byte length prefix + key bytes\r\n const keyLen = data[pos++];\r\n pos += keyLen;\r\n // LVarSpan: VarUint length + value bytes\r\n const valLen = readVarUint(data, pos);\r\n pos += valLen.bytesRead + valLen.value;\r\n }\r\n }\r\n\r\n // Deserialize arguments from argData — multiple concatenated MessagePack values\r\n const args: unknown[] = [];\r\n const argEnd = pos + argDataLen;\r\n if (argDataLen > 0) {\r\n const argSlice = data.subarray(pos, argEnd);\r\n // Reuse the provided decoder across calls; `decodeMulti` is a generator\r\n // so each yield completes before the next `decode` begins, which is\r\n // safe on a single-threaded event loop.\r\n for (const decoded of decoder.decodeMulti(argSlice)) {\r\n args.push(decoded);\r\n }\r\n pos = argEnd;\r\n }\r\n\r\n const message: RpcMessage = {\r\n Method: method,\r\n RelatedId: relId.value,\r\n CallType: callTypeId,\r\n };\r\n\r\n return { message, args, bytesRead: pos - offset };\r\n}\r\n\r\n/**\r\n * Splits a binary WebSocket frame into individual deserialized messages.\r\n * V5 envelopes are self-delimiting (header + method + argLen + argData), so\r\n * multiple envelopes concatenated in a single WebSocket frame can be decoded\r\n * by iterating with the bytesRead returned from deserializeBinaryMessage.\r\n *\r\n * This must stay symmetric with serializeBinaryFrame() (which concatenates\r\n * multiple envelopes). The .NET server batches outbound RPC messages into a\r\n * single WebSocket frame via WebSocketChannel/WriteDelayer, so any assumption\r\n * that a frame is a single envelope silently drops every trailing message.\r\n *\r\n * Accepts an optional `Decoder` for reuse across calls on the same\r\n * connection — forwarded to `deserializeBinaryMessage`.\r\n */\r\nexport function splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[] }[] {\r\n const results: { message: RpcMessage; args: unknown[] }[] = [];\r\n let offset = 0;\r\n while (offset < frame.length) {\r\n const { message, args, bytesRead } = deserializeBinaryMessage(\r\n frame,\r\n offset,\r\n decoder\r\n );\r\n if (bytesRead <= 0) break; // defensive — avoid infinite loop on malformed data\r\n results.push({ message, args });\r\n offset += bytesRead;\r\n }\r\n return results;\r\n}\r\n\r\n/**\r\n * Serializes multiple binary messages into a single WebSocket frame.\r\n * Simply concatenates the already size-prefixed messages.\r\n */\r\nexport function serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return concatUint8Arrays(messages);\r\n}\r\n\r\n// ============================================================\r\n// Compact binary format (V5Compact) — 4-byte method hash\r\n// ============================================================\r\n\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\n/**\r\n * Serializes an RpcMessage + args into V5Compact binary format.\r\n * Same as V5 except the method is encoded as a 4-byte LE hash\r\n * instead of an LVar-prefixed UTF-8 string.\r\n *\r\n * Layout:\r\n * [byte 0: (CallTypeId<<5) | HeaderCount] [VarUint relatedId]\r\n * [4-byte LE methodHash] [4-byte LE argLen]\r\n * [argData]\r\n */\r\nexport function serializeCompactBinaryMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n registry?: RpcMethodRegistry,\r\n encoder: Encoder = defaultBinaryEncoder\r\n): Uint8Array {\r\n // 1. Encode args\r\n let argsDataLen = 0;\r\n let argBufs: Uint8Array[] | null = null;\r\n if (args && args.length > 0) {\r\n argBufs = new Array<Uint8Array>(args.length);\r\n for (let i = 0; i < args.length; i++) {\r\n const buf = encoder.encode(args[i]);\r\n argBufs[i] = buf;\r\n argsDataLen += buf.length;\r\n }\r\n }\r\n\r\n // 2. Get method hash\r\n const methodName = message.Method ?? '';\r\n const methodHash = registry?.requireHash(methodName) ?? 0;\r\n\r\n // 3. Compute sizes\r\n const callType = message.CallType ?? 0;\r\n const relatedId = message.RelatedId ?? 0;\r\n const relatedIdLen = varUintByteLen(relatedId);\r\n // Compact: 4 bytes for hash (instead of LVar method name)\r\n const headerSize = 1 + relatedIdLen + 4;\r\n const totalSize = headerSize + 4 + argsDataLen;\r\n\r\n // 4. Build wire buffer\r\n const out = new Uint8Array(totalSize);\r\n let pos = 0;\r\n out[pos++] = (callType << 5) & 0xe0;\r\n pos = writeVarUintInto(out, pos, relatedId);\r\n // Method hash as 4-byte LE uint32\r\n const hashU32 = methodHash >>> 0;\r\n out[pos++] = hashU32 & 0xff;\r\n out[pos++] = (hashU32 >>> 8) & 0xff;\r\n out[pos++] = (hashU32 >>> 16) & 0xff;\r\n out[pos++] = (hashU32 >>> 24) & 0xff;\r\n // argLen as int32 LE\r\n out[pos++] = argsDataLen & 0xff;\r\n out[pos++] = (argsDataLen >>> 8) & 0xff;\r\n out[pos++] = (argsDataLen >>> 16) & 0xff;\r\n out[pos++] = (argsDataLen >>> 24) & 0xff;\r\n if (argBufs !== null) {\r\n for (const buf of argBufs) {\r\n out.set(buf, pos);\r\n pos += buf.length;\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Deserializes a single V5Compact binary message starting at `offset`.\r\n * Reads a 4-byte LE method hash and resolves it via the registry.\r\n */\r\nexport function deserializeCompactBinaryMessage(\r\n data: Uint8Array,\r\n offset: number,\r\n registry?: RpcMethodRegistry,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[]; bytesRead: number } {\r\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\r\n let pos = offset;\r\n\r\n // Byte 0: upper 3 bits = CallTypeId, lower 5 bits = HeaderCount\r\n const byte0 = data[pos++];\r\n const callTypeId = (byte0 >> 5) & 0x7;\r\n const headerCount = byte0 & 0x1f;\r\n\r\n // RelatedId as VarUint\r\n const relId = readVarUint(data, pos);\r\n pos += relId.bytesRead;\r\n\r\n // Method hash as fixed 4-byte LE uint32\r\n const methodHash = view.getUint32(pos, true);\r\n pos += 4;\r\n const method =\r\n registry?.getName(methodHash | 0) ??\r\n `<hash:0x${methodHash.toString(16).padStart(8, '0')}>`;\r\n\r\n // ArgData length as fixed 4-byte LE\r\n const argDataLen = view.getInt32(pos, true);\r\n pos += 4;\r\n\r\n // Skip headers (if any)\r\n if (headerCount > 0) {\r\n for (let h = 0; h < headerCount; h++) {\r\n const keyLen = data[pos++];\r\n pos += keyLen;\r\n const valLen = readVarUint(data, pos);\r\n pos += valLen.bytesRead + valLen.value;\r\n }\r\n }\r\n\r\n // Deserialize arguments\r\n const args: unknown[] = [];\r\n const argEnd = pos + argDataLen;\r\n if (argDataLen > 0) {\r\n const argSlice = data.subarray(pos, argEnd);\r\n for (const decoded of decoder.decodeMulti(argSlice)) {\r\n args.push(decoded);\r\n }\r\n pos = argEnd;\r\n }\r\n\r\n const message: RpcMessage = {\r\n Method: method,\r\n RelatedId: relId.value,\r\n CallType: callTypeId,\r\n };\r\n\r\n return { message, args, bytesRead: pos - offset };\r\n}\r\n\r\n/**\r\n * Splits a compact binary frame into individual deserialized messages.\r\n */\r\nexport function splitCompactBinaryFrame(\r\n frame: Uint8Array,\r\n registry?: RpcMethodRegistry,\r\n decoder: Decoder = defaultBinaryDecoder\r\n): { message: RpcMessage; args: unknown[] }[] {\r\n const results: { message: RpcMessage; args: unknown[] }[] = [];\r\n let offset = 0;\r\n while (offset < frame.length) {\r\n const { message, args, bytesRead } =\r\n deserializeCompactBinaryMessage(\r\n frame,\r\n offset,\r\n registry,\r\n decoder\r\n );\r\n if (bytesRead <= 0) break;\r\n results.push({ message, args });\r\n offset += bytesRead;\r\n }\r\n return results;\r\n}\r\n","import { Encoder } from '@msgpack/msgpack';\r\n\r\n/**\r\n * Patches `@msgpack/msgpack`'s `Encoder` so JS `Map` instances are encoded\r\n * as proper msgpack `map` types with typed keys (matching .NET's\r\n * `Dictionary<K, V>` wire format). Without this patch the encoder treats\r\n * `Map` as a plain object — since `Object.keys(new Map())` returns `[]`,\r\n * any `Map` is silently serialized as an empty `{}`.\r\n *\r\n * Idempotent: the patch is applied at most once per process.\r\n *\r\n * This is needed by the `$sys.Reconnect:3` protocol to send\r\n * `Dictionary<int, byte[]>` over msgpack in a way that a .NET server can\r\n * deserialize. The JSON side is naturally compatible (keys become strings\r\n * in JSON regardless) — this patch fixes the binary path only.\r\n */\r\n/* eslint-disable @typescript-eslint/no-explicit-any,\r\n @typescript-eslint/no-unsafe-member-access,\r\n @typescript-eslint/no-unsafe-call,\r\n @typescript-eslint/no-unsafe-assignment\r\n -- the entire patch reaches into private Encoder internals to make Map\r\n encoding work; the type annotations on `@msgpack/msgpack` don't expose\r\n these methods so all access is necessarily untyped. */\r\nexport function patchMsgpackEncoderForMaps(): void {\r\n const proto = Encoder.prototype as any;\r\n if (proto._hasMapPatch) return;\r\n proto._hasMapPatch = true;\r\n\r\n const originalDoEncode = proto.doEncode as (object: unknown, depth: number) => void;\r\n\r\n proto.doEncode = function (this: unknown, object: unknown, depth: number): void {\r\n if (object instanceof Map) {\r\n _encodeMapInstance.call(this, object, depth);\r\n return;\r\n }\r\n originalDoEncode.call(this, object, depth);\r\n };\r\n\r\n function _encodeMapInstance(this: unknown, m: Map<unknown, unknown>, depth: number): void {\r\n const self = this as any;\r\n const size = m.size;\r\n if (size < 16) {\r\n self.writeU8(0x80 + size);\r\n } else if (size < 0x10000) {\r\n self.writeU8(0xde);\r\n self.writeU16(size);\r\n } else {\r\n self.writeU8(0xdf);\r\n self.writeU32(size);\r\n }\r\n for (const [key, value] of m) {\r\n // Recurse via the (now patched) doEncode so nested Maps work too.\r\n self.doEncode(key, depth + 1);\r\n self.doEncode(value, depth + 1);\r\n }\r\n }\r\n}\r\n/* eslint-enable @typescript-eslint/no-explicit-any,\r\n @typescript-eslint/no-unsafe-member-access,\r\n @typescript-eslint/no-unsafe-call,\r\n @typescript-eslint/no-unsafe-assignment */\r\n\r\n// Apply at module load — idempotent.\r\npatchMsgpackEncoderForMaps();\r\n","// .NET counterparts:\r\n// RpcConnection — thin wrapper around RpcTransport + PropertyBag. RpcTransport\r\n// is an abstract IAsyncEnumerable<RpcInboundMessage> + Send() that can be\r\n// WebSocket-based or in-memory. The transport also handles frame batching and\r\n// back-pressure via a Channel<T> send queue.\r\n//\r\n// Omitted from .NET:\r\n// - PropertyBag on connection — used in .NET for per-connection metadata (e.g.\r\n// authentication info attached by middleware). TS has no middleware pipeline,\r\n// so no need for an extensible property bag.\r\n// - IsLocal flag — .NET distinguishes local loopback connections (same-process\r\n// server) from remote ones. TS is always a remote client.\r\n// - RpcTransport as IAsyncEnumerable — .NET reads inbound messages via\r\n// async iteration with cancellation. TS uses event-based delivery\r\n// (messageReceived handler) because browser WebSocket API is event-driven.\r\n// - Channel<RpcOutboundMessage> send queue with backpressure — .NET buffers\r\n// outbound messages in an async channel that the transport drains. TS sends\r\n// synchronously on the WebSocket; if the socket isn't ready, messages are\r\n// buffered in _sendBuffer (CONNECTING) or silently dropped (CLOSING/CLOSED).\r\n// JS is single-threaded so there's no contention, and WebSocket.send() itself\r\n// handles buffering at the OS level.\r\n// - IAsyncDisposable — .NET transports are disposable resources. TS connections\r\n// are closed via close() and garbage-collected.\r\n\r\nimport { Encoder, Decoder } from '@msgpack/msgpack';\r\nimport { PromiseSource, EventHandlerSet } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport {\r\n splitFrame,\r\n serializeFrame,\r\n splitBinaryFrame as splitBinaryFrameFn,\r\n serializeBinaryFrame,\r\n createBinaryEncoder,\r\n} from './rpc-serialization.js';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport type { RpcSerializationFormat } from './rpc-serialization-format.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\nconst { warnLog } = getLogs('RpcConnection');\r\n\r\n/** Abstract WebSocket interface — works with both browser WebSocket and Node.js ws. */\r\nexport interface WebSocketLike {\r\n readonly readyState: number;\r\n binaryType?: string;\r\n send(data: string | ArrayBufferLike | Uint8Array | ArrayBufferView): void;\r\n close(code?: number, reason?: string): void;\r\n onopen: ((ev: unknown) => void) | null;\r\n onmessage: ((ev: { data: unknown }) => void) | null;\r\n onclose: ((ev: { code: number; reason: string }) => void) | null;\r\n onerror: ((ev: unknown) => void) | null;\r\n}\r\n\r\nexport const WebSocketState = {\r\n CONNECTING: 0,\r\n OPEN: 1,\r\n CLOSING: 2,\r\n CLOSED: 3,\r\n} as const;\r\n\r\n/** Received message — either text (string) or binary (already parsed). */\r\nexport type RpcReceivedMessage =\r\n | { kind: 'text'; raw: string }\r\n | { kind: 'binary'; message: RpcMessage; args: unknown[] };\r\n\r\n/** Abstract RPC connection — transport-agnostic interface for sending/receiving messages. */\r\nexport interface RpcConnection {\r\n readonly isOpen: boolean;\r\n readonly binaryMode: boolean;\r\n readonly whenConnected: Promise<void>;\r\n readonly messageReceived: EventHandlerSet<RpcReceivedMessage>;\r\n readonly closed: EventHandlerSet<{ code: number; reason: string }>;\r\n /** Optional reusable msgpack encoder for outbound binary messages.\r\n * Callers passing this to `serializeBinaryMessage` skip the per-call\r\n * Encoder construction + resizeBuffer growth. Implementations that\r\n * don't support binary serialization can leave it undefined. */\r\n readonly encoder?: Encoder;\r\n send(serializedMessage: string): void;\r\n sendBinary(data: Uint8Array): void;\r\n close(code?: number, reason?: string): void;\r\n}\r\n\r\n/** WebSocket-based RpcConnection — handles frame splitting, binary/text modes, and message queueing. */\r\nexport class RpcWebSocketConnection implements RpcConnection {\r\n private _ws: WebSocketLike;\r\n private _sendBuffer: (string | Uint8Array)[] = [];\r\n private _connected = new PromiseSource<void>();\r\n\r\n /** Per-connection msgpack encoder — reused across outbound messages to\r\n * amortise construction cost and to keep the internal write buffer at\r\n * its largest observed size (avoids repeated `resizeBuffer` growth on\r\n * large video frames). See serializeBinaryMessage() for details. */\r\n private _encoder: Encoder = createBinaryEncoder();\r\n\r\n /** Per-connection msgpack decoder — reused across inbound messages\r\n * rather than reconstructing one for every `decodeMulti` call. */\r\n private _decoder: Decoder = new Decoder();\r\n\r\n /** Exposed for callers who need to encode outbound binary messages\r\n * through this connection (e.g. `serializeBinaryMessage`). Safe to\r\n * pass to synchronous encode calls on the same event-loop turn. */\r\n get encoder(): Encoder {\r\n return this._encoder;\r\n }\r\n get decoder(): Decoder {\r\n return this._decoder;\r\n }\r\n\r\n private _format: RpcSerializationFormat | undefined;\r\n private _methodRegistry: RpcMethodRegistry | undefined;\r\n\r\n readonly binaryMode: boolean;\r\n readonly messageReceived = new EventHandlerSet<RpcReceivedMessage>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly error = new EventHandlerSet<unknown>();\r\n\r\n constructor(\r\n ws: WebSocketLike,\r\n binaryMode = false,\r\n format?: RpcSerializationFormat,\r\n methodRegistry?: RpcMethodRegistry\r\n ) {\r\n this._ws = ws;\r\n this.binaryMode = binaryMode;\r\n this._format = format;\r\n this._methodRegistry = methodRegistry;\r\n\r\n if (binaryMode && ws.binaryType !== undefined)\r\n ws.binaryType = 'arraybuffer';\r\n\r\n if (ws.readyState === WebSocketState.OPEN) {\r\n this._connected.resolve();\r\n this._flush();\r\n }\r\n\r\n ws.onopen = () => {\r\n this._connected.resolve();\r\n this._flush();\r\n };\r\n\r\n ws.onmessage = ev => {\r\n // Binary frame — V5 self-delimiting envelopes (one or more per WS frame).\r\n //\r\n // Accept BOTH `ArrayBuffer` (browser WebSocket when `binaryType` is\r\n // 'arraybuffer') AND `Uint8Array`-shaped views (Node `ws` delivering a\r\n // `Buffer`, which is a `Uint8Array` subclass). The node-ws adapter\r\n // intentionally passes Buffer through without an ArrayBuffer copy —\r\n // at 300 concurrent video streams that copy was ~100 MB/s of pure\r\n // memcpy + allocation pressure. Matching `ArrayBuffer.isView` here\r\n // keeps the zero-copy path alive all the way to `splitBinaryFrame`.\r\n if (\r\n ev.data instanceof Uint8Array ||\r\n ev.data instanceof ArrayBuffer\r\n ) {\r\n // Node `Buffer` is a `Uint8Array` subclass, so we can hand it to\r\n // `splitBinaryFrame` directly with no view wrapping or copying.\r\n // Browsers deliver `ArrayBuffer`, which needs a single zero-copy\r\n // view wrap.\r\n const frame =\r\n ev.data instanceof Uint8Array\r\n ? ev.data\r\n : new Uint8Array(ev.data);\r\n try {\r\n const messages = this._splitBinary(frame);\r\n for (const { message, args } of messages) {\r\n this.messageReceived.trigger({\r\n kind: 'binary',\r\n message,\r\n args,\r\n });\r\n }\r\n } catch (e) {\r\n warnLog?.log('Failed to split binary frame:', e);\r\n this.error.trigger(e);\r\n }\r\n } else if (\r\n typeof Blob !== 'undefined' &&\r\n ev.data instanceof Blob &&\r\n this.binaryMode\r\n ) {\r\n // Fallback: some Chromium builds deliver the first frames as Blob even\r\n // after setting binaryType='arraybuffer'. Convert and route through\r\n // the binary path so we don't silently drop them.\r\n void ev.data.arrayBuffer().then(ab => {\r\n const frame = new Uint8Array(ab);\r\n try {\r\n const messages = this._splitBinary(frame);\r\n for (const { message, args } of messages) {\r\n this.messageReceived.trigger({\r\n kind: 'binary',\r\n message,\r\n args,\r\n });\r\n }\r\n } catch (e) {\r\n warnLog?.log('Failed to split binary blob frame:', e);\r\n this.error.trigger(e);\r\n }\r\n });\r\n } else {\r\n // Text frame — JSON delimited messages\r\n const data =\r\n typeof ev.data === 'string' ? ev.data : String(ev.data);\r\n const messages = splitFrame(data);\r\n for (const msg of messages) {\r\n if (msg.length > 0)\r\n this.messageReceived.trigger({\r\n kind: 'text',\r\n raw: msg,\r\n });\r\n }\r\n }\r\n };\r\n\r\n ws.onclose = ev => {\r\n this.closed.trigger({ code: ev.code, reason: ev.reason });\r\n };\r\n\r\n ws.onerror = ev => {\r\n this.error.trigger(ev);\r\n };\r\n }\r\n\r\n get isOpen(): boolean {\r\n return this._ws.readyState === WebSocketState.OPEN;\r\n }\r\n\r\n get whenConnected(): Promise<void> {\r\n return this._connected;\r\n }\r\n\r\n send(serializedMessage: string): void {\r\n this._sendRaw(serializedMessage);\r\n }\r\n\r\n sendBinary(data: Uint8Array): void {\r\n this._sendRaw(data);\r\n }\r\n\r\n sendTextBatch(messages: string[]): void {\r\n this._sendRaw(serializeFrame(messages));\r\n }\r\n\r\n sendBinaryBatch(messages: Uint8Array[]): void {\r\n this._sendRaw(serializeBinaryFrame(messages));\r\n }\r\n\r\n /** Split a binary frame using the format (compact-aware) or default splitter. */\r\n private _splitBinary(\r\n frame: Uint8Array\r\n ): { message: RpcMessage; args: unknown[] }[] {\r\n if (this._format) {\r\n return this._format.splitBinaryFrame(\r\n frame,\r\n this._decoder,\r\n this._methodRegistry\r\n );\r\n }\r\n return splitBinaryFrameFn(frame, this._decoder);\r\n }\r\n\r\n close(code?: number, reason?: string): void {\r\n this._ws.close(code, reason);\r\n }\r\n\r\n private _sendRaw(data: string | Uint8Array): void {\r\n try {\r\n if (this._ws.readyState === WebSocketState.OPEN) {\r\n this._ws.send(data);\r\n } else if (this._ws.readyState === WebSocketState.CONNECTING)\r\n this._sendBuffer.push(data);\r\n // CLOSING/CLOSED: silently drop\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n\r\n private _flush(): void {\r\n if (this._sendBuffer.length === 0) return;\r\n const buffer = this._sendBuffer;\r\n this._sendBuffer = [];\r\n try {\r\n for (const item of buffer) this._ws.send(item);\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n}\r\n","// .NET counterpart: none — this is a TS-only test transport.\r\n//\r\n// .NET tests use a different mechanism: RpcTestClient creates an in-memory\r\n// Channel<RpcMessage> pair wrapped in an RpcTransport. We use the browser-\r\n// standard MessageChannel API instead, which gives us a synchronous in-process\r\n// transport without requiring WebSocket mocks.\r\n\r\nimport { EventHandlerSet } from '@actuallab/core';\r\nimport { splitFrame } from './rpc-serialization.js';\r\nimport type { RpcConnection, RpcReceivedMessage } from './rpc-connection.js';\r\n\r\n/** MessagePort-based RpcConnection — for in-process testing without WebSocket mocks. */\r\nexport class RpcMessageChannelConnection implements RpcConnection {\r\n private _port: MessagePort;\r\n private _open = true;\r\n\r\n readonly binaryMode = false;\r\n readonly messageReceived = new EventHandlerSet<RpcReceivedMessage>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly whenConnected: Promise<void> = Promise.resolve(); // immediately connected\r\n\r\n constructor(port: MessagePort) {\r\n this._port = port;\r\n port.onmessage = (ev: MessageEvent) => {\r\n const data =\r\n typeof ev.data === 'string' ? ev.data : String(ev.data);\r\n for (const msg of splitFrame(data))\r\n if (msg.length > 0)\r\n this.messageReceived.trigger({ kind: 'text', raw: msg });\r\n };\r\n }\r\n\r\n get isOpen(): boolean {\r\n return this._open;\r\n }\r\n\r\n send(serializedMessage: string): void {\r\n if (!this._open) return;\r\n try {\r\n this._port.postMessage(serializedMessage);\r\n } catch {\r\n // never fail\r\n }\r\n }\r\n\r\n sendBinary(data: Uint8Array): void {\r\n if (!this._open) return;\r\n try {\r\n this._port.postMessage(data, [data.buffer]);\r\n } catch {\r\n // never fail\r\n }\r\n }\r\n\r\n close(code?: number, reason?: string): void {\r\n if (!this._open) return;\r\n this._open = false;\r\n this._port.close();\r\n this.closed.trigger({ code: code ?? 1000, reason: reason ?? '' });\r\n }\r\n}\r\n\r\n/** Creates a pair of connected RpcMessageChannelConnections for testing. */\r\nexport function createMessageChannelPair(): [\r\n RpcMessageChannelConnection,\r\n RpcMessageChannelConnection,\r\n ] {\r\n const channel = new MessageChannel();\r\n return [\r\n new RpcMessageChannelConnection(channel.port1),\r\n new RpcMessageChannelConnection(channel.port2),\r\n ];\r\n}\r\n","// .NET counterparts:\r\n// RpcServiceDef (Configuration/) — 162 lines. Describes a registered service\r\n// including its .NET Type, Mode (Local/Client/Server/Hybrid), ServerResolver\r\n// (DI-based lazy resolution), ClientType, IsSystem, IsBackend flags, Scope,\r\n// LegacyNames, PropertyBag, and a method dictionary built via reflection.\r\n// RpcMethodDef (Configuration/) — 189 lines. Describes a single method:\r\n// FullName (ServiceName.MethodName:ArgCount), CallType (Regular/Reliable),\r\n// NoWait, HasPolymorphicArguments/Result, RpcMethodKind (Query/Command/System),\r\n// RpcMethodAttribute, LegacyNames, Tracer, outbound/inbound call pipeline\r\n// (factories, invokers, middleware chains, routers, timeouts).\r\n//\r\n// Omitted from .NET:\r\n// - Reflection-based method discovery (BuildMethods) — .NET scans interface\r\n// methods via reflection, filters generics, builds call pipelines. TS uses\r\n// explicit defineRpcService() or decorator metadata — no runtime reflection.\r\n// - RpcMethodKind (Query/Command/System/Other) — .NET classifies methods to\r\n// route Commands through ICommander. TS has no command bus; the distinction\r\n// is unnecessary.\r\n// - IsBackend / IBackendService — .NET separates \"backend-only\" services that\r\n// require backend auth. TS client never exposes backend services.\r\n// - RpcMethodAttribute / LegacyNames — .NET supports custom wire names and\r\n// version-specific legacy name mappings for rolling upgrades. TS has a\r\n// single version; wire names = \"ServiceName.MethodName:wireArgCount\".\r\n// - Call pipeline (OutboundCallFactory, InboundCallFactory, InboundCallInvoker,\r\n// MiddlewareFilter, OutboundCallRouter, OutboundCallTimeouts) — .NET builds a\r\n// per-method middleware chain at startup via generic factory types. TS dispatches\r\n// directly from RpcServiceHost; no middleware or routing layer.\r\n// - CallType (Regular/Reliable) — Reliable calls track stages for reconnect\r\n// resumption. TS replays entire calls on reconnect.\r\n// - HasPolymorphicArguments/Result — governs serializer behavior for abstract\r\n// argument types. TS uses JSON (inherently polymorphic); not applicable.\r\n// - Tracer / RpcCallTracer — per-method distributed tracing. Not ported.\r\n// - PropertyBag — extensible metadata on service/method def. Not needed.\r\n// - Mode (ServiceMode) / Scope / ServerResolver / ClientType — DI wiring.\r\n\r\n/** Controls outbound RPC call behavior regarding connection waiting, reconnection, and resending. */\r\nexport const RpcRemoteExecutionMode = {\r\n /** Wait for connection if disconnected when the call is made. */\r\n AwaitForConnection: 1,\r\n /** Resend on reconnection to the same peer. */\r\n AllowReconnect: 2,\r\n /** Resend on reconnection to a different peer (implies AllowReconnect). */\r\n AllowResend: 6, // 4 | AllowReconnect\r\n /** Default: wait + reconnect + resend. */\r\n Default: 7, // AwaitForConnection | AllowResend\r\n} as const;\r\n/** Bitfield combination of RpcRemoteExecutionMode flags. */\r\nexport type RpcRemoteExecutionMode = number;\r\n\r\n/** Describes a single method on an RPC service. */\r\nexport interface RpcMethodDef {\r\n readonly name: string;\r\n readonly serviceName: string;\r\n readonly argCount: number;\r\n readonly wireArgCount: number;\r\n readonly callTypeId: number;\r\n readonly stream: boolean;\r\n readonly noWait: boolean;\r\n readonly remoteExecutionMode: RpcRemoteExecutionMode;\r\n}\r\n\r\n/** Describes an RPC service — its name and method definitions. */\r\nexport interface RpcServiceDef {\r\n readonly name: string;\r\n readonly methods: ReadonlyMap<string, RpcMethodDef>;\r\n}\r\n\r\nexport interface RpcMethodDefInput {\r\n args: unknown[];\r\n returns?: symbol;\r\n callTypeId?: number;\r\n /** Override for wireArgCount. Default: args.length + 1 (assumes a CancellationToken slot). */\r\n wireArgCount?: number;\r\n /** Override for remoteExecutionMode. Default: RpcRemoteExecutionMode.Default (7). NoWait methods always use 0. */\r\n remoteExecutionMode?: RpcRemoteExecutionMode;\r\n}\r\n\r\nexport const RpcType = {\r\n object: Symbol('object'),\r\n stream: Symbol('stream'),\r\n noWait: Symbol('noWait'),\r\n void: Symbol('void'),\r\n} as const;\r\n\r\n/** Returns the full wire method name: \"ServiceName.MethodName:wireArgCount\". */\r\nexport function wireMethodName(def: RpcMethodDef): string {\r\n return `${def.serviceName}.${def.name}:${def.wireArgCount}`;\r\n}\r\n\r\nexport function defineRpcService(\r\n name: string,\r\n methods: Record<string, RpcMethodDefInput>\r\n): RpcServiceDef {\r\n const map = new Map<string, RpcMethodDef>();\r\n for (const [key, input] of Object.entries(methods)) {\r\n // If the key contains \":\", the part before \":\" is the clean name\r\n // (the suffix is for JS object key disambiguation of overloads).\r\n // Otherwise the key IS the clean name.\r\n const colonIndex = key.indexOf(':');\r\n const cleanName = colonIndex >= 0 ? key.substring(0, colonIndex) : key;\r\n const wireArgCount = input.wireArgCount ?? input.args.length + 1;\r\n const mapKey = `${cleanName}:${wireArgCount}`;\r\n const isNoWait = input.returns === RpcType.noWait;\r\n map.set(mapKey, {\r\n name: cleanName,\r\n serviceName: name,\r\n argCount: input.args.length,\r\n wireArgCount,\r\n callTypeId: input.callTypeId ?? 0,\r\n stream: input.returns === RpcType.stream,\r\n noWait: isNoWait,\r\n remoteExecutionMode: isNoWait\r\n ? 0\r\n : (input.remoteExecutionMode ?? RpcRemoteExecutionMode.Default),\r\n });\r\n }\r\n return { name, methods: map };\r\n}\r\n","// .NET counterparts:\r\n// RpcCall (29 lines) — base class: MethodDef, Id, NoWait flag, Lock.\r\n// RpcOutboundCall (437 lines) — tracks an outbound call's full lifecycle:\r\n// result via AsyncTaskMethodBuilder, CacheInfoCapture, CancellationHandler,\r\n// CompletedStage (for Reliable reconnection), StartedAt (for timeouts),\r\n// hashing for Match, tracing, routing, and retry logic.\r\n// RpcInboundCall (298 lines) — tracks an inbound call: deserialization,\r\n// middleware-chain invocation, cancellation via linked CTS, stage-based\r\n// re-processing for reconnection, sending the result back.\r\n// RpcOutboundCallTracker (255 lines) — thread-safe ConcurrentDictionary of\r\n// outbound calls with: Maintain() loop (timeout monitoring + logging),\r\n// Reconnect() protocol (stage-based call resumption), Abort(), TryReroute().\r\n// RpcInboundCallTracker (64 lines) — ConcurrentDictionary, GetOrRegister,\r\n// Unregister, Clear.\r\n//\r\n// Omitted from .NET:\r\n// - AsyncTaskMethodBuilder / typed RpcOutboundCall<T> — .NET creates a typed\r\n// task builder for each call's return type. TS uses PromiseSource<unknown>\r\n// (untyped) because TypeScript erases generics at runtime.\r\n// - CancellationHandler (CancellationToken.Register → Cancel) — .NET registers\r\n// a callback per outbound call. TS implements equivalent via optional\r\n// AbortSignal on RpcPeer.call(); the cancel handler rejects the promise,\r\n// removes from tracker, and sends $sys.Cancel — same behavior as .NET's\r\n// Cancel() + NotifyCancelled().\r\n// - StartedAt / CpuTimestamp / Timeout monitoring — .NET's Maintain() loop\r\n// checks elapsed time against per-method timeouts every CallTimeoutCheckPeriod.\r\n// TS has no per-call timeout mechanism; the WebSocket-level disconnect +\r\n// rejectAll handles stuck calls.\r\n// - CompletedStage / RpcCallStage — supports Reliable call type's stage-based\r\n// reconnection protocol where the server tells the client which stage each\r\n// call reached, avoiding re-execution of completed stages. TS replays entire\r\n// calls on reconnect (simpler, adequate for browser client).\r\n// - Reconnect() protocol — outbound tracker sends $sys.Reconnect with compressed\r\n// call IDs grouped by stage; server responds with \"unknown\" IDs that need\r\n// re-sending. Not ported; TS uses full replay.\r\n// - Abort() with multi-pass retry — .NET iterates 3+ times with delays to\r\n// catch late-registered calls. TS does a single-pass rejectAll().\r\n// - TryReroute() — checks if the call's peer route has changed (load-balancer\r\n// rerouting) and sets RpcRerouteException. TS has no routing layer.\r\n// - IsLongLiving / _longLivingCalls — separate tracking for calls that outlive\r\n// normal timeouts (e.g. streaming). Not needed without RpcStream.\r\n// - RpcInboundCall: middleware-chain invocation, deserialization with polymorphic\r\n// argument handling, stage-based TryReprocess, CancellationTokenSource per\r\n// call. TS inbound calls are dispatched directly by RpcServiceHost; no\r\n// middleware, no per-call CTS, no stage tracking.\r\n// - RpcCall.Lock (monitor-based synchronisation) — .NET uses lock(this) for\r\n// thread-safe result setting. TS is single-threaded; no locking needed.\r\n\r\nimport { PromiseSource } from '@actuallab/core';\r\nimport { RpcCallStage } from './rpc-call-stage.js';\r\nimport { RpcRemoteExecutionMode } from './rpc-service-def.js';\r\n\r\n/** Tracks a pending outbound RPC call. */\r\nexport class RpcOutboundCall {\r\n readonly callId: number;\r\n readonly method: string;\r\n readonly result = new PromiseSource<unknown>();\r\n /** Bitfield of RpcRemoteExecutionMode flags controlling reconnect/resend behavior. */\r\n readonly remoteExecutionMode: number;\r\n\r\n /** Whether to remove this call from the tracker on $sys.Ok. Default: true.\r\n * Subclasses (e.g. compute calls) override to false to stay in tracker for invalidation. */\r\n readonly removeOnOk: boolean = true;\r\n\r\n /** The serialized wire data — stored for re-sending on reconnect. */\r\n serializedWireData: string | Uint8Array = '';\r\n\r\n /**\r\n * Bitfield of {@link RpcCallStage} flags indicating how far this call\r\n * has progressed. Used by the `$sys.Reconnect` protocol to tell the\r\n * remote peer which calls are still pending its attention.\r\n *\r\n * - 0: brand new, no result yet (the peer is expected to be actively processing).\r\n * - `ResultReady` (1): `$sys.Ok` received for this call.\r\n * - `Invalidated` (3): result was invalidated (compute calls only).\r\n * - `Unregistered` (0x1000): removed from the tracker.\r\n */\r\n completedStage = 0;\r\n\r\n constructor(callId: number, method: string, remoteExecutionMode = 7) {\r\n this.callId = callId;\r\n this.method = method;\r\n this.remoteExecutionMode = remoteExecutionMode;\r\n }\r\n\r\n /**\r\n * Returns the stage to report to the remote peer via `$sys.Reconnect`,\r\n * or `null` if this call should not be reconciled and must be aborted.\r\n *\r\n * Mirrors .NET `RpcOutboundCall.GetReconnectStage` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcOutboundCall.cs.\r\n */\r\n getReconnectStage(isPeerChanged: boolean): number | null {\r\n const mode = this.remoteExecutionMode;\r\n if (!(mode & RpcRemoteExecutionMode.AllowReconnect)) return null;\r\n if (isPeerChanged && !(mode & RpcRemoteExecutionMode.AllowResend)) return null;\r\n return this.completedStage;\r\n }\r\n\r\n /** Called when the connection is lost. Subclasses can override to resolve invalidation promises. */\r\n onDisconnect(): void {\r\n // no-op by default\r\n }\r\n}\r\n\r\n/** Manages outbound calls by their RelatedId. */\r\nexport class RpcOutboundCallTracker {\r\n private _calls = new Map<number, RpcOutboundCall>();\r\n private _nextId = 1;\r\n\r\n get size(): number {\r\n return this._calls.size;\r\n }\r\n\r\n nextId(): number {\r\n return this._nextId++;\r\n }\r\n\r\n register(call: RpcOutboundCall): void {\r\n this._calls.set(call.callId, call);\r\n }\r\n\r\n get(callId: number): RpcOutboundCall | undefined {\r\n return this._calls.get(callId);\r\n }\r\n\r\n remove(callId: number): RpcOutboundCall | undefined {\r\n const call = this._calls.get(callId);\r\n if (call !== undefined) {\r\n this._calls.delete(callId);\r\n call.completedStage |= RpcCallStage.Unregistered;\r\n }\r\n return call;\r\n }\r\n\r\n values(): IterableIterator<RpcOutboundCall> {\r\n return this._calls.values();\r\n }\r\n\r\n activeCallIds(): number[] {\r\n return [...this._calls.keys()];\r\n }\r\n\r\n /** Reject all pending calls with the given error.\r\n * Stage-3 compute calls (result resolved, awaiting invalidation) are kept in the tracker. */\r\n rejectAll(error: Error): void {\r\n for (const [id, call] of this._calls) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call — keep it for later invalidation on reconnect/stop\r\n continue;\r\n }\r\n call.result.reject(error);\r\n call.onDisconnect();\r\n this._calls.delete(id);\r\n }\r\n }\r\n\r\n /** Invalidate all remaining stage-3 calls (on reconnect or peer stop). */\r\n invalidateAll(): void {\r\n for (const call of this._calls.values()) {\r\n call.onDisconnect();\r\n }\r\n this._calls.clear();\r\n }\r\n\r\n clear(): void {\r\n this._calls.clear();\r\n }\r\n}\r\n\r\n/** Tracks an incoming inbound RPC call. */\r\nexport class RpcInboundCall {\r\n readonly callId: number;\r\n readonly method: string;\r\n readonly args: unknown[];\r\n\r\n constructor(callId: number, method: string, args: unknown[]) {\r\n this.callId = callId;\r\n this.method = method;\r\n this.args = args;\r\n }\r\n}\r\n\r\n/** Manages inbound calls by their RelatedId. */\r\nexport class RpcInboundCallTracker {\r\n private _calls = new Map<number, RpcInboundCall>();\r\n\r\n get size(): number {\r\n return this._calls.size;\r\n }\r\n\r\n register(call: RpcInboundCall): void {\r\n this._calls.set(call.callId, call);\r\n }\r\n\r\n get(callId: number): RpcInboundCall | undefined {\r\n return this._calls.get(callId);\r\n }\r\n\r\n remove(callId: number): RpcInboundCall | undefined {\r\n const call = this._calls.get(callId);\r\n if (call !== undefined) this._calls.delete(callId);\r\n return call;\r\n }\r\n\r\n clear(): void {\r\n this._calls.clear();\r\n }\r\n}\r\n","/**\n * Well-known RPC call completion stage constants, used by the\n * `$sys.Reconnect` protocol to tell the peer which calls have already\n * reached which stage and therefore don't need to be re-processed.\n *\n * Direct port of .NET `ActualLab.Rpc.Infrastructure.RpcCallStage` at\n * src/ActualLab.Rpc/Infrastructure/RpcCallStage.cs.\n */\nexport const RpcCallStage = {\n /** The call's result has been received from the peer. */\n ResultReady: 1,\n /** The compute call's result has been invalidated (compute-call specific). */\n Invalidated: 3,\n /**\n * Bit flag added on top of ResultReady/Invalidated when the outbound\n * call has been removed from the tracker (unregistered).\n */\n Unregistered: 0x1_000,\n} as const;\n\nexport type RpcCallStage = number;\n","/** Error thrown for a failed remote RPC call — carries the remote exception's type name when available. */\nexport class RpcError extends Error {\n readonly typeName?: string;\n\n constructor(message: string, typeName?: string) {\n super(message);\n this.name = 'RpcError';\n if (typeName !== undefined)\n this.typeName = typeName;\n }\n}\n","/**\n * Compresses and decompresses monotonically increasing sequences of\n * integers using variable-length LEB128 encoding of successive deltas.\n *\n * Wire-compatible with .NET `ActualLab.Rpc.Internal.IncreasingSeqCompressor`\n * (src/ActualLab.Rpc/Internal/IncreasingSeqCompressor.cs) — which\n * underlies the `$sys.Reconnect` protocol.\n *\n * Inputs MUST be sorted non-decreasing. Maximum supported value is\n * 2^53 - 1 (JavaScript `Number` safe-integer limit); call IDs in TS\n * never exceed this because they're generated from a 32-bit counter.\n */\nexport const IncreasingSeqCompressor = {\n /**\n * Serialize a non-decreasing sequence of non-negative integers as\n * LEB128-encoded deltas.\n *\n * Throws `RangeError` if the input is not monotonically non-decreasing,\n * or if a value exceeds `Number.MAX_SAFE_INTEGER`.\n */\n serialize(values: Iterable<number>): Uint8Array {\n // First pass: compute total byte length; second pass: write.\n // (A growable buffer would also work, but values are small here.)\n const deltas: number[] = [];\n let last = 0;\n let totalBytes = 0;\n for (const value of values) {\n if (!Number.isSafeInteger(value) || value < 0)\n throw new RangeError(\n `IncreasingSeqCompressor: value must be a non-negative safe integer (got ${value}).`);\n const delta = value - last;\n if (delta < 0)\n throw new RangeError(\n `IncreasingSeqCompressor: sequence must be non-decreasing (saw ${value} after ${last}).`);\n deltas.push(delta);\n last = value;\n totalBytes += _varUintByteLength(delta);\n }\n\n const out = new Uint8Array(totalBytes);\n let offset = 0;\n for (const delta of deltas) {\n offset = _writeVarUint(out, delta, offset);\n }\n return out;\n },\n\n /**\n * Decode bytes produced by {@link serialize} back into the original\n * sorted sequence of non-negative integers.\n */\n deserialize(data: Uint8Array): number[] {\n const result: number[] = [];\n let last = 0;\n let offset = 0;\n while (offset < data.length) {\n const [delta, next] = _readVarUint(data, offset);\n offset = next;\n last += delta;\n result.push(last);\n }\n return result;\n },\n} as const;\n\n// -- LEB128 (variable-length unsigned) encoding -----------------------------\n// Kept in sync with .NET SpanExt.WriteVarUInt64 / ReadVarUInt64 at\n// src/ActualLab.Core/Collections/SpanExt.ReadWriteVarUInt.cs:22-91.\n// We use `Number` arithmetic up to the 53-bit safe-integer ceiling; values\n// beyond that would need BigInt, which is not required for TS call IDs.\n\nconst LOW_BITS = 0x7f;\nconst HIGH_BIT = 0x80;\n\nfunction _varUintByteLength(value: number): number {\n let n = 0;\n let v = value;\n while (v >= HIGH_BIT) {\n n++;\n v = Math.floor(v / 128);\n }\n return n + 1;\n}\n\nfunction _writeVarUint(out: Uint8Array, value: number, offset: number): number {\n let v = value;\n while (v >= HIGH_BIT) {\n out[offset++] = (HIGH_BIT | (v & LOW_BITS)) & 0xff;\n v = Math.floor(v / 128);\n }\n out[offset++] = v & 0xff;\n return offset;\n}\n\nfunction _readVarUint(data: Uint8Array, offset: number): [number, number] {\n let value = 0;\n let shift = 0;\n // Up to 9 bytes contribute 7 bits each = 63 bits.\n while (shift < 63) {\n if (offset >= data.length)\n throw new RangeError('IncreasingSeqCompressor: truncated VarUInt.');\n const b = data[offset++];\n // Multiply by 2^shift using Math.pow to stay in Number arithmetic.\n // For shift up to 49 (7*7) the intermediate `(b & LOW_BITS) * 2^shift`\n // stays within Number.MAX_SAFE_INTEGER; beyond that the caller is\n // expected to keep call IDs within 2^53.\n value += (b & LOW_BITS) * Math.pow(2, shift);\n if (b <= LOW_BITS) return [value, offset];\n shift += 7;\n }\n // 10th byte — matches .NET's 1-bit allowance.\n if (offset >= data.length)\n throw new RangeError('IncreasingSeqCompressor: truncated VarUInt.');\n const b = data[offset++];\n if (b > 1)\n throw new RangeError('IncreasingSeqCompressor: VarUInt value exceeds 64 bits.');\n value += b * Math.pow(2, 63);\n return [value, offset];\n}\n","import Denque from 'denque';\r\nimport { PromiseSource } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcObjectId, IRpcObject } from './rpc-object.js';\r\nimport { RpcObjectKind } from './rpc-object.js';\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport { RpcStreamSender } from './rpc-stream-sender.js';\r\n\r\nconst { warnLog } = getLogs('RpcStream');\r\n\r\n/** Default ack period (matches .NET RpcStream defaults). */\r\nconst DEFAULT_ACK_PERIOD = 30;\r\n/** Default ack advance window (matches .NET RpcStream defaults). */\r\nconst DEFAULT_ACK_ADVANCE = 61;\r\n\r\n/** Parsed stream reference from the server's result payload. */\r\nexport interface RpcStreamRef {\r\n readonly hostId: string;\r\n readonly localId: number;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n}\r\n\r\n/**\r\n * A stream source is either a plain async iterable or a factory function\r\n * that receives an AbortSignal and returns an async iterable.\r\n * The AbortSignal is aborted when the sender is disconnected, allowing\r\n * the source to release resources (e.g., camera, microphone) promptly.\r\n */\r\nexport type RpcStreamSource<T> = AsyncIterable<T> | ((abortSignal: AbortSignal) => AsyncIterable<T>);\r\n\r\n/** Configuration options for creating a local (origin-side) RpcStream. */\r\nexport interface RpcStreamOptions<T> {\r\n ackPeriod?: number;\r\n /**\r\n * Wire-level flow-control window: the maximum index the sender may advance\r\n * past the last acknowledged index. Mirrors .NET `RpcStream.AckAdvance`.\r\n */\r\n ackAdvance?: number;\r\n /**\r\n * Local-only sender ring buffer capacity. When undefined (or smaller than\r\n * `ackAdvance`), `ackAdvance` is used as the buffer size.\r\n * Set this larger than `ackAdvance` to let the sender pre-buffer items\r\n * beyond the in-flight window. Not transmitted on the wire.\r\n */\r\n bufferSize?: number;\r\n allowReconnect?: boolean;\r\n isRealTime?: boolean;\r\n canSkipTo?: (item: T) => boolean;\r\n}\r\n\r\n/**\r\n * Parses a stream reference string of the form \"{hostId},{localId},{ackPeriod},{ackAdvance}\".\r\n * Returns null if the value is not a valid stream reference.\r\n */\r\nexport function parseStreamRef(value: unknown): RpcStreamRef | null {\r\n // Text format: \"hostId,localId,ackPeriod,ackAdvance[,allowReconnect[,isRealTime]]\"\r\n if (typeof value === 'string') {\r\n const parts = value.split(',');\r\n if (parts.length < 4 || parts.length > 6) return null;\r\n const hostId = parts[0];\r\n const localId = parseInt(parts[1], 10);\r\n const ackPeriod = parseInt(parts[2], 10);\r\n const ackAdvance = parseInt(parts[3], 10);\r\n if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance))\r\n return null;\r\n const allowReconnect = parts.length < 5 || parts[4] !== '0';\r\n const isRealTime = parts.length >= 6 && parts[5] === '1';\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };\r\n }\r\n // Binary (MessagePack) format: { SerializedId: [hostId, localId], AckPeriod, AckAdvance, AllowReconnect }\r\n if (typeof value === 'object' && value !== null) {\r\n const obj = value as Record<string, unknown>;\r\n const serializedId = obj.SerializedId as unknown[];\r\n if (!Array.isArray(serializedId) || serializedId.length < 2)\r\n return null;\r\n const rawHostId = serializedId[0];\r\n const hostId = String(rawHostId);\r\n const localId = Number(serializedId[1]);\r\n const ackPeriod = Number(obj.AckPeriod ?? 256);\r\n const ackAdvance = Number(obj.AckAdvance ?? 128);\r\n const allowReconnect = obj.AllowReconnect !== false;\r\n const isRealTime = obj.IsRealTime === true;\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Dual-mode RPC stream — works as both local (origin-side) and remote (target-side).\r\n *\r\n * **Local mode**: wraps an AsyncIterable and carries streaming configuration\r\n * (IsRealTime, CanSkipTo, etc.). Created by service methods and sent to the\r\n * remote peer via RpcStreamSender.\r\n *\r\n * **Remote mode**: consumes items sent by the server via system calls\r\n * ($sys.I, $sys.B, $sys.End) and implements AsyncIterable for for-await-of consumption.\r\n *\r\n * This mirrors the .NET RpcStream<T> design where the same type serves both roles.\r\n */\r\nexport class RpcStream<T> implements AsyncIterable<T>, IRpcObject {\r\n /**\r\n * Grace period (ms) given to a factory-form local source to honor its\r\n * AbortSignal and exit naturally before the sender force-closes the\r\n * iterator via `iterator.return()`.\r\n *\r\n * Only applies to sources provided as `(abortSignal) => AsyncIterable<T>`.\r\n * Plain `AsyncIterable<T>` sources are force-closed immediately on disconnect.\r\n */\r\n public static disconnectGracePeriodMs = 100;\r\n\r\n readonly id: RpcObjectId;\r\n readonly kind: RpcObjectKind;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n readonly canSkipTo: (item: T) => boolean;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n /**\r\n * Local-only ring buffer capacity hint for the sender. Undefined means\r\n * \"use ackAdvance as the buffer size\". Not part of the wire ref.\r\n */\r\n readonly bufferSize?: number;\r\n\r\n // Local (origin) mode\r\n readonly localSource?: RpcStreamSource<T>;\r\n\r\n // Remote (target) mode\r\n readonly peer!: RpcPeer;\r\n\r\n // Local-only state\r\n private _sender?: RpcStreamSender<T>;\r\n private _toRefValue?: unknown;\r\n private _whenSent?: Promise<void>;\r\n\r\n // Remote-only state\r\n private _buffer: Denque<T> = new Denque<T>();\r\n private _nextExpectedIndex = 0;\r\n private _consumerWaiting: PromiseSource<void> | null = null;\r\n private _completed = false;\r\n private _completionError: Error | null = null;\r\n private _disposed = false;\r\n private _started = false;\r\n private _iterating = false;\r\n private _ackSentUpTo = 0;\r\n private _nextConsumedIndex = 0;\r\n\r\n /** Create a local (origin-side) stream wrapping an async iterable or source factory with optional configuration. */\r\n constructor(source: RpcStreamSource<T>, options?: RpcStreamOptions<T>);\r\n /** Create a remote (target-side) stream from a parsed stream reference and peer. */\r\n constructor(ref: RpcStreamRef, peer: RpcPeer);\r\n constructor(\r\n sourceOrRef: RpcStreamSource<T> | RpcStreamRef,\r\n optionsOrPeer?: RpcStreamOptions<T> | RpcPeer,\r\n ) {\r\n if (_isStreamSource(sourceOrRef)) {\r\n // Local (origin) mode\r\n const options = (optionsOrPeer as RpcStreamOptions<T> | undefined) ?? {};\r\n this.kind = RpcObjectKind.Local;\r\n this.localSource = sourceOrRef;\r\n this.id = { hostId: '', localId: 0 };\r\n this.allowReconnect = options.allowReconnect ?? true;\r\n this.isRealTime = options.isRealTime ?? false;\r\n this.canSkipTo = options.canSkipTo ?? (() => true);\r\n this.ackPeriod = options.ackPeriod ?? DEFAULT_ACK_PERIOD;\r\n this.ackAdvance = options.ackAdvance ?? DEFAULT_ACK_ADVANCE;\r\n this.bufferSize = options.bufferSize;\r\n } else {\r\n // Remote (target) mode\r\n const ref = sourceOrRef;\r\n const peer = optionsOrPeer as RpcPeer;\r\n this.kind = RpcObjectKind.Remote;\r\n this.id = { hostId: ref.hostId, localId: ref.localId };\r\n this.peer = peer;\r\n this.allowReconnect = ref.allowReconnect;\r\n this.isRealTime = ref.isRealTime;\r\n this.canSkipTo = () => true; // not serialized\r\n this.ackPeriod = ref.ackPeriod;\r\n this.ackAdvance = ref.ackAdvance;\r\n // bufferSize is local-only; not present on the wire ref\r\n }\r\n }\r\n\r\n /**\r\n * For local streams: resolves when the sender finishes pumping (after `toRef()` was called).\r\n * Rejects if the source throws. Throws if `toRef()` hasn't been called yet.\r\n */\r\n get whenSent(): Promise<void> {\r\n if (!this._whenSent)\r\n throw new Error('toRef() must be called before awaiting whenSent.');\r\n return this._whenSent;\r\n }\r\n\r\n /** Local sender backing this stream after `toRef()` has been called.\r\n * `undefined` for remote streams or before `toRef()`. */\r\n get sender(): RpcStreamSender<T> | undefined {\r\n return this._sender;\r\n }\r\n\r\n /**\r\n * For local streams: bind to a peer, create and register an `RpcStreamSender`,\r\n * start pumping items from the local source in the background, and return the\r\n * stream reference.\r\n *\r\n * For binary peers the ref is a MessagePack-compatible object\r\n * (`{ SerializedId, AckPeriod, AckAdvance, AllowReconnect }`);\r\n * for text peers it is a comma-separated string.\r\n *\r\n * Can only be called once per stream.\r\n */\r\n toRef(peer: RpcPeer): unknown {\r\n if (this.kind !== RpcObjectKind.Local)\r\n throw new Error('toRef() is only valid for local streams.');\r\n if (this._sender) {\r\n if (this._sender.peer !== peer)\r\n throw new Error('toRef() was already called with a different peer.');\r\n // Same peer — return the cached ref\r\n return this._toRefValue!;\r\n }\r\n\r\n const isFactory = typeof this.localSource === 'function';\r\n const sender = new RpcStreamSender<T>(\r\n peer, this.ackPeriod, this.ackAdvance,\r\n this.allowReconnect, this.isRealTime, this.canSkipTo,\r\n isFactory, this.bufferSize,\r\n );\r\n peer.sharedObjects.register(sender);\r\n this._sender = sender;\r\n\r\n // Resolve source: if it's a factory, call it with the sender's AbortSignal\r\n const source = isFactory\r\n ? (this.localSource as (s: AbortSignal) => AsyncIterable<T>)(sender.abortSignal)\r\n : this.localSource as AsyncIterable<T>;\r\n\r\n // Start pumping in background (writeFrom awaits the initial Ack)\r\n this._whenSent = sender.writeFrom(source);\r\n\r\n // Cache and return ref in the format appropriate for the peer's serialization\r\n this._toRefValue = peer.serializationFormat.isBinary\r\n ? {\r\n SerializedId: [sender.id.hostId, sender.id.localId],\r\n AckPeriod: sender.ackPeriod,\r\n AckAdvance: sender.ackAdvance,\r\n AllowReconnect: sender.allowReconnect,\r\n }\r\n : sender.toRef();\r\n return this._toRefValue;\r\n }\r\n\r\n /** Called by system call handler when a single item arrives ($sys.I). */\r\n onItem(index: number, item: T): void {\r\n if (this._disposed || this._completed) return;\r\n\r\n if (index > this._nextExpectedIndex) {\r\n warnLog?.log(`item index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);\r\n if (!this.allowReconnect) {\r\n this._completed = true;\r\n this._completionError = new Error(\r\n `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`\r\n );\r\n this._notifyConsumer();\r\n return;\r\n }\r\n this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n // Duplicate — ack and ignore\r\n this._maybeSendAck(Math.min(index + 1, this._nextConsumedIndex));\r\n return;\r\n }\r\n\r\n this._buffer.push(item);\r\n this._nextExpectedIndex = index + 1;\r\n this._notifyConsumer();\r\n }\r\n\r\n /** Called by system call handler when a batch arrives ($sys.B). */\r\n onBatch(index: number, items: T[]): void {\r\n if (this._disposed || this._completed) return;\r\n\r\n if (index > this._nextExpectedIndex) {\r\n warnLog?.log(`batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`);\r\n if (!this.allowReconnect) {\r\n this._completed = true;\r\n this._completionError = new Error(\r\n `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`\r\n );\r\n this._notifyConsumer();\r\n return;\r\n }\r\n this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n this._maybeSendAck(Math.min(index + items.length, this._nextConsumedIndex));\r\n return;\r\n }\r\n\r\n for (const item of items) this._buffer.push(item);\r\n this._nextExpectedIndex = index + items.length;\r\n this._notifyConsumer();\r\n }\r\n\r\n /** Called by system call handler when the stream ends ($sys.End). */\r\n onEnd(index: number, error: Error | null): void {\r\n if (this._disposed || this._completed) return;\r\n this._completed = true;\r\n this._completionError = error;\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- IRpcObject --\r\n\r\n reconnect(): void {\r\n if (this.kind === RpcObjectKind.Local) return; // no-op for local streams\r\n if (this.allowReconnect) {\r\n this._sendAck(this._nextExpectedIndex, true);\r\n } else {\r\n this.disconnect();\r\n }\r\n }\r\n\r\n disconnect(): void {\r\n if (this.kind === RpcObjectKind.Local) {\r\n // Propagate to sender — aborts the AbortSignal and terminates the iterator\r\n this._sender?.disconnect();\r\n return;\r\n }\r\n if (this._completed) return;\r\n this._completed = true;\r\n this._completionError = new Error('Peer disconnected.');\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- AsyncIterable --\r\n\r\n [Symbol.asyncIterator](): AsyncIterator<T> {\r\n // Local mode: delegate to the local source\r\n if (this.kind === RpcObjectKind.Local) {\r\n const source = typeof this.localSource === 'function'\r\n ? this.localSource(new AbortController().signal)\r\n : this.localSource!;\r\n return source[Symbol.asyncIterator]();\r\n }\r\n\r\n // Remote mode: buffer-based consumption\r\n if (this._iterating)\r\n throw new Error('RpcStream can only be iterated once.');\r\n this._iterating = true;\r\n\r\n return {\r\n next: async (): Promise<IteratorResult<T>> => {\r\n // Lazy start: send initial ack on first next() call\r\n if (!this._started) {\r\n this._started = true;\r\n this._sendAck(0, true);\r\n }\r\n this._maybeSendAck(this._nextConsumedIndex);\r\n\r\n // Read from buffer or wait for new data\r\n for (;;) {\r\n if (!this._buffer.isEmpty()) {\r\n // shift() is O(1) on Denque and releases the slot so the\r\n // ring buffer can be reclaimed as the consumer drains.\r\n const value = this._buffer.shift()!;\r\n this._nextConsumedIndex++;\r\n return { value, done: false };\r\n }\r\n\r\n if (this._completed) {\r\n if (this._completionError) throw this._completionError;\r\n return { value: undefined as T, done: true };\r\n }\r\n\r\n // Wait for more data\r\n this._consumerWaiting = new PromiseSource<void>();\r\n await this._consumerWaiting;\r\n }\r\n },\r\n\r\n return: (): Promise<IteratorResult<T>> => {\r\n this._sendAckEnd();\r\n this.dispose();\r\n return Promise.resolve({ value: undefined as T, done: true });\r\n },\r\n };\r\n }\r\n\r\n dispose(): void {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n this._completed = true;\r\n if (this.kind === RpcObjectKind.Remote) {\r\n this.peer.remoteObjects.unregister(this);\r\n }\r\n this._notifyConsumer();\r\n }\r\n\r\n // -- Private --\r\n\r\n private _notifyConsumer(): void {\r\n if (this._consumerWaiting !== null) {\r\n this._consumerWaiting.resolve();\r\n this._consumerWaiting = null;\r\n }\r\n }\r\n\r\n private _maybeSendAck(nextIndex: number): void {\r\n if (this.ackPeriod <= 0) return;\r\n const threshold = this._ackSentUpTo + this.ackPeriod;\r\n if (nextIndex >= threshold) {\r\n this._sendAck(nextIndex, false);\r\n }\r\n }\r\n\r\n private static readonly _emptyGuid = '00000000-0000-0000-0000-000000000000';\r\n\r\n private _sendAck(nextIndex: number, mustReset: boolean): void {\r\n this._ackSentUpTo = nextIndex;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n const hostId = mustReset ? this.id.hostId : RpcStream._emptyGuid;\r\n this.peer.hub.systemCallSender.ack(\r\n conn,\r\n this.peer.serializationFormat,\r\n this.id.localId,\r\n nextIndex,\r\n hostId\r\n );\r\n }\r\n }\r\n\r\n private _sendAckEnd(): void {\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.ackEnd(\r\n conn,\r\n this.peer.serializationFormat,\r\n this.id.localId,\r\n this.id.hostId\r\n );\r\n }\r\n }\r\n}\r\n\r\n/** @internal Type guard for RpcStreamSource (AsyncIterable or factory function). */\r\nfunction _isStreamSource<T>(value: unknown): value is RpcStreamSource<T> {\r\n if (typeof value === 'function') return true;\r\n return (\r\n value !== null &&\r\n typeof value === 'object' &&\r\n Symbol.asyncIterator in (value)\r\n );\r\n}\r\n\r\n/**\r\n * Recursively walks a deserialized value and replaces any stream ref strings\r\n * with live RpcStream<unknown> instances registered on the given peer.\r\n * This handles nested stream refs inside complex return types (e.g. Table<T>\r\n * containing RpcStream<Row<T>> fields serialized as \"{hostId},{localId},{ackPeriod},{ackAdvance}\").\r\n */\r\nexport function resolveStreamRefs(value: unknown, peer: RpcPeer): unknown {\r\n if (value === null || value === undefined) return value;\r\n\r\n if (typeof value === 'string') {\r\n const ref = parseStreamRef(value);\r\n if (ref !== null) {\r\n const stream = new RpcStream(ref, peer);\r\n peer.remoteObjects.register(stream);\r\n return stream;\r\n }\r\n return value;\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n for (let i = 0; i < value.length; i++) {\r\n value[i] = resolveStreamRefs(value[i], peer);\r\n }\r\n return value;\r\n }\r\n\r\n if (typeof value === 'object') {\r\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer)\r\n return value;\r\n\r\n for (const key of Object.keys(value as Record<string, unknown>)) {\r\n (value as Record<string, unknown>)[key] = resolveStreamRefs(\r\n (value as Record<string, unknown>)[key],\r\n peer\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n return value;\r\n}\r\n","export interface RpcObjectId {\r\n readonly hostId: string;\r\n readonly localId: number;\r\n}\r\n\r\nexport const RpcObjectKind = { Local: 0, Remote: 1 } as const;\r\nexport type RpcObjectKind = (typeof RpcObjectKind)[keyof typeof RpcObjectKind];\r\n\r\nexport interface IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind: RpcObjectKind;\r\n readonly allowReconnect: boolean;\r\n reconnect(): void;\r\n disconnect(): void;\r\n}\r\n","import { PromiseSource, RingBuffer } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcObjectId, IRpcObject } from './rpc-object.js';\r\nimport { RpcObjectKind } from './rpc-object.js';\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport { RpcStream } from './rpc-stream.js';\r\n\r\nconst { warnLog } = getLogs('RpcSharedStream');\r\n\r\n/** Default ack period for server-side streams. */\r\nconst DEFAULT_ACK_PERIOD = 256;\r\n/** Default ack advance window for server-side streams. */\r\nconst DEFAULT_ACK_ADVANCE = 128;\r\n\r\n/**\r\n * Server-side RPC stream producer — sends items to a remote consumer via\r\n * $sys.I / $sys.B / $sys.End system calls.\r\n *\r\n * Wire protocol (server perspective):\r\n * - Server returns the stream reference string as $sys.Ok\r\n * - Client sends Ack(0, hostId) to start the stream\r\n * - Server sends I (single item) / B (batch) messages\r\n * - Client acks every ackPeriod items for flow control\r\n * - Server sends End to signal completion (with optional error)\r\n * - Client sends AckEnd to acknowledge completion\r\n *\r\n * The sender uses a single ACK-driven loop:\r\n * - On each ACK, drain the source synchronously into the ring buffer up to\r\n * `effectiveBufferSize` (this lets us pre-buffer past `ackAdvance`).\r\n * - For real-time, optionally compact the unsent suffix to the latest\r\n * buffered `canSkipTo` item.\r\n * - Send items up to the `ackAdvance` flow-control window.\r\n * - Wait for the next ACK.\r\n *\r\n * Setting `bufferSize > ackAdvance` lets the sender pre-buffer items past\r\n * the in-flight window so a fresh ACK can be served from RAM instead of\r\n * waiting on the source.\r\n *\r\n * Direct port of .NET `RpcSharedStream<T>` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.\r\n */\r\nexport class RpcStreamSender<T> implements IRpcObject {\r\n private static readonly _emptyGuid = '00000000-0000-0000-0000-000000000000';\r\n\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Local;\r\n readonly allowReconnect: boolean;\r\n readonly isRealTime: boolean;\r\n readonly canSkipTo: (item: T) => boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n /**\r\n * Wire-level flow-control window: the maximum index the sender may\r\n * advance past the most recently acknowledged index. Mirrors .NET\r\n * `RpcStream.AckAdvance`.\r\n */\r\n readonly ackAdvance: number;\r\n /**\r\n * Effective local ring buffer capacity used by the pump. Set larger\r\n * than `ackAdvance` to pre-buffer items beyond the in-flight window;\r\n * values below `ackAdvance` are clamped up (with a warning) at\r\n * construction time. Not transmitted on the wire.\r\n */\r\n readonly bufferSize: number;\r\n /**\r\n * If true, the source was produced by a factory `(abortSignal) => AsyncIterable<T>`\r\n * and may honor the AbortSignal to exit gracefully. `disconnect()` first aborts\r\n * the signal and gives the source up to `RpcStream.disconnectGracePeriodMs` ms\r\n * before force-closing via `iterator.return()`.\r\n *\r\n * If false, the source is a plain AsyncIterable that cannot observe the signal,\r\n * so `disconnect()` force-closes immediately.\r\n */\r\n readonly sourceUsesAbortSignal: boolean;\r\n\r\n // -- Pump state --\r\n /** Next stream index that will be sent (matches .NET's `index`). */\r\n private _nextIndex = 0;\r\n private _ended = false;\r\n private _started = new PromiseSource<void>();\r\n /** Highest `nextIndex` observed in an ACK so far (matches .NET's `lastAckNextIndex` idiom). */\r\n private _lastAckedIndex = 0;\r\n private _abortController = new AbortController();\r\n private _iterator: AsyncIterator<T> | null = null;\r\n\r\n // -- Recorder-controller metrics (Step 9.1) --\r\n /** Total items skipped via real-time `canSkipTo` compaction. */\r\n private _skipCount = 0;\r\n /** Fires after a $sys.Ack has been processed (post-compaction). The\r\n * recorder's quality controller uses this to bump a \"last ACK at\"\r\n * watchdog so it can distinguish \"stuck\" (no ACK > N s) from\r\n * \"throttled\" (ACKs flowing). */\r\n onAckProcessed?: () => void;\r\n /** Fires after each item is pushed onto the local ring buffer. The\r\n * argument is the buffer count *after* the push. Together with\r\n * `onAckProcessed` (which is the only place items leave the buffer),\r\n * this gives controllers a complete picture of buffer utilization.\r\n * When `bufferedCount === bufferSize`, the source pull is paused\r\n * until the next ACK frees space. Listener errors are swallowed. */\r\n onBuffered?: (bufferedCount: number) => void;\r\n\r\n get nextIndex(): number {\r\n return this._nextIndex;\r\n }\r\n get lastAckIndex(): number {\r\n return this._lastAckedIndex;\r\n }\r\n get skipCount(): number {\r\n return this._skipCount;\r\n }\r\n\r\n // -- ACK queue (analog of .NET's Channel<(long, bool)>) --\r\n private _acks: { nextIndex: number; mustReset: boolean }[] = [];\r\n private _whenAckReady: PromiseSource<void> | null = null;\r\n\r\n constructor(\r\n peer: RpcPeer,\r\n ackPeriod = DEFAULT_ACK_PERIOD,\r\n ackAdvance = DEFAULT_ACK_ADVANCE,\r\n allowReconnect = true,\r\n isRealTime = false,\r\n canSkipTo: (item: T) => boolean = () => true,\r\n sourceUsesAbortSignal = false,\r\n bufferSize?: number,\r\n ) {\r\n const localId = peer.sharedObjects.nextId();\r\n this.id = { hostId: peer.hub.hubId, localId };\r\n this.allowReconnect = allowReconnect;\r\n this.isRealTime = isRealTime;\r\n this.canSkipTo = canSkipTo;\r\n this.peer = peer;\r\n this.ackPeriod = ackPeriod;\r\n this.ackAdvance = ackAdvance;\r\n this.sourceUsesAbortSignal = sourceUsesAbortSignal;\r\n\r\n if (bufferSize !== undefined && bufferSize !== 0 && bufferSize < ackAdvance)\r\n warnLog?.log(\r\n `RpcStream bufferSize (${bufferSize}) is below ackAdvance (${ackAdvance}); using ackAdvance as buffer size.`,\r\n );\r\n this.bufferSize = bufferSize !== undefined && bufferSize >= ackAdvance ? bufferSize : ackAdvance;\r\n }\r\n\r\n /** AbortSignal that is aborted when the sender is disconnected. */\r\n get abortSignal(): AbortSignal {\r\n return this._abortController.signal;\r\n }\r\n\r\n /** Returns the stream reference string for the $sys.Ok response. */\r\n toRef(): string {\r\n return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? '1' : '0'},${this.isRealTime ? '1' : '0'}`;\r\n }\r\n\r\n /**\r\n * Called by system call handler when $sys.Ack is received from the client.\r\n *\r\n * Mirrors .NET `RpcSharedStream<T>.OnAck` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs:68-101.\r\n */\r\n onAck(nextIndex: number, hostId: string): void {\r\n const mustReset = hostId !== '' && hostId !== RpcStreamSender._emptyGuid;\r\n if (!this._started.isCompleted) this._started.resolve();\r\n this._acks.push({ nextIndex, mustReset });\r\n if (this._whenAckReady) {\r\n this._whenAckReady.resolve();\r\n this._whenAckReady = null;\r\n }\r\n }\r\n\r\n /** Called by system call handler when $sys.AckEnd is received from the client. */\r\n onAckEnd(_hostId: string): void {\r\n this.disconnect();\r\n }\r\n\r\n /**\r\n * Send a single item to the client at the current `_nextIndex`.\r\n *\r\n * Advances `_nextIndex` unconditionally — even if the peer is disconnected\r\n * the item is considered \"sent\" from the pump's perspective so the replay\r\n * buffer indexing stays consistent. If a connection is absent, the wire\r\n * message is dropped; the item will be resent from the replay buffer\r\n * after a reconnect (client sends `Ack(N, mustReset=true)`).\r\n */\r\n sendItem(item: T): void {\r\n if (this._ended) return;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.item(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, item,\r\n );\r\n }\r\n this._nextIndex++;\r\n }\r\n\r\n /** Send a batch of items to the client. See {@link sendItem} for disconnect semantics. */\r\n sendBatch(items: T[]): void {\r\n if (this._ended || items.length === 0) return;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.batch(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, items,\r\n );\r\n }\r\n this._nextIndex += items.length;\r\n }\r\n\r\n /** Signal stream completion to the client. */\r\n sendEnd(error?: Error | null): void {\r\n if (this._ended) return;\r\n this._ended = true;\r\n const conn = this.peer.connection;\r\n if (!conn) return;\r\n // .NET ExceptionInfo is a non-nullable value type, so we must always\r\n // emit a valid map shape (empty TypeRef+Message for the \"no error\" case).\r\n const errorInfo = error\r\n ? { TypeRef: 'System.Exception', Message: error.message }\r\n : { TypeRef: '', Message: '' };\r\n this.peer.hub.systemCallSender.end(\r\n conn, this.peer.serializationFormat, this.id.localId, this._nextIndex, errorInfo,\r\n );\r\n }\r\n\r\n /**\r\n * Consume an AsyncIterable and send all items to the client.\r\n *\r\n * Matches .NET `RpcSharedStream<T>.OnRun` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcSharedStream.cs.\r\n */\r\n async writeFrom(source: AsyncIterable<T>): Promise<void> {\r\n await this._started;\r\n if (this._ended) return;\r\n\r\n const iterator = source[Symbol.asyncIterator]();\r\n this._iterator = iterator;\r\n // Tracks whether the generator has reached a terminal state (done=true\r\n // or thrown). When true, its finally block has already run and the\r\n // iterator reference can be cleared. When false, disconnect()'s\r\n // scheduled iterator.return() is what drives it to its finally.\r\n const state: _PumpState = { iteratorDone: false };\r\n try {\r\n await this._run(iterator, state);\r\n } finally {\r\n if (state.iteratorDone && this._iterator === iterator) {\r\n this._iterator = null;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * ACK-driven pump. Non-real-time streams apply normal backpressure at\r\n * `ackAdvance`; real-time streams may compact the already-buffered unsent\r\n * suffix to the latest buffered `canSkipTo` item after the consumer ACKs.\r\n * With `bufferSize > ackAdvance` the local ring buffer holds more than the\r\n * in-flight window — real-time mode uses the extra space to pre-buffer.\r\n */\r\n /* eslint-disable @typescript-eslint/no-unnecessary-condition -- _ended changes across awaits */\r\n private async _run(\r\n iterator: AsyncIterator<T>,\r\n state: _PumpState,\r\n ): Promise<void> {\r\n const buffer = new RingBuffer<_StreamItem<T>>(this.bufferSize + 1);\r\n const isRealTime = this.isRealTime;\r\n let bufferStart = 0;\r\n let isFullyBuffered = false;\r\n let whenMovedNext: Promise<IteratorResult<T>> | null = null;\r\n const pending = Symbol('pending');\r\n\r\n const readNext = async (): Promise<IteratorResult<T>> => {\r\n const next = whenMovedNext ?? iterator.next();\r\n whenMovedNext = null;\r\n return await next;\r\n };\r\n\r\n const tryReadReady = async (): Promise<IteratorResult<T> | typeof pending> => {\r\n whenMovedNext ??= iterator.next();\r\n return await Promise.race([\r\n whenMovedNext,\r\n Promise.resolve(pending),\r\n ]);\r\n };\r\n\r\n const fireBuffered = (): void => {\r\n try {\r\n this.onBuffered?.(buffer.count);\r\n } catch { /* listener errors don't break the pump */ }\r\n };\r\n\r\n const bufferNext = async (onlyIfReady: boolean, prefetchNext: boolean): Promise<boolean> => {\r\n let r: IteratorResult<T> | typeof pending;\r\n try {\r\n r = onlyIfReady ? await tryReadReady() : await readNext();\r\n if (r === pending)\r\n return false;\r\n whenMovedNext = null;\r\n if (this._ended) return false;\r\n if (r.done) {\r\n buffer.pushTail(_endItem);\r\n isFullyBuffered = true;\r\n state.iteratorDone = true;\r\n } else {\r\n buffer.pushTail({ kind: 'value', value: r.value });\r\n if (prefetchNext)\r\n whenMovedNext = iterator.next();\r\n }\r\n fireBuffered();\r\n return true;\r\n } catch (e) {\r\n whenMovedNext = null;\r\n state.iteratorDone = true;\r\n isFullyBuffered = true;\r\n buffer.pushTail({\r\n kind: 'error',\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n fireBuffered();\r\n return true;\r\n }\r\n };\r\n\r\n while (true) {\r\n // ---- nextAck ----\r\n // 1. Await for an acknowledgement & process accumulated ACKs.\r\n let ack = this._tryProcessAcks();\r\n if (!ack) {\r\n await this._waitAckReady();\r\n if (this._ended) return;\r\n ack = this._tryProcessAcks();\r\n if (!ack) {\r\n warnLog?.log(\"Something is off: couldn't read an acknowledgement\");\r\n return;\r\n }\r\n }\r\n this._lastAckedIndex = ack.nextIndex;\r\n\r\n // 1.1. Reconnect: real-time streams can clear stale buffered data\r\n // and restart from the next source item accepted by canSkipTo.\r\n if (isRealTime && ack.mustReset && !isFullyBuffered) {\r\n buffer.clear();\r\n bufferStart = this._nextIndex;\r\n while (true) {\r\n if (this._ended) return;\r\n let item: _StreamItem<T>;\r\n let accepted = false;\r\n try {\r\n const r = await readNext();\r\n if (this._ended) return;\r\n if (r.done) {\r\n item = _endItem;\r\n isFullyBuffered = true;\r\n state.iteratorDone = true;\r\n } else if (this.canSkipTo(r.value)) {\r\n item = { kind: 'value', value: r.value };\r\n accepted = true;\r\n } else {\r\n continue; // discard; keep draining\r\n }\r\n } catch (e) {\r\n state.iteratorDone = true;\r\n isFullyBuffered = true;\r\n item = {\r\n kind: 'error',\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n };\r\n }\r\n buffer.pushTail(item);\r\n fireBuffered();\r\n if (accepted || isFullyBuffered) break;\r\n }\r\n }\r\n\r\n // 2. Remove what's useless from the buffer.\r\n const shift = _clamp(ack.nextIndex - bufferStart, 0, buffer.count);\r\n if (shift > 0) {\r\n buffer.moveHead(shift);\r\n bufferStart += shift;\r\n }\r\n\r\n // 3. Recalculate the next range to send.\r\n if (this._nextIndex < bufferStart) {\r\n if (!this._ended)\r\n this.sendEnd(new Error('Stream position unavailable.'));\r\n return;\r\n }\r\n let bufferIndex = this._nextIndex - bufferStart;\r\n\r\n // 3. Send as much as the current ACK window allows.\r\n const maxIndex = ack.nextIndex + this.ackAdvance;\r\n while (this._nextIndex < maxIndex) {\r\n if (this._ended) return;\r\n\r\n if (isRealTime) {\r\n // Real-time: pre-buffer aggressively up to the ring's\r\n // capacity (which may exceed ackAdvance when bufferSize\r\n // is set), so a freshly arrived ACK is served from RAM.\r\n while (buffer.hasRemainingCapacity && !isFullyBuffered && this._acks.length === 0) {\r\n if (!await bufferNext(true, true))\r\n break;\r\n }\r\n if (bufferIndex >= buffer.count\r\n && !isFullyBuffered\r\n && buffer.hasRemainingCapacity\r\n && this._acks.length === 0) {\r\n await bufferNext(false, true);\r\n }\r\n if (ack.nextIndex > 0) {\r\n const result = _compactBufferedUnsentSuffix(\r\n buffer, bufferIndex, this.canSkipTo);\r\n bufferIndex = result.newIndex;\r\n if (result.skipped > 0)\r\n this._skipCount += result.skipped;\r\n }\r\n\r\n // Bail out so step 1 can process a mustReset ACK immediately.\r\n if (this._acks.length > 0) break;\r\n }\r\n\r\n // Drain buffered items until the window is full.\r\n while (this._nextIndex < maxIndex && bufferIndex < buffer.count) {\r\n const item = buffer.get(bufferIndex++);\r\n if (item.kind === 'value') {\r\n this.sendItem(item.value);\r\n } else if (item.kind === 'end') {\r\n if (!this._ended) this.sendEnd();\r\n return;\r\n } else {\r\n if (!this._ended) this.sendEnd(item.error);\r\n return;\r\n }\r\n }\r\n if (this._nextIndex >= maxIndex)\r\n break;\r\n if (isFullyBuffered) break;\r\n if (!buffer.hasRemainingCapacity) break;\r\n\r\n if (!isRealTime)\r\n await bufferNext(false, false);\r\n }\r\n }\r\n }\r\n /* eslint-enable @typescript-eslint/no-unnecessary-condition */\r\n\r\n /** Drain all queued ACKs, returning the most recent one (or null if none). */\r\n private _tryProcessAcks(): { nextIndex: number; mustReset: boolean } | null {\r\n if (this._acks.length === 0) return null;\r\n let last: { nextIndex: number; mustReset: boolean } | null = null;\r\n while (this._acks.length > 0) {\r\n const a = this._acks.shift()!;\r\n last = a;\r\n if (a.mustReset || this._nextIndex < a.nextIndex) {\r\n this._nextIndex = a.nextIndex;\r\n }\r\n }\r\n try {\r\n this.onAckProcessed?.();\r\n } catch { /* listener errors don't break the pump */ }\r\n return last;\r\n }\r\n\r\n /** Wait for the ACK queue to have at least one entry (or `_ended`). */\r\n private async _waitAckReady(): Promise<void> {\r\n if (this._acks.length > 0 || this._ended) return;\r\n this._whenAckReady = new PromiseSource<void>();\r\n await this._whenAckReady;\r\n this._whenAckReady = null;\r\n }\r\n\r\n // -- IRpcObject --\r\n\r\n reconnect(): void {\r\n // Server-side streams don't support reconnection — the client\r\n // creates a new stream on reconnect.\r\n }\r\n\r\n disconnect(): void {\r\n if (this._ended) return;\r\n this._ended = true;\r\n this._abortController.abort();\r\n if (!this._started.isCompleted) {\r\n this._started.resolve();\r\n }\r\n if (this._whenAckReady) {\r\n this._whenAckReady.resolve();\r\n this._whenAckReady = null;\r\n }\r\n this.peer.sharedObjects.unregister(this);\r\n\r\n // Iterator force-close policy:\r\n // - Plain AsyncIterable: abortSignal is unobserved, so force-close now.\r\n // - Factory source (abortSignal-aware): give it a grace period to exit\r\n // gracefully, then force-close if still pumping.\r\n const iterator = this._iterator;\r\n if (!iterator) return;\r\n if (!this.sourceUsesAbortSignal) {\r\n this._iterator = null;\r\n this._forceCloseIterator(iterator);\r\n return;\r\n }\r\n setTimeout(() => {\r\n if (this._iterator === iterator) {\r\n this._iterator = null;\r\n this._forceCloseIterator(iterator);\r\n }\r\n }, RpcStream.disconnectGracePeriodMs);\r\n }\r\n\r\n private _forceCloseIterator(iterator: AsyncIterator<T>): void {\r\n try {\r\n const ret = iterator.return?.(undefined);\r\n if (ret) void ret.catch(() => { /* ignore */ });\r\n } catch { /* ignore */ }\r\n }\r\n}\r\n\r\n// -- Internal helpers (file-local) --\r\n\r\n/** Wrapper matching .NET's `Result<T>` — value, end-of-stream, or error. */\r\ntype _StreamItem<T> =\r\n | { kind: 'value'; value: T }\r\n | { kind: 'end' }\r\n | { kind: 'error'; error: Error };\r\n\r\n/** Singleton \"source exhausted\" marker (matches .NET's `NoMoreItemsTag`). */\r\nconst _endItem: _StreamItem<never> = { kind: 'end' };\r\n\r\n/** Shared mutable state between writeFrom and its per-mode pump. */\r\ninterface _PumpState {\r\n iteratorDone: boolean;\r\n}\r\n\r\nfunction _clamp(value: number, min: number, max: number): number {\r\n return value < min ? min : value > max ? max : value;\r\n}\r\n\r\nfunction _compactBufferedUnsentSuffix<T>(\r\n buffer: RingBuffer<_StreamItem<T>>,\r\n firstUnsentIndex: number,\r\n canSkipTo: (item: T) => boolean,\r\n): { newIndex: number; skipped: number } {\r\n // Keep items that have already been assigned RPC stream indexes, then\r\n // collapse the unsent suffix to the latest buffered restart point. The\r\n // surviving suffix is intentionally sent under fresh RPC stream indexes;\r\n // item-level timestamps/frame indexes are the caller's responsibility.\r\n let skipToIndex = -1;\r\n for (let i = firstUnsentIndex; i < buffer.count; i++) {\r\n const item = buffer.get(i);\r\n if (item.kind === 'value' && canSkipTo(item.value))\r\n skipToIndex = i;\r\n }\r\n if (skipToIndex <= firstUnsentIndex)\r\n return { newIndex: firstUnsentIndex, skipped: 0 };\r\n\r\n const cutFrom = firstUnsentIndex;\r\n const cutTo = skipToIndex;\r\n\r\n const items = buffer.toArray();\r\n buffer.clear();\r\n for (let i = 0; i < cutFrom; i++)\r\n buffer.pushTail(items[i]);\r\n for (let i = cutTo; i < items.length; i++)\r\n buffer.pushTail(items[i]);\r\n return { newIndex: firstUnsentIndex, skipped: skipToIndex - firstUnsentIndex };\r\n}\r\n","/**\n * Minimal base64 ↔ Uint8Array helpers used by the `$sys.Reconnect` protocol\n * (byte arrays are encoded as base64 strings on the JSON wire to match\n * .NET's default `System.Text.Json` serialization of `byte[]`).\n */\n\nexport function base64Encode(bytes: Uint8Array): string {\n // btoa works on latin1 strings; chunk to avoid \"Maximum call stack size exceeded\"\n // when building the intermediate string for large inputs.\n const chunkSize = 0x8000;\n let binary = '';\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(\n ...(bytes.subarray(i, Math.min(bytes.length, i + chunkSize)) as unknown as number[]),\n );\n }\n return btoa(binary);\n}\n\nexport function base64Decode(encoded: string): Uint8Array {\n const binary = atob(encoded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n","// .NET counterpart:\n// RpcSystemCalls (239 lines) — implements IRpcSystemCalls interface; handles\n// all inbound system messages as regular RPC method calls dispatched through\n// the same inbound-call pipeline as user calls.\n//\n// Omitted from .NET:\n// - Reconnect() handler — receives compressed call-ID sets grouped by stage,\n// looks up each inbound call, calls TryReprocess(), and returns the IDs of\n// unknown calls. TS doesn't implement the Reliable reconnection protocol.\n// - Cancel() handler — .NET finds the inbound call and cancels its\n// CancellationTokenSource, aborting the running handler. TS removes the\n// inbound call from the tracker but does not yet propagate cancellation\n// to the service handler (no AbortSignal threading through dispatch).\n// - M() (Match) handler — tells an outbound call to use its cached result\n// instead of the response payload. TS has no response caching.\n// - NotFound() — throws an EndpointNotFound error. TS sends this as a regular\n// $sys.Error; no separate system call type.\n// - KeepAlive() / Disconnect() — manage RpcSharedObject lifetimes. TS has no\n// shared-object tracker.\n// - IRpcPolymorphicArgumentHandler.IsValidCall — resolves the concrete\n// deserialization type for polymorphic Ok/Item/Batch arguments by looking up\n// the related outbound call's return type. TS deserializes all args as\n// unknown via JSON.parse (inherently polymorphic).\n// - DI / IServiceProvider / RpcServiceBase — .NET system calls are resolved via\n// DI. TS uses a class with a handle() method.\n\nimport { getLogs } from './logging.js';\nimport { RpcError } from './rpc-error.js';\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\nimport type { RpcPeer } from './rpc-peer.js';\nimport type { RpcStream } from './rpc-stream.js';\nimport { resolveStreamRefs } from './rpc-stream.js';\nimport type { RpcStreamSender } from './rpc-stream-sender.js';\nimport { RpcCallStage } from './rpc-call-stage.js';\nimport { IncreasingSeqCompressor } from './increasing-seq-compressor.js';\nimport { base64Decode, base64Encode } from './base64.js';\n\nconst { debugLog, warnLog } = getLogs('RpcSystemCallHandler');\n\n/** Handles incoming system call messages — class-based equivalent of the former standalone function. */\nexport class RpcSystemCallHandler {\n handle(message: RpcMessage, args: unknown[], peer: RpcPeer): void {\n const method = message.Method;\n const relatedId = message.RelatedId ?? 0;\n debugLog?.log(`'${peer.ref}': handle ${method}#${relatedId}`);\n\n switch (method) {\n case RpcSystemCalls.ok: {\n const call = peer.outboundCalls.get(relatedId);\n if (call !== undefined) {\n call.completedStage |= RpcCallStage.ResultReady;\n if (call.removeOnOk) {\n peer.outboundCalls.remove(relatedId);\n }\n call.result.resolve(args[0]);\n }\n break;\n }\n case RpcSystemCalls.reconnect: {\n // Server-side `$sys.Reconnect` handler. Computes the set of call\n // IDs the inbound tracker doesn't know about, replies with a\n // byte[] result via $sys.Ok. Mirrors .NET `RpcSystemCalls.Reconnect`\n // at src/ActualLab.Rpc/Infrastructure/RpcSystemCalls.cs:51-85.\n //\n // Limitations vs .NET: does not validate `handshakeIndex` against\n // this peer's own handshake, and does not perform per-stage\n // re-processing of compute calls (TS has no compute-invalidation\n // tracking on the inbound side). A call ID is reported as\n // \"known\" iff it is still in the inbound tracker.\n this._handleReconnect(relatedId, args, peer);\n break;\n }\n case RpcSystemCalls.error: {\n const call = peer.outboundCalls.remove(relatedId);\n if (call !== undefined) {\n const errorInfo = args[0] as\n | Record<string, unknown>\n | undefined;\n const msg = (errorInfo?.Message ??\n errorInfo?.message ??\n 'RPC error') as string;\n const errorType = errorInfo?.TypeRef ?? errorInfo?.typeRef;\n let typeName: string | undefined;\n if (typeof errorType === 'string') {\n // .NET TypeRef is serialized as the assembly-qualified name string,\n // e.g. \"System.InvalidOperationException, System.Private.CoreLib\".\n // Mirrors .NET TypeRef.TypeName: substring before the first ','.\n const commaIdx = errorType.indexOf(',');\n typeName = commaIdx >= 0 ? errorType.slice(0, commaIdx) : errorType;\n } else if (errorType !== null && typeof errorType === 'object') {\n const t = errorType as Record<string, unknown>;\n typeName = (t.TypeName ?? t.typeName) as string | undefined;\n }\n // Mirrors RpcSystemCalls.cs:102 — surface RpcRerouteException.\n if (typeName === 'ActualLab.Rpc.RpcRerouteException')\n warnLog?.log('Got RpcRerouteException from remote peer:', msg);\n call.result.reject(new RpcError(msg, typeName));\n }\n break;\n }\n case RpcSystemCalls.cancel: {\n // Remote peer is cancelling a call it asked us to process — remove\n // from the inbound tracker. Full cancellation propagation (aborting\n // the running service handler) is not yet implemented; this just\n // unregisters the call so we don't send a response for it.\n peer.inboundCalls.remove(relatedId);\n break;\n }\n case RpcSystemCalls.keepAlive: {\n // Remote keep-alive — mark the peer as \"alive\" so the peer's\n // watchdog doesn't force-close the connection. Mirrors .NET\n // `RpcObjectTrackers.KeepAlive` which sets `LastKeepAliveAt`.\n peer.notifyKeepAliveReceived();\n break;\n }\n case RpcSystemCalls.item: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n // Silent no-op when no stream — mirrors RpcSystemCalls.cs:164-170.\n // This is normal during reconnect: the server may send items for\n // streams the client has already disposed.\n if (!stream)\n debugLog?.log(`$sys.I: no stream for relatedId=${relatedId}`);\n else\n stream.onItem(\n args[0] as number,\n resolveStreamRefs(args[1], peer)\n );\n break;\n }\n case RpcSystemCalls.batch: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n if (!stream)\n debugLog?.log(`$sys.B: no stream for relatedId=${relatedId}`);\n else if (Array.isArray(args[1])) {\n const items = args[1] as unknown[];\n for (let i = 0; i < items.length; i++)\n items[i] = resolveStreamRefs(items[i], peer);\n stream.onBatch(args[0] as number, items);\n }\n break;\n }\n case RpcSystemCalls.end: {\n const stream = peer.remoteObjects.get(relatedId) as\n | RpcStream<unknown>\n | undefined;\n if (!stream) {\n debugLog?.log(`$sys.End: no stream for relatedId=${relatedId}`);\n } else {\n // .NET ExceptionInfo is a struct — even for normal completion, it serializes\n // as a non-null object with empty fields (e.g. { \"message\": \"\", \"typeRef\": {...} }).\n // Check both PascalCase and camelCase, and treat empty messages as no error.\n const errorInfo = args[1] as Record<string, unknown> | null;\n const msg = (errorInfo?.Message ?? errorInfo?.message) as\n | string\n | undefined;\n const error = msg ? new Error(msg) : null;\n stream.onEnd(args[0] as number, error);\n }\n break;\n }\n case RpcSystemCalls.ack: {\n const sender = peer.sharedObjects.get(relatedId) as\n | RpcStreamSender<unknown>\n | undefined;\n sender?.onAck(args[0] as number, args[1] as string);\n break;\n }\n case RpcSystemCalls.ackEnd: {\n const sender = peer.sharedObjects.get(relatedId) as\n | RpcStreamSender<unknown>\n | undefined;\n sender?.onAckEnd(args[0] as string);\n break;\n }\n case RpcSystemCalls.disconnect: {\n // Server is telling us the listed remote object IDs have been torn down\n // on its side (e.g. shared stream closed). Propagate disconnect() to the\n // matching remote objects so any pending consumers (for-await loops) exit\n // cleanly instead of hanging.\n const ids = args[0] as number[] | undefined;\n if (Array.isArray(ids)) {\n for (const id of ids) {\n const remoteObj = peer.remoteObjects.get(id);\n if (\n remoteObj &&\n typeof remoteObj.disconnect === 'function'\n ) {\n remoteObj.disconnect();\n }\n // Also check shared objects — server may disconnect a client-to-server\n // stream sender (e.g. audio stream) after pod restart or timeout.\n const sharedObj = peer.sharedObjects.get(id);\n if (\n sharedObj &&\n typeof sharedObj.disconnect === 'function'\n ) {\n sharedObj.disconnect();\n }\n }\n }\n break;\n }\n }\n }\n\n /**\n * Server-side handler for the `$sys.Reconnect` call. Parses the peer's\n * report of in-flight call IDs (grouped by stage, IncreasingSeqCompressor-\n * encoded, base64-wrapped for JSON transport), computes the subset this\n * peer does not recognize, and replies with a compressed byte[] of the\n * unknown IDs wrapped in `$sys.Ok`.\n *\n * @param relatedId The outbound call ID the client used for this\n * `$sys.Reconnect` invocation — we send `$sys.Ok` back\n * with the same `RelatedId`.\n */\n private _handleReconnect(relatedId: number, args: unknown[], peer: RpcPeer): void {\n if (peer.connection === undefined) return;\n\n // args[0]: handshakeIndex (number) — not validated in TS, see class comment.\n // args[1]: completedStagesData — shape depends on wire format.\n // JSON: { [stage: string]: base64-string }\n // msgpack: Map-like with int keys and Uint8Array values (unsupported in TS).\n let unknownIds: number[] = [];\n try {\n const stagesRaw = args[1];\n if (stagesRaw && typeof stagesRaw === 'object' && !Array.isArray(stagesRaw)) {\n const unknownSet = new Set<number>();\n for (const [, rawValue] of Object.entries(stagesRaw as Record<string, unknown>)) {\n const bytes = _toBytes(rawValue);\n if (bytes === null) continue;\n const callIds = IncreasingSeqCompressor.deserialize(bytes);\n for (const callId of callIds) {\n if (peer.inboundCalls.get(callId) === undefined) {\n unknownSet.add(callId);\n }\n }\n }\n unknownIds = [...unknownSet].sort((a, b) => a - b);\n }\n } catch {\n // If parsing fails, report all as unknown (safest behavior — client\n // will resend everything).\n // Client handles this via its own try/catch around the reconcile call.\n }\n\n const responseBytes = IncreasingSeqCompressor.serialize(unknownIds);\n const responseValue = peer.serializationFormat.isBinary ? responseBytes : base64Encode(responseBytes);\n peer.hub.systemCallSender.ok(peer.connection, peer.serializationFormat, relatedId, responseValue);\n }\n}\n\n/**\n * Coerce a wire value carrying a byte[] into a Uint8Array.\n * - JSON format sends byte[] as a base64 string.\n * - msgpack format sends byte[] as native `bin` (Uint8Array).\n * Returns `null` if the value is neither.\n */\nfunction _toBytes(value: unknown): Uint8Array | null {\n if (value instanceof Uint8Array) return value;\n if (typeof value === 'string') {\n try { return base64Decode(value); } catch { return null; }\n }\n return null;\n}\n","// .NET counterpart:\r\n// RpcSystemCallSender (202 lines) — a DI-registered service that sends system\r\n// calls by creating RpcOutboundContext + calling PrepareCallForSendNoWait +\r\n// serialising via the full outbound message pipeline.\r\n//\r\n// Omitted from .NET:\r\n// - Complete<TResult>() / typed Ok<TResult>() — .NET serialises the Ok result\r\n// using the method's unwrapped return type for proper polymorphic handling.\r\n// TS uses JSON.stringify(result) which handles any type uniformly.\r\n// - Match() — sends $sys.M when the response hash matches a cached entry,\r\n// avoiding re-sending the full payload. TS has no response caching.\r\n// - NotFound() — TS sends \"Service not found\" / \"Method not found\" as $sys.Error.\r\n// - Disconnect() — sends $sys.Disconnect with object IDs for shared-object\r\n// lifetime management. TS has no shared-object tracker.\r\n// - Ack() / AckEnd() / Item<T>() / Batch<T>() / End() — stream control\r\n// messages for RpcStream. TS has no streaming support yet.\r\n// - RpcOutboundContext / PrepareCallForSendNoWait pipeline — .NET creates a full\r\n// outbound context with MethodDef, ArgumentList, etc., then serialises via the\r\n// peer's MessageSerializer. TS directly calls serializeMessage() (JSON) since\r\n// there's only one serialization format.\r\n// - Tracing / CallLogger integration — .NET logs each system call through the\r\n// peer's CallLogger. TS has no tracing infrastructure.\r\n// - StopMode-aware Error() — .NET checks peer.StopMode to decide whether to\r\n// suppress error responses when the peer is shutting down. TS has no stop\r\n// mode concept.\r\n\r\nimport { getLogs } from './logging.js';\r\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\r\nimport type { RpcConnection } from './rpc-connection.js';\r\nimport type { RpcSerializationFormat } from './rpc-serialization-format.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\nconst { errorLog } = getLogs('RpcSystemCallSender');\r\n\r\n/**\r\n * Sends system RPC messages — like .NET's RpcSystemCallSender.\r\n *\r\n * Hot-path allocation notes: `item`, `batch`, `ack`, and `end` are called\r\n * once per stream frame (or once per ack window). Naively building a fresh\r\n * `{ Method, RelatedId }` envelope object plus a fresh two-element arg array\r\n * on every call showed up as ~2-4% self time + non-trivial GC pressure in\r\n * the 300-pull video-load-test profile.\r\n *\r\n * The sender itself is a per-Hub singleton and `_send`/format.serializeMessage\r\n * are fully synchronous (no awaits, no re-entrancy on a single-threaded event\r\n * loop), so we reuse a small pool of preallocated envelope + arg-array slots\r\n * per hot method. Mutate fields just before the send call; the encoder reads\r\n * them synchronously and produces a detached copy, after which the slot is\r\n * free to be mutated again on the next call.\r\n */\r\nexport class RpcSystemCallSender {\r\n // Reusable envelopes — one per hot-path method.\r\n private readonly _itemEnv: RpcMessage = {\r\n Method: RpcSystemCalls.item,\r\n RelatedId: 0,\r\n };\r\n private readonly _batchEnv: RpcMessage = {\r\n Method: RpcSystemCalls.batch,\r\n RelatedId: 0,\r\n };\r\n private readonly _endEnv: RpcMessage = {\r\n Method: RpcSystemCalls.end,\r\n RelatedId: 0,\r\n };\r\n private readonly _ackEnv: RpcMessage = {\r\n Method: RpcSystemCalls.ack,\r\n RelatedId: 0,\r\n };\r\n private readonly _ackEndEnv: RpcMessage = {\r\n Method: RpcSystemCalls.ackEnd,\r\n RelatedId: 0,\r\n };\r\n\r\n // Reusable arg arrays.\r\n private readonly _itemArgs: [number, unknown] = [0, null];\r\n private readonly _batchArgs: [number, unknown] = [0, null];\r\n private readonly _endArgs: [number, { TypeRef: string; Message: string }] =\r\n [0, { TypeRef: '', Message: '' }];\r\n private readonly _ackArgs: [number, string] = [0, ''];\r\n private readonly _ackEndArgs: [string] = [''];\r\n\r\n private static readonly NO_ERROR: { TypeRef: string; Message: string } =\r\n Object.freeze({ TypeRef: '', Message: '' }) as unknown as {\r\n TypeRef: string;\r\n Message: string;\r\n };\r\n\r\n registry?: RpcMethodRegistry;\r\n\r\n private _send(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n envelope: RpcMessage,\r\n args?: unknown[]\r\n ): void {\r\n const wireData = format.serializeMessage(\r\n envelope,\r\n args,\r\n conn.encoder,\r\n this.registry\r\n );\r\n if (typeof wireData === 'string') {\r\n conn.send(wireData);\r\n } else {\r\n conn.sendBinary(wireData);\r\n }\r\n }\r\n\r\n handshake(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n peerId: string,\r\n hubId: string,\r\n index: number\r\n ): void {\r\n // RpcHandshake uses [MessagePackObject] array mode for binary.\r\n const handshakeArg = format.isBinary\r\n ? [peerId, null, hubId, 2, index]\r\n : {\r\n RemotePeerId: peerId,\r\n RemoteApiVersionSet: null,\r\n RemoteHubId: hubId,\r\n ProtocolVersion: 2,\r\n Index: index,\r\n };\r\n this._send(conn, format, { Method: RpcSystemCalls.handshake }, [\r\n handshakeArg,\r\n ]);\r\n }\r\n\r\n ok(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number,\r\n result: unknown\r\n ): void {\r\n try {\r\n this._send(\r\n conn,\r\n format,\r\n { Method: RpcSystemCalls.ok, RelatedId: relatedId },\r\n [result]\r\n );\r\n }\r\n catch (e) {\r\n // Mirrors RpcSystemCallSender.cs:104 — \"Failed to send Ok response\".\r\n errorLog?.log(`Failed to send Ok response for call #${relatedId}`, e);\r\n this.error(conn, format, relatedId, e);\r\n }\r\n }\r\n\r\n error(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number,\r\n error: unknown\r\n ): void {\r\n const message = error instanceof Error ? error.message : String(error);\r\n this._send(\r\n conn,\r\n format,\r\n { Method: RpcSystemCalls.error, RelatedId: relatedId },\r\n [{ Message: message }]\r\n );\r\n }\r\n\r\n cancel(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n relatedId: number\r\n ): void {\r\n this._send(conn, format, {\r\n Method: RpcSystemCalls.cancel,\r\n RelatedId: relatedId,\r\n });\r\n }\r\n\r\n keepAlive(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n activeCallIds: number[]\r\n ): void {\r\n this._send(conn, format, { Method: RpcSystemCalls.keepAlive }, [\r\n activeCallIds,\r\n ]);\r\n }\r\n\r\n item(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n item: unknown\r\n ): void {\r\n this._itemEnv.RelatedId = localId;\r\n this._itemArgs[0] = index;\r\n this._itemArgs[1] = item;\r\n this._send(conn, format, this._itemEnv, this._itemArgs);\r\n this._itemArgs[1] = null;\r\n }\r\n\r\n batch(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n items: unknown[]\r\n ): void {\r\n this._batchEnv.RelatedId = localId;\r\n this._batchArgs[0] = index;\r\n this._batchArgs[1] = items;\r\n this._send(conn, format, this._batchEnv, this._batchArgs);\r\n this._batchArgs[1] = null;\r\n }\r\n\r\n end(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n index: number,\r\n error: {\r\n TypeRef: string;\r\n Message: string;\r\n } = RpcSystemCallSender.NO_ERROR\r\n ): void {\r\n this._endEnv.RelatedId = localId;\r\n this._endArgs[0] = index;\r\n this._endArgs[1] = error;\r\n this._send(conn, format, this._endEnv, this._endArgs);\r\n }\r\n\r\n ack(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n nextIndex: number,\r\n hostId: string\r\n ): void {\r\n this._ackEnv.RelatedId = localId;\r\n this._ackArgs[0] = nextIndex;\r\n this._ackArgs[1] = hostId;\r\n this._send(conn, format, this._ackEnv, this._ackArgs);\r\n }\r\n\r\n ackEnd(\r\n conn: RpcConnection,\r\n format: RpcSerializationFormat,\r\n localId: number,\r\n hostId: string\r\n ): void {\r\n this._ackEndEnv.RelatedId = localId;\r\n this._ackEndArgs[0] = hostId;\r\n this._send(conn, format, this._ackEndEnv, this._ackEndArgs);\r\n }\r\n}\r\n","import type { IRpcObject } from './rpc-object.js';\r\n\r\nexport class RpcRemoteObjectTracker {\r\n private _objects = new Map<number, IRpcObject>();\r\n\r\n register(obj: IRpcObject): void {\r\n this._objects.set(obj.id.localId, obj);\r\n }\r\n\r\n get(localId: number): IRpcObject | undefined {\r\n return this._objects.get(localId);\r\n }\r\n\r\n keys(): IterableIterator<number> {\r\n return this._objects.keys();\r\n }\r\n\r\n unregister(obj: IRpcObject): void {\r\n this._objects.delete(obj.id.localId);\r\n }\r\n\r\n disconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n obj.disconnect();\r\n }\r\n this._objects.clear();\r\n }\r\n\r\n reconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n if (obj.allowReconnect) obj.reconnect();\r\n else obj.disconnect();\r\n }\r\n }\r\n}\r\n","import type { IRpcObject } from './rpc-object.js';\r\n\r\nexport class RpcSharedObjectTracker {\r\n private _nextId = 1;\r\n private _objects = new Map<number, IRpcObject>();\r\n\r\n nextId(): number {\r\n return this._nextId++;\r\n }\r\n\r\n register(obj: IRpcObject): void {\r\n this._objects.set(obj.id.localId, obj);\r\n }\r\n\r\n get(localId: number): IRpcObject | undefined {\r\n return this._objects.get(localId);\r\n }\r\n\r\n keys(): IterableIterator<number> {\r\n return this._objects.keys();\r\n }\r\n\r\n unregister(obj: IRpcObject): void {\r\n this._objects.delete(obj.id.localId);\r\n }\r\n\r\n disconnectAll(): void {\r\n for (const obj of this._objects.values()) {\r\n obj.disconnect();\r\n }\r\n this._objects.clear();\r\n }\r\n}\r\n","// .NET counterparts:\r\n// RpcPeer (529 lines) — abstract WorkerBase that runs a reconnection loop in\r\n// OnRun(). Manages AsyncState<RpcPeerConnectionState> (a linked list of\r\n// immutable connection snapshots), handshake exchange, peerChanged detection,\r\n// message processing loop, maintenance tasks, Reset(), and SetConnectionState()\r\n// with thread-safe locking.\r\n// RpcClientPeer (56 lines) — calls GetConnection → WebSocket transport.\r\n// RpcServerPeer (54 lines) — receives connection via RpcServerConnectionHandler.\r\n// RpcPeerConnectionKind (48 lines) — None/Remote/Loopback/Local enum.\r\n// RpcPeerConnectionState (42 lines) — immutable record: Connection, Transport,\r\n// Handshake, OwnHandshake, ReaderTokenSource, Error, TryIndex.\r\n// RpcPeerChangeKind (11 lines) — Unchanged/ChangedToVeryFirst/Changed enum.\r\n// RpcHandshake (28 lines) — PeerId, ApiVersionSet, HubId, ProtocolVersion,\r\n// Index. Has GetPeerChangeKind(lastHandshake) comparison method.\r\n// RpcClientPeerReconnectDelayer (35 lines) — exponential backoff profiles:\r\n// test (50ms), server (250ms), client (1s) with 1.5x growth, max 10s.\r\n//\r\n// Ported from .NET:\r\n// - Handshake exchange — run() sends $sys.Handshake on connect, waits for the\r\n// server's response. RpcServerPeer._onHandshakeReceived() auto-replies.\r\n// Like .NET's OnRun() handshake sequence.\r\n// - Peer change detection — run() compares RemoteHubId from successive\r\n// handshakes to detect server restarts, triggering peerChanged + inbound.clear().\r\n// Like .NET's RpcHandshake.GetPeerChangeKind().\r\n// - Outbound call cancellation via AbortSignal — call(signal) registers an\r\n// abort listener that rejects the call, removes it from the tracker, and sends\r\n// $sys.Cancel to the server. Like .NET's CancellationHandler + NotifyCancelled().\r\n//\r\n// Omitted from .NET:\r\n// - AsyncState<RpcPeerConnectionState> — immutable linked-list of connection\r\n// states that consumers can await via WhenConnected/WhenDisconnected. TS uses\r\n// simpler EventHandlerSet events (connected/disconnected). AsyncState enables\r\n// callers to block until a specific state transition, which is essential in\r\n// .NET's multi-threaded world but unnecessary in TS's single-threaded model.\r\n// - RpcPeerConnectionState record — snapshots Connection + Handshake + OwnHandshake\r\n// + ReaderTokenSource + Error + TryIndex. TS tracks these as mutable fields\r\n// on the peer directly.\r\n// - Maintenance tasks (SharedObjects.Maintain, RemoteObjects.Maintain,\r\n// OutboundCalls.Maintain) — background loops that run while connected,\r\n// checking timeouts and managing object lifetimes. TS has simpler keep-alive\r\n// timer; no shared/remote object tracking, no call timeout monitoring.\r\n// - Reconnect protocol (OutboundCalls.Reconnect) — stage-based call resumption\r\n// after reconnection. TS uses transparent reconnect: outbound calls survive\r\n// disconnect, are re-sent on reconnect (stage-3 compute calls self-invalidate\r\n// on peer change instead of being re-sent).\r\n// - peerChangedToken / CancellationTokenSource — .NET threads a cancellation\r\n// token through all inbound calls that gets cancelled when the remote peer\r\n// changes identity. TS clears the inbound tracker on peer change;\r\n// outbound calls survive disconnect and are re-sent on reconnect.\r\n// - Reset() — aborts RemoteObjects, SharedObjects, OutboundCalls, and clears\r\n// InboundCalls. TS clears inbound tracker on peer change; outbound calls\r\n// are never rejected on disconnect (only on close/stop).\r\n// - ServerMethodResolver / GetServerMethodResolver(handshake) — resolves method\r\n// defs using the remote peer's API version set + legacy names. TS has no\r\n// versioning.\r\n// - ProcessMessage() — creates RpcInboundContext per message, dispatches via\r\n// MethodDef.InboundCallInvoker. TS dispatches via _handleMessage →\r\n// handleSystemCall or RpcServiceHost.dispatch.\r\n// - RpcPeerRef / routing — .NET peers are keyed by RpcPeerRef (which encodes\r\n// client/server, route state, versions, serialization format). TS peers are\r\n// keyed by string ref (URL for clients, \"server://{uuid}\" for servers).\r\n// - StopMode / ComputeAutoStopMode — controls behavior of inbound calls when\r\n// peer stops (cancel vs keep-incomplete). TS has no stop mode.\r\n// - ConnectionKind detector (Remote/Loopback/Local/None) — .NET detects if peer\r\n// is in-process. TS is always remote.\r\n// - Versions / VersionSet / API version negotiation — .NET exchanges version\r\n// sets during handshake for backward compatibility. TS has no versioning.\r\n// - Diagnostics (CallLogger, CallLogLevel, DebugLog) — per-peer logging/tracing.\r\n// - Per-call timeout monitoring (OutboundCallTracker.Maintain) — .NET checks\r\n// elapsed time against per-method timeouts. TS relies on WebSocket-level\r\n// disconnect + rejectAll.\r\n// - Inbound call cancellation propagation — when $sys.Cancel is received, .NET\r\n// cancels the inbound call's CancellationTokenSource, aborting the running\r\n// service method. TS removes the call from the inbound tracker but does not\r\n// yet propagate cancellation to the service handler (would require AbortSignal\r\n// threading through RpcServiceHost.dispatch).\r\n\r\nimport { EventHandlerSet, PromiseSource, TimeoutError, withTimeout } from '@actuallab/core';\r\nimport { getLogs } from './logging.js';\r\nimport {\r\n RpcWebSocketConnection,\r\n type RpcConnection,\r\n type RpcReceivedMessage,\r\n type WebSocketLike,\r\n} from './rpc-connection.js';\r\nimport {\r\n RpcOutboundCall,\r\n RpcOutboundCallTracker,\r\n RpcInboundCall,\r\n RpcInboundCallTracker,\r\n} from './rpc-call-tracker.js';\r\nimport { RpcSystemCalls, type RpcMessage } from './rpc-message.js';\r\nimport { deserializeMessage } from './rpc-serialization.js';\r\nimport type { RpcHub } from './rpc-hub.js';\r\nimport { RpcRemoteObjectTracker } from './rpc-remote-object-tracker.js';\r\nimport { RpcSharedObjectTracker } from './rpc-shared-object-tracker.js';\r\nimport { RpcStream } from './rpc-stream.js';\r\nimport { RpcSerializationFormat, RpcSerializationFormatResolver } from './rpc-serialization-format.js';\r\nimport { IncreasingSeqCompressor } from './increasing-seq-compressor.js';\r\nimport { base64Decode, base64Encode } from './base64.js';\r\n\r\n// Yields to the event loop. Used by the reconnect-resend loop to chunk\r\n// large bursts so they don't spike main-thread time post-handshake.\r\n// setTimeout(0) is a macrotask (queueMicrotask would not yield to other\r\n// tasks); 0 ms still gets bumped to 4 ms by the browser for nested\r\n// setTimeouts but the slack is fine — we want a yield, not zero latency.\r\nfunction yieldToEventLoop(): Promise<void> {\r\n return new Promise<void>(resolve => setTimeout(resolve, 0));\r\n}\r\n\r\n/** WebSocket close code sent by the server when the client's serialization format is unsupported. */\r\n\r\nexport const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;\r\n\r\n// Connect / handshake / keepalive timing limits now live on `RpcLimits`,\r\n// resolved via `hub.limits` at peer construction. See `rpc-limits.ts` for\r\n// the override paths (process-wide / per-hub / per-peer).\r\n\r\nconst { debugLog, infoLog, warnLog, errorLog } = getLogs('RpcPeer');\r\n\r\n/** Builds the WebSocket connection URL for an RpcClientPeer. */\r\nexport type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string | Promise<string>;\r\n\r\n/** Default connection URL provider — sets `clientId` and `f` query parameters,\r\n * replacing any existing values to keep the wire format consistent with the\r\n * peer's actual `serializationFormat`. */\r\nexport const defaultConnectionUrlResolver: RpcConnectionUrlResolver = peer => {\r\n const formatKey = peer.serializationFormat.key;\r\n try {\r\n const url = new URL(peer.ref);\r\n url.searchParams.set('clientId', peer.clientId);\r\n url.searchParams.set('f', formatKey);\r\n return url.toString();\r\n } catch {\r\n // Fallback for non-URL refs (tests/harnesses that pass \"ws://test\" etc.)\r\n const sep = peer.ref.includes('?') ? '&' : '?';\r\n return peer.ref + sep + `clientId=${peer.clientId}&f=${formatKey}`;\r\n }\r\n};\r\n\r\n// eslint-disable-next-line prefer-const -- intentionally `let` so consumers can swap the implementation\r\nexport let sanitizeUrl = (url: string): string => {\r\n try {\r\n const parsed = new URL(url);\r\n if (!parsed.searchParams.has('session'))\r\n return url;\r\n parsed.searchParams.set('session', '<redacted>');\r\n return parsed.toString();\r\n } catch {\r\n return url.replace(/([?&]session=)[^&]*/i, '$1<redacted>');\r\n }\r\n};\r\n\r\n/** Extract the serialization format key from an `f=` query parameter, if any. */\r\nfunction parseFormatFromUrl(url: string): string | undefined {\r\n try {\r\n const f = new URL(url).searchParams.get('f');\r\n return f ?? undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Options for RpcPeer.call() — allows custom call types and cancellation. */\r\nexport interface RpcCallOptions {\r\n /** Wire CallType field (0 = regular). */\r\n callTypeId?: number;\r\n /** Factory for creating custom outbound call instances (e.g. compute calls). */\r\n outboundCallFactory?: (id: number, method: string) => RpcOutboundCall;\r\n /** AbortSignal for caller-initiated cancellation. */\r\n signal?: AbortSignal;\r\n /** Bitfield of RpcRemoteExecutionMode flags (default: 7 = Default). */\r\n remoteExecutionMode?: number;\r\n}\r\n\r\n/** Data extracted from an inbound $sys.Handshake message. */\r\nexport interface RemoteHandshake {\r\n RemotePeerId?: string;\r\n RemoteHubId?: string;\r\n ProtocolVersion?: number;\r\n Index?: number;\r\n}\r\n\r\n/** Base class for RPC peers — handles bidirectional message dispatch. */\r\nexport abstract class RpcPeer {\r\n readonly hub: RpcHub;\r\n /** Routing/addressing key — used as key in hub.peers (URL for clients, \"server://{uuid}\" for servers). */\r\n readonly ref: string;\r\n /** Auto-generated GUID — used in handshakes only. */\r\n readonly id: string = crypto.randomUUID();\r\n readonly outboundCalls = new RpcOutboundCallTracker();\r\n readonly inboundCalls = new RpcInboundCallTracker();\r\n readonly remoteObjects = new RpcRemoteObjectTracker();\r\n readonly sharedObjects = new RpcSharedObjectTracker();\r\n\r\n /** Fired on every state transition with the new value. Initial state is\r\n * `Disconnected`; `Connecting` is entered when the peer starts an\r\n * attempt (the reconnect delay is part of this phase); `Connected` is\r\n * entered only after the handshake has succeeded. */\r\n readonly connectionStateChanged = new EventHandlerSet<RpcConnectionState>();\r\n\r\n protected _connection: RpcConnection | undefined;\r\n /** Mirror of `_connectionState === Connected`. True only after the\r\n * handshake round-trip completes; outbound `call()` checks this to\r\n * decide between sending now and deferring until the next `Connected`\r\n * transition. Kept as its own field so it can stay false through the\r\n * Handshaking phase without exposing the run loop's `_connection`\r\n * bookkeeping. */\r\n protected _isConnected = false;\r\n protected _connectionState: RpcConnectionState = RpcConnectionState.Disconnected;\r\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\r\n private _keepAliveWatchdog: ReturnType<typeof setTimeout> | undefined;\r\n /** Wall-clock time of the last inbound `$sys.KeepAlive`, or 0 if none yet\r\n * for the current connection. Useful for diagnostics. */\r\n private _lastKeepAliveAt = 0;\r\n /** Outbound keep-alive send period. Initialized from `hub.limits` at\r\n * construction; set directly to override on a single peer. */\r\n keepAlivePeriodMs: number;\r\n /** Inbound keep-alive silence tolerance. Initialized from `hub.limits`\r\n * at construction; set directly to override on a single peer. */\r\n keepAliveTimeoutMs: number;\r\n\r\n protected constructor(hub: RpcHub, ref: string) {\r\n this.hub = hub;\r\n this.ref = ref;\r\n this.keepAlivePeriodMs = hub.limits.keepAlivePeriodMs;\r\n this.keepAliveTimeoutMs = hub.limits.keepAliveTimeoutMs;\r\n }\r\n\r\n get connection(): RpcConnection | undefined {\r\n return this._connection;\r\n }\r\n\r\n get connectionState(): RpcConnectionState {\r\n return this._connectionState;\r\n }\r\n\r\n get isConnected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n protected _setConnectionState(value: RpcConnectionState): void {\r\n if (this._connectionState === value) return;\r\n const previousState = this._connectionState;\r\n this._connectionState = value;\r\n // `_isConnected` flips on entering `Connected`, and off only when\r\n // leaving `Connected`. Failed-handshake transitions (Connecting →\r\n // Disconnected) leave it alone — it was already false in production,\r\n // and tests that set it directly via `connectWith` keep it true while\r\n // the run loop spins through retry iterations without ever reaching\r\n // `Connected`.\r\n if (value === RpcConnectionState.Connected) {\r\n this._isConnected = true;\r\n this._armKeepAliveWatchdog();\r\n } else if (value === RpcConnectionState.Disconnected) {\r\n if (previousState === RpcConnectionState.Connected)\r\n this._isConnected = false;\r\n this._disarmKeepAliveWatchdog();\r\n }\r\n this.connectionStateChanged.trigger(value);\r\n }\r\n\r\n /** Resolves on the next transition into `Connected` — or immediately if\r\n * already in that state. */\r\n whenConnected(): Promise<void> {\r\n if (this._connectionState === RpcConnectionState.Connected)\r\n return Promise.resolve();\r\n return new Promise<void>(resolve => {\r\n const handler = (state: RpcConnectionState): void => {\r\n if (state !== RpcConnectionState.Connected) return;\r\n this.connectionStateChanged.remove(handler);\r\n resolve();\r\n };\r\n this.connectionStateChanged.add(handler);\r\n });\r\n }\r\n\r\n protected setupConnection(conn: RpcConnection): void {\r\n this._connection = conn;\r\n\r\n conn.messageReceived.add(raw => this._handleMessage(raw));\r\n conn.closed.add(ev => {\r\n // Bail if this conn has already been replaced (e.g. another\r\n // setupConnection() call ran in between). Without this guard,\r\n // a late-firing close event from the old socket would clobber\r\n // the live `_connection` field and force the state machine\r\n // back to Disconnected mid-session.\r\n if (this._connection !== conn) return;\r\n this._connection = undefined;\r\n // A live conn going away always drops us out of \"connected\",\r\n // regardless of where the state machine thought we were. The\r\n // `_setConnectionState(Disconnected)` below only flips\r\n // `_isConnected` off if we were coming from `Connected`, which\r\n // misses the case where the conn closes mid-handshake (state\r\n // still `Connecting`) or was set up via `connectWith` (state\r\n // still `Disconnected`).\r\n this._isConnected = false;\r\n // Mirrors RpcPeer.cs:524-526 — log disconnect with reason if present.\r\n if (ev.reason)\r\n infoLog?.log(`'${this.ref}': Disconnected: ${ev.reason} (code=${ev.code})`);\r\n else\r\n infoLog?.log(`'${this.ref}': Disconnected`);\r\n // Flipping state to Disconnected tears down the keep-alive watchdog.\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n });\r\n\r\n void conn.whenConnected.then(() => {\r\n // The state transition to `Connected` happens after the handshake —\r\n // that's where keep-alive arming happens too.\r\n infoLog?.log(`'${this.ref}': WS open`);\r\n });\r\n }\r\n\r\n /**\r\n * The serialization format used by this peer for outgoing messages.\r\n * If not set explicitly, defaults to the resolver's default (JSON /\r\n * json5np). Subclasses can pre-assign in their constructor.\r\n */\r\n private _serializationFormat: RpcSerializationFormat | undefined;\r\n\r\n get serializationFormat(): RpcSerializationFormat {\r\n return this._serializationFormat ?? RpcSerializationFormatResolver.Default.defaultFormat;\r\n }\r\n\r\n set serializationFormat(value: RpcSerializationFormat) {\r\n this._serializationFormat = value;\r\n }\r\n\r\n call(\r\n method: string,\r\n args?: unknown[],\r\n options?: RpcCallOptions\r\n ): RpcOutboundCall {\r\n const callId = this.outboundCalls.nextId();\r\n const outboundCall = options?.outboundCallFactory\r\n ? options.outboundCallFactory(callId, method)\r\n : new RpcOutboundCall(callId, method, options?.remoteExecutionMode ?? 7);\r\n\r\n const envelope: RpcMessage = {\r\n Method: method,\r\n RelatedId: callId,\r\n CallType: options?.callTypeId ?? 0,\r\n };\r\n outboundCall.serializedWireData = this.serializationFormat.serializeMessage(\r\n envelope,\r\n args,\r\n this._connection?.encoder,\r\n this.hub.registry\r\n );\r\n\r\n // Mirror .NET `RpcOutboundCall.SendAsync`: register the call up front,\r\n // then either send now (fast path) or wait for `Connected` and send.\r\n // No central \"pending\" queue — each call self-manages its wait.\r\n this.outboundCalls.register(outboundCall);\r\n if (this._isConnected) {\r\n this._sendWireData(outboundCall.serializedWireData);\r\n } else {\r\n // Defer: send when the peer transitions to `Connected`. If the\r\n // call's result is completed before that (cancel, peer close,\r\n // error), the registered handler skips the send.\r\n const stateHandler = (state: RpcConnectionState): void => {\r\n if (state !== RpcConnectionState.Connected) return;\r\n this.connectionStateChanged.remove(stateHandler);\r\n if (outboundCall.result.isCompleted) return;\r\n this._sendWireData(outboundCall.serializedWireData);\r\n };\r\n this.connectionStateChanged.add(stateHandler);\r\n // Detach the handler if the call is completed before we ever\r\n // reach `Connected` — prevents leaking the listener.\r\n outboundCall.result\r\n .then(() => this.connectionStateChanged.remove(stateHandler))\r\n .catch(() => this.connectionStateChanged.remove(stateHandler));\r\n }\r\n\r\n // Wire up caller-initiated cancellation → sends $sys.Cancel to remote peer\r\n const signal = options?.signal;\r\n if (signal !== undefined) {\r\n const onAbort = () => {\r\n if (this.outboundCalls.remove(callId) !== undefined) {\r\n outboundCall.result.reject(new Error('Call cancelled.'));\r\n outboundCall.onDisconnect();\r\n if (this._connection !== undefined)\r\n this.hub.systemCallSender.cancel(this._connection, this.serializationFormat, callId);\r\n }\r\n };\r\n signal.addEventListener('abort', onAbort, { once: true });\r\n // Clean up listener when the call completes normally\r\n outboundCall.result\r\n .then(() => signal.removeEventListener('abort', onAbort))\r\n .catch(() => signal.removeEventListener('abort', onAbort));\r\n }\r\n\r\n return outboundCall;\r\n }\r\n\r\n callNoWait(method: string, args?: unknown[]): void {\r\n if (!this._isConnected) return; // silently drop\r\n // Invariant: `_isConnected` is only true while `_connection` is set\r\n // (they both flip together in `_setConnectionState` / `conn.closed`).\r\n const conn = this._connection!;\r\n const envelope: RpcMessage = { Method: method, RelatedId: 0 };\r\n const wireData = this.serializationFormat.serializeMessage(\r\n envelope,\r\n args,\r\n conn.encoder,\r\n this.hub.registry\r\n );\r\n this._sendWireData(wireData);\r\n }\r\n\r\n /** Close the current WS connection without disposing the peer. The\r\n * `conn.closed` handler flips state to `Disconnected`; for\r\n * `RpcClientPeer` the run loop then iterates and reopens. The peer\r\n * stays in the hub and all client proxies bound to it remain valid. */\r\n disconnect(): void {\r\n this._connection?.close();\r\n }\r\n\r\n close(): void {\r\n // Mirrors RpcPeer.cs:397 — \"Stopping\".\r\n infoLog?.log(`'${this.ref}': Stopping`);\r\n // `_setConnectionState(Disconnected)` below will tear down the keep-alive\r\n // watchdog + send timer. Calls deferred via `whenConnected` self-detach\r\n // when their result rejects (see `call()`).\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n this.outboundCalls.rejectAll(new Error('Peer closed.'));\r\n this.outboundCalls.invalidateAll();\r\n this._connection?.close();\r\n this.hub.peers.delete(this.ref);\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n // Mirrors RpcPeer.cs:439 — \"Stopped\".\r\n infoLog?.log(`'${this.ref}': Stopped`);\r\n }\r\n\r\n /** Send pre-serialized wire data through the current connection. */\r\n protected _sendWireData(data: string | Uint8Array): void {\r\n if (this._connection === undefined) return;\r\n if (typeof data === 'string') {\r\n this._connection.send(data);\r\n } else {\r\n this._connection.sendBinary(data);\r\n }\r\n }\r\n\r\n /** Override in subclasses to handle the remote peer's handshake response. */\r\n protected _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Default: no-op. RpcClientPeer resolves a pending promise;\r\n // RpcServerPeer sends its own handshake back.\r\n }\r\n\r\n private _handleMessage(received: RpcReceivedMessage): void {\r\n let message: RpcMessage;\r\n let args: unknown[];\r\n try {\r\n if (received.kind === 'binary') {\r\n message = received.message;\r\n args = received.args;\r\n } else {\r\n const parsed = deserializeMessage(received.raw);\r\n message = parsed.message;\r\n args = parsed.args;\r\n }\r\n }\r\n catch (e) {\r\n // Mirrors RpcPeer.cs:453 — \"Failed to process inbound message\".\r\n errorLog?.log(`'${this.ref}': Failed to process inbound message:`, received, e);\r\n return;\r\n }\r\n const method = message.Method ?? '';\r\n debugLog?.log(`'${this.ref}': <- ${method}#${message.RelatedId ?? 0}`);\r\n\r\n // Handshake — dispatch to subclass handler\r\n if (method === RpcSystemCalls.handshake) {\r\n const raw = args[0];\r\n if (raw !== undefined) {\r\n // .NET's RpcHandshake is [MessagePackObject(Key)] — serialized\r\n // as a 5-element array in msgpack. In JSON it's an object with\r\n // named properties. Normalize both shapes here.\r\n const handshake = Array.isArray(raw)\r\n ? {\r\n RemotePeerId: raw[0] as string | undefined,\r\n RemoteHubId: raw[2] as string | undefined,\r\n ProtocolVersion: raw[3] as number | undefined,\r\n Index: raw[4] as number | undefined,\r\n }\r\n : raw as RemoteHandshake;\r\n this._onHandshakeReceived(handshake);\r\n }\r\n return;\r\n }\r\n\r\n // Other system calls — delegate to hub's system call handler\r\n if (method.startsWith('$sys')) {\r\n this.hub.systemCallHandler.handle(message, args, this);\r\n return;\r\n }\r\n\r\n // Regular inbound call — dispatch to service host\r\n this._handleInbound(message, args);\r\n }\r\n\r\n private _handleInbound(message: RpcMessage, args: unknown[]): void {\r\n const method = message.Method ?? '';\r\n const relatedId = message.RelatedId ?? 0;\r\n\r\n const call = new RpcInboundCall(relatedId, method, args);\r\n const methodDef = this.hub.serviceHost.getMethodDef(method);\r\n\r\n // For noWait calls: don't register in tracker, don't send response\r\n const isNoWait = methodDef?.noWait === true;\r\n if (!isNoWait) {\r\n this.inboundCalls.register(call);\r\n }\r\n\r\n // Dispatch to the hub's service host\r\n const serviceHost = this.hub.serviceHost;\r\n void (async () => {\r\n try {\r\n const context =\r\n this._connection !== undefined\r\n ? {\r\n __rpcDispatch: true as const,\r\n callId: relatedId,\r\n connection: this._connection,\r\n }\r\n : undefined;\r\n const result = await serviceHost.dispatch(\r\n method,\r\n args,\r\n context\r\n );\r\n if (\r\n methodDef?.stream === true &&\r\n this._connection !== undefined\r\n ) {\r\n // Stream method — wrap result in RpcStream if needed,\r\n // then let toRef() create the sender, register it, and start pumping.\r\n const stream = result instanceof RpcStream\r\n ? result as RpcStream<unknown>\r\n : new RpcStream<unknown>(result as AsyncIterable<unknown>);\r\n this.hub.systemCallSender.ok(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n stream.toRef(this)\r\n );\r\n } else if (!isNoWait && this._connection !== undefined) {\r\n this.hub.systemCallSender.ok(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n result\r\n );\r\n }\r\n } catch (e) {\r\n if (!isNoWait && this._connection !== undefined) {\r\n this.hub.systemCallSender.error(\r\n this._connection,\r\n this.serializationFormat,\r\n relatedId,\r\n e\r\n );\r\n }\r\n debugLog?.log(`'${this.ref}': Inbound call failed: ${method}#${relatedId}`, e);\r\n } finally {\r\n if (!isNoWait) {\r\n this.inboundCalls.remove(relatedId);\r\n }\r\n }\r\n })();\r\n }\r\n\r\n /** Start sending `$sys.KeepAlive` pings and arm the silence watchdog.\r\n * Called when the peer transitions into `Connected` (after handshake).\r\n * Each inbound `$sys.KeepAlive` re-arms the watchdog; if it fires, the\r\n * WebSocket is force-closed so the normal disconnect path runs\r\n * (`conn.closed` → `connectionState = Disconnected` → UI).\r\n * Mirrors .NET `RpcObjectTrackers.Maintain`'s `KeepAliveTimeout` branch. */\r\n private _armKeepAliveWatchdog(): void {\r\n // Start the send timer once per connected session.\r\n this._keepAliveTimer ??= setInterval(() => {\r\n if (this._connection !== undefined) {\r\n // Send remote object IDs so the server's SharedObjectTracker keeps them alive.\r\n // Must NOT send outbound call IDs — those are a different ID namespace and would\r\n // cause the server to send $sys.Disconnect for IDs it doesn't recognize, which\r\n // the client may misinterpret as a disconnect of its own shared objects (e.g.\r\n // RpcStreamSender) when the IDs collide numerically.\r\n this.hub.systemCallSender.keepAlive(this._connection, this.serializationFormat, [\r\n ...this.remoteObjects.keys(),\r\n ]);\r\n }\r\n }, this.keepAlivePeriodMs);\r\n // (Re)arm the silence watchdog.\r\n if (this._keepAliveWatchdog !== undefined)\r\n clearTimeout(this._keepAliveWatchdog);\r\n this._keepAliveWatchdog = setTimeout(() => {\r\n this._keepAliveWatchdog = undefined;\r\n if (this._connection === undefined) return;\r\n warnLog?.log(\r\n `'${this.ref}': no keep-alive for ${this.keepAliveTimeoutMs}ms — force-closing connection`);\r\n this._connection.close();\r\n }, this.keepAliveTimeoutMs);\r\n }\r\n\r\n /** Stop sending keep-alives and clear the silence watchdog. Called when\r\n * the peer transitions into `Disconnected`. */\r\n private _disarmKeepAliveWatchdog(): void {\r\n if (this._keepAliveTimer !== undefined) {\r\n clearInterval(this._keepAliveTimer);\r\n this._keepAliveTimer = undefined;\r\n }\r\n if (this._keepAliveWatchdog !== undefined) {\r\n clearTimeout(this._keepAliveWatchdog);\r\n this._keepAliveWatchdog = undefined;\r\n }\r\n }\r\n\r\n /** Called by `RpcSystemCallHandler` when a `$sys.KeepAlive` message\r\n * arrives — records the timestamp and re-arms the silence watchdog. */\r\n notifyKeepAliveReceived(): void {\r\n this._lastKeepAliveAt = Date.now();\r\n if (this._keepAliveWatchdog !== undefined)\r\n this._armKeepAliveWatchdog();\r\n }\r\n}\r\n\r\n/** Connection state for RpcPeer. Mirrors .NET `RpcPeerConnectionState`'s\r\n * observable phases: the transport is `Connecting` until the WebSocket\r\n * opens, `Handshaking` from WS-open until the server's handshake reply\r\n * arrives, and `Connected` once the handshake completes. Outbound calls\r\n * are gated on `Connected`. */\r\nexport const enum RpcConnectionState {\r\n Disconnected = 0,\r\n Connecting = 1,\r\n Handshaking = 2,\r\n Connected = 3,\r\n}\r\n\r\n/** Client-side RPC peer — initiates WebSocket connection. ref = URL. */\r\nexport class RpcClientPeer extends RpcPeer {\r\n private _disposed = false;\r\n private _tryIndex = 0;\r\n private _handshakeIndex = 0;\r\n /** Index from the MOST RECENTLY RECEIVED server handshake — sent back\r\n * to the server in `$sys.Reconnect:3` so the server can verify we're\r\n * talking about the current connection. Mirrors .NET's\r\n * `connectionState.Handshake.Index` path in\r\n * `RpcOutboundCallTracker.TryReconnect`. */\r\n private _remoteHandshakeIndex = 0;\r\n private _lastRemoteHubId: string | undefined;\r\n private _pendingHandshake: PromiseSource<RemoteHandshake> | undefined;\r\n private _reconnectsAt = 0;\r\n\r\n /** Base64url-encoded peer ID — matches .NET's RpcClientPeer.ClientId (Guid.ToBase64Url). */\r\n readonly clientId: string;\r\n /** Builds the WebSocket URL for this peer. Replace to customize URL construction. */\r\n connectionUrlResolver: RpcConnectionUrlResolver =\r\n defaultConnectionUrlResolver;\r\n /** Optional WebSocket factory. When set, `run()` uses this instead of the\r\n * global `new WebSocket(url)` constructor. Used by tests (to inject mock\r\n * sockets) and by the Node load-test harness (to attach `Session` /\r\n * `Cookie` headers to the WS upgrade, which the browser `WebSocket`\r\n * constructor can't do). Must be set before `start()`. */\r\n webSocketFactory: ((url: string) => WebSocketLike) | undefined;\r\n /** Max time to wait for the WebSocket to reach OPEN before aborting the\r\n * connection attempt. Initialized from `hub.limits` at construction;\r\n * set directly to override on a single peer. */\r\n connectTimeoutMs: number;\r\n /** Max time to wait for the server's handshake response before aborting\r\n * the connection attempt. Initialized from `hub.limits` at construction;\r\n * set directly to override on a single peer. */\r\n handshakeTimeoutMs: number;\r\n readonly peerChanged = new EventHandlerSet<void>();\r\n /** Fired when the server rejects the connection due to an unsupported serialization format (close code 4010). */\r\n readonly unsupportedFormat = new EventHandlerSet<{ reason: string }>();\r\n readonly reconnectsAtChanged = new EventHandlerSet<void>();\r\n\r\n get reconnectsAt(): number {\r\n return this._reconnectsAt;\r\n }\r\n\r\n private _setReconnectsAt(value: number): void {\r\n if (this._reconnectsAt === value) return;\r\n this._reconnectsAt = value;\r\n this.reconnectsAtChanged.trigger();\r\n }\r\n\r\n /** The promise returned by the reconnect loop. `undefined` until\r\n * `start()` is invoked (which happens automatically when\r\n * `mustStart = true` is passed to the ctor). */\r\n whenRunning: Promise<void> | undefined;\r\n\r\n /**\r\n * @param hub RpcHub instance this peer will be tracked by\r\n * @param url Full WebSocket URL — the serialization format is taken from\r\n * the `f=` query parameter (e.g. `wss://host/rpc/ws?f=msgpack6`);\r\n * falls back to the resolver's default when absent. Use\r\n * {@link RpcPeerRefBuilder.forClient} to compose URLs with a format.\r\n * @param mustStart Kick off the reconnect loop automatically at the end of\r\n * construction (default `true`). Pass `false` if you need to\r\n * tweak `reconnectDelayer`, `handshakeTimeoutMs`, or similar\r\n * options before the loop starts — then call `peer.start()`\r\n * manually.\r\n */\r\n constructor(hub: RpcHub, url: string, mustStart = true) {\r\n super(hub, url);\r\n this.connectTimeoutMs = hub.limits.connectTimeoutMs;\r\n this.handshakeTimeoutMs = hub.limits.handshakeTimeoutMs;\r\n this.clientId = guidToBase64Url(this.id);\r\n const resolver = RpcSerializationFormatResolver.Default;\r\n this.serializationFormat = resolver.get(parseFormatFromUrl(url) ?? resolver.defaultFormatKey);\r\n if (mustStart)\r\n this.start();\r\n }\r\n\r\n /** One-shot connection for tests — no reconnection loop, no handshake.\r\n * @param conn Rpc connection\r\n * @param isPeerChanged Whether to treat this as a peer-identity change\r\n * (default `true` — no handshake was exchanged, so stage-3 compute calls\r\n * self-invalidate and remote/shared objects are disposed). Pass `false`\r\n * to simulate a same-peer reconnect in tests. */\r\n connectWith(conn: RpcConnection, isPeerChanged = true): void {\r\n this.setupConnection(conn);\r\n // Test-only: synthesize the \"ready for calls\" state without running\r\n // the handshake. Stays off the public state machine on purpose —\r\n // tests don't drive the run loop, and entering `Connected` via\r\n // `_setConnectionState` would arm the keep-alive watchdog against a\r\n // mock transport that never replies to keep-alives. We still need\r\n // to release any calls deferred via `whenConnected`, so we trigger\r\n // the listener set directly.\r\n this._isConnected = true;\r\n this.connectionStateChanged.trigger(RpcConnectionState.Connected);\r\n void this._reconnect(isPeerChanged);\r\n }\r\n\r\n /** Kick off the reconnect loop. Idempotent — subsequent calls are\r\n * no-ops; await `peer.whenRunning` if you need the loop's completion\r\n * promise. Customize `peer.webSocketFactory` first if you need a\r\n * non-default WebSocket constructor. */\r\n start(): void {\r\n void this.run();\r\n }\r\n\r\n /** The reconnect loop itself. Exposed as `protected` so subclasses (and\r\n * `start()`) can drive it; public callers should use {@link start}. */\r\n protected run(): Promise<void> {\r\n return this.whenRunning ??= this._runImpl();\r\n }\r\n\r\n private async _runImpl(): Promise<void> {\r\n if (this._disposed) return;\r\n // Mirrors RpcPeer.cs:230 — \"Started\".\r\n infoLog?.log(`'${this.ref}': Started (Client)`);\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n while (!this._disposed) {\r\n // Enter Connecting for this iteration — the reconnect delay\r\n // below (if any) is part of this state per the contract\r\n // \"connecting when the peer starts to reconnect\".\r\n this._setConnectionState(RpcConnectionState.Connecting);\r\n\r\n // Apply retry delay (no-op on first iteration where tryIndex === 0).\r\n // The ApiReconnectDelayer may park indefinitely here while\r\n // `Api.canConnect === false`.\r\n const retryDelay = this.hub.reconnectDelayer.getDelay(this._tryIndex);\r\n if (retryDelay.isLimitExceeded) {\r\n warnLog?.log(`'${this.ref}': Reconnect limit exceeded - shutting down`);\r\n this._disposed = true;\r\n break;\r\n }\r\n this._setReconnectsAt(retryDelay.endsAt);\r\n try {\r\n await retryDelay.promise;\r\n } finally {\r\n this._setReconnectsAt(0);\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n if (this._disposed) break;\r\n\r\n let lastCloseCode = 0;\r\n let lastCloseReason = '';\r\n try {\r\n const connUrl = await this.connectionUrlResolver(this);\r\n // Mirrors RpcClientPeer.cs:45 — \"Connecting...\".\r\n infoLog?.log(`'${this.ref}': Connecting to ${sanitizeUrl(connUrl)}`);\r\n const ws =\r\n this.webSocketFactory?.(connUrl) ??\r\n (new WebSocket(connUrl) as unknown as WebSocketLike);\r\n const conn = new RpcWebSocketConnection(\r\n ws,\r\n this.serializationFormat.isBinary,\r\n this.serializationFormat,\r\n this.hub.registry\r\n );\r\n\r\n // Eager \"connection closed\" PromiseSource — captured right\r\n // after connection creation so every subsequent await can\r\n // race against it without missing an already-fired close.\r\n // EventHandlerSet fires synchronously and does not replay\r\n // past triggers, so a late `conn.closed.add(...)` would\r\n // deadlock if the socket closed just before registration.\r\n const whenClosed = new PromiseSource<{ code: number; reason: string }>();\r\n const closedController = new AbortController();\r\n conn.closed.add(ev => {\r\n lastCloseCode = ev.code;\r\n lastCloseReason = ev.reason;\r\n whenClosed.resolve(ev);\r\n closedController.abort();\r\n });\r\n\r\n // Derived rejecting promise — used to unblock awaits that\r\n // expect a successful signal (whenConnected, handshake).\r\n const closedRejection = whenClosed.then<never>(() => {\r\n throw new Error('Connection failed');\r\n });\r\n closedRejection.catch(() => { /* noop — prevent unhandled rejection when conn closes normally */ });\r\n\r\n // Create a fresh handshake promise before setupConnection (which registers\r\n // the message handler). The handler can only fire after the WS opens,\r\n // and we send our handshake below, so timing is safe.\r\n this._pendingHandshake = new PromiseSource<RemoteHandshake>();\r\n this.setupConnection(conn);\r\n // `_connection` stays set throughout the handshake window so\r\n // `close()` can tear down the WS. Outbound calls are gated by\r\n // `_isConnected`, which only flips on when we enter `Connected`\r\n // below — before that, `call()` defers the send via the\r\n // per-call `whenConnected` listener.\r\n\r\n // Race connection open against close — if WS fails to connect,\r\n // whenConnected stays pending forever, so we must also watch for close.\r\n // The connect timeout caps how long we wait for the OPEN state: on a\r\n // hung connect (mobile after network change, half-open after sleep)\r\n // the browser may take ~2 min to emit onerror/onclose, which blocks\r\n // the reconnect loop and surfaces as \"can't reconnect\" to the user.\r\n const connectTimeoutMs = this.connectTimeoutMs;\r\n try {\r\n await withTimeout(\r\n Promise.race([conn.whenConnected, closedRejection]),\r\n connectTimeoutMs,\r\n 'Connect timeout');\r\n } catch (e) {\r\n if (e instanceof TimeoutError) {\r\n warnLog?.log(`'${this.ref}': Connect timed out after ${connectTimeoutMs}ms`);\r\n // Force-close the WS so the run loop iterates to retry-delay\r\n // instead of leaking a half-open socket. The browser onclose\r\n // may still take a moment but the loop no longer waits for it.\r\n conn.close();\r\n }\r\n throw e;\r\n }\r\n\r\n // WS open, handshake exchange about to start. Mirrors\r\n // .NET's `Handshaking` phase of `RpcPeerConnectionState`.\r\n this._setConnectionState(RpcConnectionState.Handshaking);\r\n\r\n // Send our handshake, then wait for the server's response.\r\n try {\r\n this.hub.systemCallSender.handshake(\r\n conn,\r\n this.serializationFormat,\r\n this.id,\r\n this.hub.hubId,\r\n ++this._handshakeIndex\r\n );\r\n }\r\n catch (e) {\r\n // Mirrors RpcPeer.cs:305 — \"Failed to send handshake\".\r\n warnLog?.log(`'${this.ref}': Failed to send handshake`, e);\r\n throw e;\r\n }\r\n\r\n // Handshake timeout — mirrors .NET's `Hub.Limits.HandshakeTimeout`.\r\n // Without this, an accepted WS that never replies to $sys.Handshake\r\n // (half-open TCP, mid-restart server holding the socket briefly)\r\n // would block the run loop indefinitely, preventing reconnect.\r\n const handshakeTimeoutMs = this.handshakeTimeoutMs;\r\n let remoteHandshake: RemoteHandshake;\r\n try {\r\n remoteHandshake = await withTimeout(\r\n Promise.race([this._pendingHandshake, closedRejection]),\r\n handshakeTimeoutMs,\r\n 'Handshake timeout');\r\n } catch (e) {\r\n if (e instanceof TimeoutError) {\r\n warnLog?.log(`'${this.ref}': Handshake timed out after ${handshakeTimeoutMs}ms`);\r\n // Force-close the socket so the run loop continues to\r\n // the retry-delay branch instead of leaking the WS.\r\n conn.close();\r\n }\r\n throw e;\r\n } finally {\r\n this._pendingHandshake = undefined;\r\n }\r\n\r\n // Record the server's handshake index for $sys.Reconnect.\r\n this._remoteHandshakeIndex = remoteHandshake.Index ?? 0;\r\n\r\n // Peer change detection (like .NET's RpcHandshake.GetPeerChangeKind)\r\n const remoteHubId = remoteHandshake.RemoteHubId;\r\n let isPeerChanged = false;\r\n if (remoteHubId !== undefined) {\r\n isPeerChanged =\r\n this._lastRemoteHubId !== undefined &&\r\n this._lastRemoteHubId !== remoteHubId;\r\n if (isPeerChanged) {\r\n // Server identity changed — clear inbound state\r\n this.inboundCalls.clear();\r\n // Mirrors RpcPeer.cs:439 (Peer changed branch).\r\n infoLog?.log(`'${this.ref}': Peer changed`);\r\n this.peerChanged.trigger();\r\n }\r\n this._lastRemoteHubId = remoteHubId;\r\n }\r\n // Mirrors RpcPeer.cs:316 — \"Handshake succeeded\".\r\n infoLog?.log(`'${this.ref}': Handshake succeeded, peerChanged=${isPeerChanged}`);\r\n\r\n // Activate the connection and re-send outbound calls.\r\n // All of this happens AFTER the handshake, so the server is ready to process calls.\r\n this._setConnectionState(RpcConnectionState.Connected);\r\n this._tryIndex = 0;\r\n // Pass the close signal so `_reconcileReconnect`'s inner\r\n // `$sys.Reconnect` call cancels cleanly if the socket dies\r\n // mid-reconcile — otherwise its pending result.promise would\r\n // deadlock the outer _reconnect() await.\r\n await this._reconnect(isPeerChanged, closedController.signal);\r\n\r\n // Wait until disconnected — use the eager whenClosed promise\r\n // so we don't miss an already-fired close event.\r\n await whenClosed;\r\n } catch (e) {\r\n // connection failed or handshake failed\r\n debugLog?.log(`'${this.ref}': run() iteration failed:`, e);\r\n }\r\n\r\n // Normally redundant — the `conn.closed` handler in\r\n // `setupConnection` already fired `Disconnected`. Kept for\r\n // paths that throw before setupConnection runs.\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- _disposed can change during await\r\n if (this._disposed) break;\r\n\r\n // Server rejected our serialization format — stop reconnecting and notify listeners\r\n if (lastCloseCode === RPC_CLOSE_CODE_UNSUPPORTED_FORMAT) {\r\n // Mirrors RpcPeer.cs:518 — \"Can't (re)connect, will shut down\".\r\n warnLog?.log(`'${this.ref}': Unsupported serialization format - shutting down`);\r\n this.unsupportedFormat.trigger({ reason: lastCloseReason });\r\n this._disposed = true;\r\n break;\r\n }\r\n\r\n this._tryIndex++;\r\n }\r\n } finally {\r\n this._setConnectionState(RpcConnectionState.Disconnected);\r\n }\r\n }\r\n\r\n /**\r\n * Reconcile outbound calls after reconnection, then flush pending sends.\r\n *\r\n * On peer change (different server hubId) or with binary wire format —\r\n * we blind-resend every eligible call.\r\n *\r\n * On a same-peer reconnect with JSON transport, first call\r\n * `$sys.Reconnect` to ask the server which call IDs it no longer\r\n * recognizes (e.g. inbound tracker was cleared, or the call never\r\n * reached the server). Only those get resent; the rest stay in the\r\n * tracker awaiting their $sys.Ok. This prevents duplicate server-side\r\n * invocation of in-flight streaming calls on every transient\r\n * reconnect — mirrors .NET `RpcOutboundCallTracker.Reconnect` at\r\n * src/ActualLab.Rpc/Infrastructure/RpcCallTrackers.cs:209-279.\r\n *\r\n * Stage-3 compute calls (result already resolved, awaiting invalidation)\r\n * are always self-invalidated — TS doesn't track compute-call stages\r\n * across reconnect, so the safest behavior is to force a fresh compute.\r\n */\r\n private async _reconnect(isPeerChanged: boolean, closedSignal?: AbortSignal): Promise<void> {\r\n if (this._connection === undefined) return;\r\n\r\n // Handle remote and shared objects on reconnect.\r\n // On peer change, the remote server is gone — both remote objects\r\n // (proxies registered here for server-owned objects) and shared objects\r\n // (e.g. RpcStreamSender instances this peer owns and is pushing to the\r\n // server) must be disposed: the new server has no corresponding entries,\r\n // so they would hang forever waiting for ACKs that never arrive.\r\n // Mirrors .NET RpcPeer.Reset (src/ActualLab.Rpc/RpcPeer.cs:430-440).\r\n if (isPeerChanged) {\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n } else {\r\n this.remoteObjects.reconnectAll();\r\n }\r\n\r\n // Filter calls by RemoteExecutionMode (same logic as before).\r\n const eligible: RpcOutboundCall[] = [];\r\n for (const call of [...this.outboundCalls.values()]) {\r\n const mode = call.remoteExecutionMode;\r\n if (!(mode & 2)) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\r\n 'Outbound call failed: disconnected and AllowReconnect is not set.'));\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n if (isPeerChanged && !(mode & 4)) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\r\n 'Outbound call failed: reconnected to a different peer and AllowResend is not set.'));\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n eligible.push(call);\r\n }\r\n\r\n // Reconcile with the server on same-peer reconnects. On peer change\r\n // we skip the round-trip (the new peer knows nothing) and blind-resend.\r\n //\r\n // Wire format: the `completedStages` dict uses plain JS-object shape\r\n // with string keys and base64-encoded `Uint8Array` values. This is\r\n // JSON-compatible with .NET's `System.Text.Json` serialization of\r\n // `Dictionary<int, byte[]>` and also works for TS-to-TS msgpack\r\n // (serializes as map<str, str>). Full .NET-msgpack interop for\r\n // $sys.Reconnect — which uses `map<int, bin>` — is a future\r\n // enhancement; the JSON path covers ActualChat's primary use case.\r\n let unknownIds: Set<number> | null = null;\r\n if (!isPeerChanged && eligible.length > 0) {\r\n unknownIds = await this._reconcileReconnect(eligible, closedSignal);\r\n }\r\n\r\n // Yield-batched resend. Eligible can be hundreds after a long\r\n // disconnect; chunking prevents a sync spike on the main thread.\r\n const YIELD_BATCH = 20;\r\n let resentSinceYield = 0;\r\n for (const call of eligible) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call: always self-invalidate to force a\r\n // fresh recompute — TS has no cross-reconnect compute-state\r\n // tracking (unlike .NET's Reliable reconnection).\r\n call.onDisconnect();\r\n this.outboundCalls.remove(call.callId);\r\n continue;\r\n }\r\n if (unknownIds !== null && !unknownIds.has(call.callId)) {\r\n // Server reports this call is still in flight on its side —\r\n // skip the resend. The pending result promise continues to\r\n // await the original call's $sys.Ok.\r\n continue;\r\n }\r\n this._sendWireData(call.serializedWireData);\r\n resentSinceYield++;\r\n if (resentSinceYield >= YIELD_BATCH) {\r\n resentSinceYield = 0;\r\n await yieldToEventLoop();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Ask the server which call IDs in `eligible` it no longer recognizes\r\n * via `$sys.Reconnect:3`. Returns the set of call IDs that need to be\r\n * resent. On any failure, falls back to \"resend everything\".\r\n *\r\n * Wire format of the `completedStages` argument is chosen per wire format:\r\n * - JSON (json5np): plain object `{\"<stage>\": \"<base64-of-bytes>\"}` —\r\n * matches .NET `System.Text.Json`'s default for `Dictionary<int, byte[]>`.\r\n * - MessagePack (msgpack6/msgpack6c): `Map<number, Uint8Array>` —\r\n * the patched encoder (see `./msgpack-map-patch.ts`) writes this as a\r\n * native msgpack `map<int, bin>`, matching .NET MessagePack-CSharp's\r\n * default for `Dictionary<int, byte[]>`.\r\n */\r\n private async _reconcileReconnect(eligible: RpcOutboundCall[], closedSignal?: AbortSignal): Promise<Set<number>> {\r\n try {\r\n // Group by completedStage, sort IDs, compress per-stage.\r\n const byStage = new Map<number, number[]>();\r\n for (const call of eligible) {\r\n const stage = call.getReconnectStage(false);\r\n if (stage === null) continue;\r\n let arr = byStage.get(stage);\r\n if (!arr) { arr = []; byStage.set(stage, arr); }\r\n arr.push(call.callId);\r\n }\r\n if (byStage.size === 0) {\r\n // Nothing to reconcile — fall back to resend-all.\r\n return new Set(eligible.map(c => c.callId));\r\n }\r\n\r\n const isBinary = this.serializationFormat.isBinary;\r\n const stagesArg: Map<number, Uint8Array> | Record<string, string> = isBinary\r\n ? new Map<number, Uint8Array>()\r\n : {};\r\n for (const [stage, ids] of byStage) {\r\n ids.sort((a, b) => a - b);\r\n const bytes = IncreasingSeqCompressor.serialize(ids);\r\n if (isBinary) {\r\n (stagesArg as Map<number, Uint8Array>).set(stage, bytes);\r\n } else {\r\n (stagesArg as Record<string, string>)[String(stage)] = base64Encode(bytes);\r\n }\r\n }\r\n\r\n // Send $sys.Reconnect via the regular call path so the server's\r\n // $sys.Ok reply resolves a PromiseSource we can await. The\r\n // first arg is the server's OWN handshake index (captured from\r\n // the remote handshake we just received), not ours — this\r\n // matches .NET's protocol, where the server validates\r\n // `ownHandshake.Index == handshakeIndex`.\r\n const reconnectCall = this.call(\r\n RpcSystemCalls.reconnect,\r\n [this._remoteHandshakeIndex, stagesArg],\r\n {\r\n remoteExecutionMode: 1, /* AwaitForConnection only; must not itself reconnect */\r\n signal: closedSignal,\r\n },\r\n );\r\n const result = await reconnectCall.result;\r\n const bytes = _toBytes(result);\r\n if (bytes === null) return new Set(eligible.map(c => c.callId));\r\n return new Set(IncreasingSeqCompressor.deserialize(bytes));\r\n } catch {\r\n // Reconciliation failed — resend everything.\r\n return new Set(eligible.map(c => c.callId));\r\n }\r\n }\r\n\r\n protected override _onHandshakeReceived(handshake: RemoteHandshake): void {\r\n this._pendingHandshake?.resolve(handshake);\r\n }\r\n\r\n override close(): void {\r\n this._disposed = true;\r\n super.close();\r\n }\r\n}\r\n\r\n/** Server-side RPC peer — wraps an accepted connection. ref = \"server://{uuid}\". */\r\nexport class RpcServerPeer extends RpcPeer {\r\n // Needed to widen the base `protected constructor` to public.\r\n // eslint-disable-next-line @typescript-eslint/no-useless-constructor\r\n constructor(hub: RpcHub, ref: string) {\r\n super(hub, ref);\r\n }\r\n\r\n /** Accept an incoming connection — sets up message handling and handshake response. */\r\n accept(conn: RpcConnection): void {\r\n this._setConnectionState(RpcConnectionState.Connecting);\r\n this.setupConnection(conn);\r\n // Server starts in Handshaking immediately on accept — there's no\r\n // separate \"WS open\" phase since the WS is already up by the time\r\n // we accept. Stay in Handshaking until the client's handshake\r\n // arrives in `_onHandshakeReceived` below.\r\n this._setConnectionState(RpcConnectionState.Handshaking);\r\n // When the client connection drops, clean up shared objects (stream senders).\r\n // Server peers don't reconnect — once the connection is gone, senders must be\r\n // terminated so their source iterators release resources promptly.\r\n conn.closed.add(() => {\r\n // Bail if this conn has already been replaced by a subsequent accept().\r\n // Without this guard, a late-firing close from the old socket would\r\n // disconnect shared objects that belong to the live session.\r\n if (this._connection !== conn && this._connection !== undefined) return;\r\n this.sharedObjects.disconnectAll();\r\n });\r\n }\r\n\r\n protected override _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Client sent its handshake → respond with our own and transition to Connected.\r\n if (this._connection !== undefined) {\r\n this.hub.systemCallSender.handshake(\r\n this._connection,\r\n this.serializationFormat,\r\n this.id,\r\n this.hub.hubId,\r\n 0\r\n );\r\n this._setConnectionState(RpcConnectionState.Connected);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Convert a UUID string to base64url — matches .NET's Guid.ToBase64Url().\r\n * .NET Guid stores the first 3 groups in little-endian byte order.\r\n */\r\nfunction guidToBase64Url(uuid: string): string {\r\n const hex = uuid.replace(/-/g, '');\r\n const bytes = new Uint8Array(16);\r\n\r\n // Group 1 (bytes 0-3): little-endian\r\n bytes[0] = parseInt(hex.slice(6, 8), 16);\r\n bytes[1] = parseInt(hex.slice(4, 6), 16);\r\n bytes[2] = parseInt(hex.slice(2, 4), 16);\r\n bytes[3] = parseInt(hex.slice(0, 2), 16);\r\n // Group 2 (bytes 4-5): little-endian\r\n bytes[4] = parseInt(hex.slice(10, 12), 16);\r\n bytes[5] = parseInt(hex.slice(8, 10), 16);\r\n // Group 3 (bytes 6-7): little-endian\r\n bytes[6] = parseInt(hex.slice(14, 16), 16);\r\n bytes[7] = parseInt(hex.slice(12, 14), 16);\r\n // Groups 4-5 (bytes 8-15): big-endian\r\n for (let i = 8; i < 16; i++)\r\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\r\n\r\n const binary = String.fromCharCode(...bytes);\r\n return btoa(binary)\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=+$/, '');\r\n}\r\n\r\n/** Coerce a `$sys.Ok` result carrying a byte[] into a Uint8Array. */\r\nfunction _toBytes(value: unknown): Uint8Array | null {\r\n if (value instanceof Uint8Array) return value;\r\n if (typeof value === 'string') {\r\n try { return base64Decode(value); } catch { return null; }\r\n }\r\n return null;\r\n}\r\n","// RPC serialization format definitions — mirrors .NET RpcSerializationFormat.\r\n//\r\n// Each format knows how to serialize/deserialize RPC messages on the wire.\r\n// Format instances are immutable singletons — state like method registries\r\n// is passed in via parameters, not held by the format.\r\n\r\nimport { Encoder, Decoder } from '@msgpack/msgpack';\r\nimport type { RpcMessage } from './rpc-message.js';\r\nimport {\r\n serializeMessage,\r\n deserializeMessage,\r\n serializeBinaryMessage,\r\n splitBinaryFrame,\r\n serializeBinaryFrame,\r\n createBinaryEncoder,\r\n serializeCompactBinaryMessage,\r\n splitCompactBinaryFrame,\r\n} from './rpc-serialization.js';\r\nimport type { RpcMethodRegistry } from './rpc-method-registry.js';\r\n\r\n/** Parsed inbound message — result of deserialization. */\r\nexport interface RpcDeserializedMessage {\r\n message: RpcMessage;\r\n args: unknown[];\r\n}\r\n\r\n/** Wire data produced by serialization — either text or binary. */\r\nexport type RpcWireData = string | Uint8Array;\r\n\r\n/**\r\n * Base class for RPC serialization formats.\r\n * Instances are immutable singletons — one per format key.\r\n * State (like method registries) is passed via parameters.\r\n */\r\nexport abstract class RpcSerializationFormat {\r\n abstract readonly key: string;\r\n abstract readonly isBinary: boolean;\r\n /** Whether this format uses compact method hashes (needs RpcMethodRegistry). */\r\n abstract readonly isCompact: boolean;\r\n\r\n /** Serialize an outbound message. */\r\n abstract serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcWireData;\r\n\r\n /** Deserialize a single text message (only called for text formats). */\r\n abstract deserializeTextMessage(raw: string): RpcDeserializedMessage;\r\n\r\n /** Split + deserialize a binary frame (only called for binary formats). */\r\n abstract splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcDeserializedMessage[];\r\n\r\n /** Concatenate multiple serialized binary messages into one frame. */\r\n abstract serializeBinaryFrame(messages: Uint8Array[]): Uint8Array;\r\n\r\n /** Create a reusable binary encoder for this format. Returns undefined for text formats. */\r\n createEncoder(): Encoder | undefined {\r\n return undefined;\r\n }\r\n\r\n /** Create a reusable binary decoder for this format. Returns undefined for text formats. */\r\n createDecoder(): Decoder | undefined {\r\n return undefined;\r\n }\r\n\r\n // Named format instances — initialized in initFormats() below (after subclasses are defined)\r\n static SystemJsonV5: RpcSerializationFormat;\r\n static SystemJsonV5NP: RpcSerializationFormat;\r\n static NewtonsoftJsonV5: RpcSerializationFormat;\r\n static NewtonsoftJsonV5NP: RpcSerializationFormat;\r\n static MessagePackV6: RpcSerializationFormat;\r\n static MemoryPackV6: RpcSerializationFormat;\r\n static MessagePackV6C: RpcSerializationFormat;\r\n static MemoryPackV6C: RpcSerializationFormat;\r\n static All: readonly RpcSerializationFormat[];\r\n\r\n /** Convenience shortcut for RpcSerializationFormatResolver.Default.get(key). */\r\n static get(key: string): RpcSerializationFormat {\r\n return RpcSerializationFormatResolver.Default.get(key);\r\n }\r\n\r\n /** Convenience shortcut for RpcSerializationFormatResolver.Default.tryGet(key). */\r\n static tryGet(key: string): RpcSerializationFormat | undefined {\r\n return RpcSerializationFormatResolver.Default.tryGet(key);\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Concrete formats (immutable singletons)\r\n// ============================================================\r\n\r\n/**\r\n * JSON text format — \"json5np\" (System.Text.Json, no polymorphism).\r\n */\r\nexport class RpcJsonSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = false;\r\n readonly isCompact = false;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(message: RpcMessage, args?: unknown[]): string {\r\n return serializeMessage(message, args);\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(): RpcDeserializedMessage[] {\r\n throw new Error('Text format does not support binary frames');\r\n }\r\n\r\n serializeBinaryFrame(): Uint8Array {\r\n throw new Error('Text format does not support binary frames');\r\n }\r\n}\r\n\r\n/**\r\n * MessagePack binary format — \"msgpack6\" / \"mempack6\" (V5 wire format).\r\n */\r\nexport class RpcMessagePackSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = true;\r\n readonly isCompact = false;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder\r\n ): Uint8Array {\r\n return serializeBinaryMessage(message, args, encoder);\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder\r\n ): RpcDeserializedMessage[] {\r\n return splitBinaryFrame(frame, decoder);\r\n }\r\n\r\n serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return serializeBinaryFrame(messages);\r\n }\r\n\r\n override createEncoder(): Encoder {\r\n return createBinaryEncoder();\r\n }\r\n\r\n override createDecoder(): Decoder {\r\n return new Decoder();\r\n }\r\n}\r\n\r\n/**\r\n * MessagePack compact binary format — \"msgpack6c\" / \"mempack6c\" (V5Compact).\r\n * Uses 4-byte method hash instead of full method name.\r\n * Registry is passed via parameters — format instance is immutable.\r\n */\r\nexport class RpcMessagePackCompactSerializationFormat extends RpcSerializationFormat {\r\n readonly key: string;\r\n readonly isBinary = true;\r\n readonly isCompact = true;\r\n\r\n constructor(key: string) {\r\n super();\r\n this.key = key;\r\n }\r\n\r\n serializeMessage(\r\n message: RpcMessage,\r\n args?: unknown[],\r\n encoder?: Encoder,\r\n registry?: RpcMethodRegistry\r\n ): Uint8Array {\r\n return serializeCompactBinaryMessage(\r\n message,\r\n args,\r\n registry,\r\n encoder\r\n );\r\n }\r\n\r\n deserializeTextMessage(raw: string): RpcDeserializedMessage {\r\n return deserializeMessage(raw);\r\n }\r\n\r\n splitBinaryFrame(\r\n frame: Uint8Array,\r\n decoder?: Decoder,\r\n registry?: RpcMethodRegistry\r\n ): RpcDeserializedMessage[] {\r\n return splitCompactBinaryFrame(frame, registry, decoder);\r\n }\r\n\r\n serializeBinaryFrame(messages: Uint8Array[]): Uint8Array {\r\n return serializeBinaryFrame(messages);\r\n }\r\n\r\n override createEncoder(): Encoder {\r\n return createBinaryEncoder();\r\n }\r\n\r\n override createDecoder(): Decoder {\r\n return new Decoder();\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Initialize named format instances (after subclasses are defined)\r\n// ============================================================\r\n\r\n// Text: System.Text.Json\r\nRpcSerializationFormat.SystemJsonV5 = new RpcJsonSerializationFormat('json5');\r\nRpcSerializationFormat.SystemJsonV5NP = new RpcJsonSerializationFormat('json5np');\r\n// Text: Newtonsoft.Json\r\nRpcSerializationFormat.NewtonsoftJsonV5 = new RpcJsonSerializationFormat('njson5');\r\nRpcSerializationFormat.NewtonsoftJsonV5NP = new RpcJsonSerializationFormat('njson5np');\r\n// Binary: MessagePack (non-compact)\r\nRpcSerializationFormat.MessagePackV6 = new RpcMessagePackSerializationFormat('msgpack6');\r\nRpcSerializationFormat.MemoryPackV6 = new RpcMessagePackSerializationFormat('mempack6');\r\n// Binary: MessagePack (compact)\r\nRpcSerializationFormat.MessagePackV6C = new RpcMessagePackCompactSerializationFormat('msgpack6c');\r\nRpcSerializationFormat.MemoryPackV6C = new RpcMessagePackCompactSerializationFormat('mempack6c');\r\n\r\nRpcSerializationFormat.All = [\r\n RpcSerializationFormat.SystemJsonV5,\r\n RpcSerializationFormat.SystemJsonV5NP,\r\n RpcSerializationFormat.NewtonsoftJsonV5,\r\n RpcSerializationFormat.NewtonsoftJsonV5NP,\r\n RpcSerializationFormat.MessagePackV6,\r\n RpcSerializationFormat.MemoryPackV6,\r\n RpcSerializationFormat.MessagePackV6C,\r\n RpcSerializationFormat.MemoryPackV6C,\r\n];\r\n\r\n// ============================================================\r\n// RpcSerializationFormatResolver — mirrors .NET RpcSerializationFormatResolver\r\n// ============================================================\r\n\r\n/**\r\n * Resolves RpcSerializationFormat instances by key, with a configurable default.\r\n * Mirrors .NET RpcSerializationFormatResolver.\r\n */\r\nexport class RpcSerializationFormatResolver {\r\n static DefaultFormats: readonly RpcSerializationFormat[] = RpcSerializationFormat.All;\r\n static Default = new RpcSerializationFormatResolver('json5np');\r\n\r\n readonly defaultFormatKey: string;\r\n readonly formats: ReadonlyMap<string, RpcSerializationFormat>;\r\n\r\n get defaultFormat(): RpcSerializationFormat {\r\n return this.get(this.defaultFormatKey);\r\n }\r\n\r\n constructor(defaultFormatKey: string, formats?: Iterable<RpcSerializationFormat>) {\r\n const map = new Map<string, RpcSerializationFormat>();\r\n for (const f of formats ?? RpcSerializationFormatResolver.DefaultFormats)\r\n map.set(f.key, f);\r\n\r\n if (!map.has(defaultFormatKey))\r\n throw new Error(`No format with key '${defaultFormatKey}'.`);\r\n\r\n this.defaultFormatKey = defaultFormatKey;\r\n this.formats = map;\r\n }\r\n\r\n get(key: string): RpcSerializationFormat {\r\n const format = this.tryGet(key);\r\n if (!format)\r\n throw new Error(`No format with key '${key}'.`);\r\n return format;\r\n }\r\n\r\n tryGet(key: string): RpcSerializationFormat | undefined {\r\n if (!key) key = this.defaultFormatKey;\r\n return this.formats.get(key);\r\n }\r\n}\r\n","/**\n * Configurable timing limits for RPC connection lifecycle. Mirrors .NET's\n * `RpcLimits` (src/ActualLab.Rpc/Configuration/RpcLimits.cs).\n *\n * Override paths, from broadest to narrowest:\n * - **Process-wide**: mutate fields on {@link RpcLimits.Default} (affects\n * every hub that hasn't been given its own instance — hubs default to\n * using the shared `Default`, so mutations propagate live), or replace\n * `RpcLimits.Default = new RpcLimits({ … })` (affects hubs constructed\n * *after* the replacement).\n * - **Per-hub**: assign `hub.limits = new RpcLimits({ … })` before peers\n * are created from that hub.\n * - **Per-peer**: set the matching `*Ms` field on the peer directly\n * (e.g. `peer.keepAliveTimeoutMs = 60_000`) before `start()`.\n *\n * Peers snapshot the relevant values from `hub.limits` at construction —\n * later mutations to `hub.limits` don't retroactively affect already-built\n * peers; that's by design and matches the .NET semantics.\n */\nexport class RpcLimits {\n /** Process-wide default. Hubs read from this when they're constructed\n * unless caller assigns a custom instance to `hub.limits`. */\n static Default: RpcLimits = new RpcLimits();\n\n /** Max time to wait for the WebSocket to enter the OPEN state. On a\n * hung connect (mobile after network change, half-open after sleep)\n * the browser can take ~2 min to emit `onerror`/`onclose`; without\n * this cap the reconnect loop blocks for that whole window. */\n connectTimeoutMs = 10_000;\n\n /** Max time to wait for the server's handshake response after WS opens. */\n handshakeTimeoutMs = 10_000;\n\n /** Outbound `$sys.KeepAlive` send period. */\n keepAlivePeriodMs = 10_000;\n\n /** Reaper threshold — connection is force-closed (and the reconnect\n * loop takes over) if no inbound `$sys.KeepAlive` has been seen for\n * this long. Sized to tolerate a complete server stall plus most of\n * one keepalive cycle: the worst-case age of `_lastKeepAliveAt` is\n * `keepAlivePeriodMs + stall_duration`, so with these defaults the\n * tolerated stall is ~15 s. */\n keepAliveTimeoutMs = 25_000;\n\n constructor(overrides?: Partial<RpcLimits>) {\n if (overrides) Object.assign(this, overrides);\n }\n}\n","// .NET counterpart: ActualLab.Rpc.RpcClientPeerReconnectDelayer\n\nimport { RetryDelaySeq, RetryDelayer } from '@actuallab/core';\n\nexport class RpcClientPeerReconnectDelayer extends RetryDelayer {\n constructor() {\n super();\n // Fixed 0.1s — callers gate *whether* to reconnect via app-level\n // signals (see Api / ApiReconnectDelayer). Exponential backoff would\n // just add latency on a flaky drop once the gate says \"go\".\n this.delays = RetryDelaySeq.fixed(100);\n }\n}\n","// .NET counterparts:\r\n// RpcServiceBase (17 lines) — abstract base class providing DI access (Services,\r\n// Hub, Log). All RPC service implementations inherit from it.\r\n// RpcServiceRegistry — builds the full map of all services and their method defs\r\n// at startup; provides GetServerMethodResolver() for version-aware method\r\n// lookup. Inbound call dispatch is handled by RpcInboundCall.Process() which\r\n// invokes the method via a compiled delegate chain (MethodDef.InboundCallInvoker).\r\n//\r\n// Omitted from .NET:\r\n// - Compiled delegate pipeline — .NET pre-compiles per-method invoker delegates\r\n// (InboundCallServerInvokerFactory<T>, InboundCallMiddlewareInvokerFactory<T>)\r\n// using generic type instantiation. These invokers handle deserialization,\r\n// middleware chain, server method invocation, and result serialization — all\r\n// as a strongly-typed Task<TResult>. TS uses a simple async function dispatch\r\n// via fn(...args) because there's no middleware pipeline and JSON handles any\r\n// type.\r\n// - ServiceResolver (DI-based lazy service resolution) — .NET resolves the\r\n// service implementation from DI on first use. TS receives the impl object\r\n// directly in register().\r\n// - RpcServiceMode (Local/Client/Server/Hybrid) — .NET classifies each service\r\n// registration. TS has no mode; services are either registered (server) or\r\n// proxied (client).\r\n// - Version-aware method resolution (ServerMethodResolver with legacy names) —\r\n// .NET can resolve methods by legacy names for rolling-upgrade compatibility.\r\n// TS has no versioning.\r\n// - RpcInboundContext / AsyncLocal — .NET sets thread-local inbound context\r\n// so system call handlers can access the current peer/message. TS passes\r\n// context explicitly as a function argument (RpcDispatchContext).\r\n\r\nimport { getLogs } from './logging.js';\r\nimport type { RpcMethodDef } from './rpc-service-def.js';\r\nimport { wireMethodName } from './rpc-service-def.js';\r\nimport type { RpcServiceDef } from './rpc-service-def.js';\r\nimport type { RpcConnection } from './rpc-connection.js';\r\n\r\nconst { warnLog } = getLogs('RpcServiceHost');\r\n\r\nexport type RpcServiceImpl = Record<string, ((...args: unknown[]) => unknown) | undefined>;\r\n\r\n/** Context passed to service dispatch — carries callId and connection for compute tracking. */\r\nexport interface RpcDispatchContext {\r\n __rpcDispatch: true;\r\n callId: number;\r\n connection: RpcConnection;\r\n}\r\n\r\n/** Dispatches inbound RPC calls to registered service implementations. */\r\nexport class RpcServiceHost {\r\n private _methods = new Map<\r\n string,\r\n { def: RpcMethodDef; fn: (...args: unknown[]) => unknown }\r\n >();\r\n\r\n register(def: RpcServiceDef, impl: RpcServiceImpl): void {\r\n for (const methodDef of def.methods.values()) {\r\n const fn = impl[methodDef.name];\r\n if (!fn) continue;\r\n this._methods.set(wireMethodName(methodDef), {\r\n def: methodDef,\r\n fn,\r\n });\r\n }\r\n }\r\n\r\n async dispatch(\r\n wireMethod: string,\r\n args: unknown[],\r\n context?: RpcDispatchContext\r\n ): Promise<unknown> {\r\n const entry = this._methods.get(wireMethod);\r\n if (entry === undefined) {\r\n // Mirrors .NET: missing method is sent back as $sys.Error; warn for visibility.\r\n warnLog?.log(`Method not found: ${wireMethod}`);\r\n throw new Error(`Method not found: ${wireMethod}`);\r\n }\r\n\r\n // Pass context as the last arg only for custom call types (e.g., compute) — their\r\n // wrapped functions use it; regular methods should not see it to avoid polluting ...args.\r\n if (context !== undefined && entry.def.callTypeId !== 0) {\r\n return await entry.fn(...args, context);\r\n }\r\n return await entry.fn(...args);\r\n }\r\n\r\n getMethodDef(wireMethod: string): RpcMethodDef | undefined {\r\n return this._methods.get(wireMethod)?.def;\r\n }\r\n}\r\n","// .NET counterpart:\r\n// RpcMethodAttribute — optional attribute that can override a method's wire name.\r\n// Method metadata (arg count, return type, NoWait, stream, etc.) is discovered\r\n// automatically via reflection on the service interface.\r\n// Interface-based metadata — .NET discovers all methods by reflecting on the\r\n// service interface type. NoWait is determined by return type == Task<RpcNoWait>.\r\n// Stream is determined by return type being IAsyncEnumerable<T>.\r\n//\r\n// Omitted from .NET:\r\n// - Reflection-based auto-discovery — .NET scans interface methods, parameter\r\n// types, and return types at startup. TS uses explicit decorator metadata\r\n// because TypeScript erases type information at runtime.\r\n// - RpcMethodAttribute.Name — custom wire name override. TS decorators don't\r\n// support custom wire names (always \"ServiceName.methodName\").\r\n// - RpcServiceAttribute — .NET has no class-level decorator; the service name\r\n// is configured in RpcServiceBuilder via DI. TS uses @rpcService(\"name\")\r\n// class decorator as a convenient alternative to DI configuration.\r\n// - Automatic NoWait detection from return type — .NET checks if return type\r\n// is Task<RpcNoWait>. TS requires explicit { returns: RpcType.noWait }\r\n// because return types are erased.\r\n// - Automatic stream detection from return type — .NET checks for\r\n// IAsyncEnumerable<T>. TS requires explicit { returns: RpcType.stream }.\r\n// - compute flag in method metadata — stored here for FusionHub's\r\n// addServiceFromContract() which reads decorator metadata to build a\r\n// RpcServiceDef. .NET determines this via the service interface hierarchy\r\n// (IComputeService marker interface).\r\n\r\nconst METHODS_META = Symbol.for('actuallab.methods');\r\nconst SERVICE_META = Symbol.for('actuallab.service');\r\n\r\nexport interface MethodMeta {\r\n argCount: number;\r\n returns?: symbol;\r\n /** Bitfield of RpcRemoteExecutionMode flags. */\r\n remoteExecutionMode?: number;\r\n [key: symbol]: unknown;\r\n}\r\n\r\nexport interface ServiceMeta {\r\n name: string;\r\n}\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- constructor type requires any for maximum compatibility\r\nexport type AnyConstructor = abstract new (...args: any[]) => any;\r\n\r\n/** Read method metadata from a decorated class. */\r\nexport function getMethodsMeta(\r\n cls: AnyConstructor,\r\n): Record<string, MethodMeta> | undefined {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- Symbol.metadata is not in standard typings\r\n return (cls as any)[Symbol.metadata]?.[METHODS_META] as Record<string, MethodMeta> | undefined;\r\n}\r\n\r\n/** Read service metadata from a decorated class. */\r\nexport function getServiceMeta(\r\n cls: AnyConstructor,\r\n): ServiceMeta | undefined {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- Symbol.metadata is not in standard typings\r\n return (cls as any)[Symbol.metadata]?.[SERVICE_META] as ServiceMeta | undefined;\r\n}\r\n\r\n/** Class decorator — stores the RPC service wire name. */\r\nexport function rpcService(serviceName: string) {\r\n return function <T extends AnyConstructor>(\r\n _target: T,\r\n context: ClassDecoratorContext<T>,\r\n ): void {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- decorator metadata requires untyped access\r\n const meta: ServiceMeta = ((context.metadata as any)[SERVICE_META] ??=\r\n {} as ServiceMeta);\r\n meta.name = serviceName;\r\n };\r\n}\r\n\r\n/** Method decorator — stores RPC method metadata (argCount, returns, remoteExecutionMode). Does NOT wrap the method. */\r\nexport function rpcMethod(options?: { returns?: symbol; remoteExecutionMode?: number }) {\r\n return function <This, Args extends unknown[], Return>(\r\n target: (this: This, ...args: Args) => Return,\r\n context: ClassMethodDecoratorContext<\r\n This,\r\n (this: This, ...args: Args) => Return\r\n >,\r\n ): (this: This, ...args: Args) => Return {\r\n const methodName = String(context.name);\r\n /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- decorator metadata requires untyped access */\r\n const methods: Record<string, MethodMeta> = ((context.metadata as any)[\r\n METHODS_META\r\n ] ??= {});\r\n /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any */\r\n methods[methodName] = {\r\n ...methods[methodName],\r\n argCount: target.length,\r\n returns: options?.returns,\r\n remoteExecutionMode: options?.remoteExecutionMode,\r\n };\r\n return target; // no wrapping — RPC proxy handles invocation\r\n };\r\n}\r\n","// Pure TypeScript XXH3-64 implementation using BigInt.\r\n// Sync, zero dependencies. Supports inputs of any length.\r\n//\r\n// Reference: https://github.com/Cyan4973/xxHash/blob/dev/xxhash.h\r\n// Verified against .NET System.IO.Hashing.XxHash3.HashToUInt64.\r\n\r\n \r\n\r\nconst U64_MASK = 0xffff_ffff_ffff_ffffn;\r\nconst U32_MASK = 0xffff_ffffn;\r\n\r\nconst PRIME32_1 = 0x9e3779b1;\r\nconst PRIME32_2 = 0x85ebca77;\r\nconst PRIME32_3 = 0xc2b2ae3d;\r\n\r\nconst PRIME64_1 = 0x9e3779b185ebca87n;\r\nconst PRIME64_2 = 0xc2b2ae3d27d4eb4fn;\r\nconst PRIME64_3 = 0x165667b19e3779f9n;\r\nconst PRIME64_4 = 0x85ebca77c2b2ae63n;\r\nconst PRIME64_5 = 0x27d4eb2f165667c5n;\r\n\r\nconst XXH3_SECRET_SIZE_MIN = 136;\r\nconst XXH_SECRET_DEFAULT_SIZE = 192;\r\n\r\nconst STRIPE_LEN = 64;\r\nconst XXH_SECRET_CONSUME_RATE = 8;\r\nconst ACC_NB = STRIPE_LEN / 8;\r\n\r\nconst XXH3_MIDSIZE_MAX = 240;\r\nconst XXH3_MIDSIZE_STARTOFFSET = 3;\r\nconst XXH3_MIDSIZE_LASTOFFSET = 17;\r\nconst XXH_SECRET_LASTACC_START = 7;\r\nconst XXH_SECRET_MERGEACCS_START = 11;\r\n\r\nconst RRMXMX = 0x9fb21c651e98df25n;\r\n\r\n// prettier-ignore\r\nconst DEFAULT_SECRET = new Uint8Array([\r\n 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,\r\n 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,\r\n 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,\r\n 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,\r\n 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,\r\n 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,\r\n 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,\r\n 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,\r\n 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,\r\n 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,\r\n 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,\r\n 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,\r\n]);\r\n\r\nfunction u64(x: bigint): bigint {\r\n return x & U64_MASK;\r\n}\r\n\r\nfunction read32LE(buf: Uint8Array, off: number): number {\r\n return (\r\n (buf[off] |\r\n (buf[off + 1] << 8) |\r\n (buf[off + 2] << 16) |\r\n (buf[off + 3] << 24)) >>>\r\n 0\r\n );\r\n}\r\n\r\nfunction read64LE(buf: Uint8Array, off: number): bigint {\r\n return (\r\n BigInt(buf[off]) |\r\n (BigInt(buf[off + 1]) << 8n) |\r\n (BigInt(buf[off + 2]) << 16n) |\r\n (BigInt(buf[off + 3]) << 24n) |\r\n (BigInt(buf[off + 4]) << 32n) |\r\n (BigInt(buf[off + 5]) << 40n) |\r\n (BigInt(buf[off + 6]) << 48n) |\r\n (BigInt(buf[off + 7]) << 56n)\r\n );\r\n}\r\n\r\nfunction write64LE(buf: Uint8Array, off: number, value: bigint): void {\r\n let x = u64(value);\r\n for (let i = 0; i < 8; i++) {\r\n buf[off + i] = Number(x & 0xffn);\r\n x >>= 8n;\r\n }\r\n}\r\n\r\nfunction swap32(x: number): number {\r\n return (\r\n (((x << 24) & 0xff000000) |\r\n ((x << 8) & 0x00ff0000) |\r\n ((x >>> 8) & 0x0000ff00) |\r\n ((x >>> 24) & 0x000000ff)) >>>\r\n 0\r\n );\r\n}\r\n\r\nfunction swap64(x: bigint): bigint {\r\n x = u64(x);\r\n return (\r\n ((x << 56n) & 0xff00000000000000n) |\r\n ((x << 40n) & 0x00ff000000000000n) |\r\n ((x << 24n) & 0x0000ff0000000000n) |\r\n ((x << 8n) & 0x000000ff00000000n) |\r\n ((x >> 8n) & 0x00000000ff000000n) |\r\n ((x >> 24n) & 0x0000000000ff0000n) |\r\n ((x >> 40n) & 0x000000000000ff00n) |\r\n ((x >> 56n) & 0x00000000000000ffn)\r\n );\r\n}\r\n\r\nfunction rotl64(x: bigint, amount: number): bigint {\r\n const r = BigInt(amount & 63);\r\n return u64((x << r) | (x >> (64n - r)));\r\n}\r\n\r\nfunction mul128Fold64(lhs: bigint, rhs: bigint): bigint {\r\n const product = u64(lhs) * u64(rhs);\r\n return u64(product) ^ u64(product >> 64n);\r\n}\r\n\r\n// XXH64_avalanche — used by len_0 and len_1to3\r\nfunction xxh64Avalanche(hash: bigint): bigint {\r\n let h = u64(hash);\r\n h ^= h >> 33n;\r\n h = u64(h * PRIME64_2);\r\n h ^= h >> 29n;\r\n h = u64(h * PRIME64_3);\r\n h ^= h >> 32n;\r\n return u64(h);\r\n}\r\n\r\n// XXH3_avalanche — used by len_9to16, len_17to128, len_129to240\r\nfunction xxh3Avalanche(hash: bigint): bigint {\r\n let h = u64(hash);\r\n h ^= h >> 37n;\r\n h = u64(h * 0x165667919e3779f9n);\r\n h ^= h >> 32n;\r\n return u64(h);\r\n}\r\n\r\nfunction len0(secret: Uint8Array, seed: bigint): bigint {\r\n const acc = u64(seed ^ read64LE(secret, 56) ^ read64LE(secret, 64));\r\n return xxh64Avalanche(acc);\r\n}\r\n\r\nfunction len1to3(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n const byte1 = input[0];\r\n const byte2 = length > 1 ? input[1] : input[0];\r\n const byte3 = input[length - 1];\r\n const combined =\r\n ((byte1 << 16) | (byte2 << 24) | byte3 | (length << 8)) >>> 0;\r\n const bitflip = u64(\r\n BigInt((read32LE(secret, 0) ^ read32LE(secret, 4)) >>> 0) + seed\r\n );\r\n const value64 = u64(BigInt(combined) ^ bitflip);\r\n return xxh64Avalanche(value64);\r\n}\r\n\r\nfunction len4to8(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n const inputHi = read32LE(input, 0);\r\n const inputLo = read32LE(input, length - 4);\r\n const input64 = BigInt(inputLo) | (BigInt(inputHi) << 32n);\r\n let acc = read64LE(secret, 8) ^ read64LE(secret, 16);\r\n seed ^= BigInt(swap32(Number(seed & U32_MASK))) << 32n;\r\n acc = u64(acc - seed);\r\n acc ^= input64;\r\n acc ^= rotl64(acc, 49) ^ rotl64(acc, 24);\r\n acc = u64(acc * RRMXMX);\r\n acc ^= (acc >> 35n) + BigInt(length);\r\n acc = u64(acc * RRMXMX);\r\n acc ^= acc >> 28n;\r\n return u64(acc);\r\n}\r\n\r\nfunction len9to16(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let inputLo = read64LE(secret, 24) ^ read64LE(secret, 32);\r\n let inputHi = read64LE(secret, 40) ^ read64LE(secret, 48);\r\n let acc = BigInt(length);\r\n inputLo = u64(inputLo + seed);\r\n inputHi = u64(inputHi - seed);\r\n inputLo ^= read64LE(input, 0);\r\n inputHi ^= read64LE(input, length - 8);\r\n acc = u64(acc + swap64(inputLo));\r\n acc = u64(acc + inputHi);\r\n acc = u64(acc + mul128Fold64(inputLo, inputHi));\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction mix16B(\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number,\r\n seed: bigint\r\n): bigint {\r\n let lhs = seed;\r\n let rhs = u64(0n - seed);\r\n lhs = u64(lhs + read64LE(secret, secretOff));\r\n rhs = u64(rhs + read64LE(secret, secretOff + 8));\r\n lhs ^= read64LE(input, inputOff);\r\n rhs ^= read64LE(input, inputOff + 8);\r\n return mul128Fold64(lhs, rhs);\r\n}\r\n\r\nfunction len17to128(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let i = Math.floor((length - 1) / 32);\r\n let acc = u64(BigInt(length) * PRIME64_1);\r\n for (; i >= 0; i--) {\r\n acc = u64(acc + mix16B(input, 16 * i, secret, 32 * i, seed));\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n length - 16 * (i + 1),\r\n secret,\r\n 32 * i + 16,\r\n seed\r\n )\r\n );\r\n }\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction len129to240(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n let acc = u64(BigInt(length) * PRIME64_1);\r\n const nbRounds = Math.floor(length / 16);\r\n for (let i = 0; i < 8; i++) {\r\n acc = u64(acc + mix16B(input, 16 * i, secret, 16 * i, seed));\r\n }\r\n acc = xxh3Avalanche(acc);\r\n for (let i = 8; i < nbRounds; i++) {\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n 16 * i,\r\n secret,\r\n 16 * (i - 8) + XXH3_MIDSIZE_STARTOFFSET,\r\n seed\r\n )\r\n );\r\n }\r\n acc = u64(\r\n acc +\r\n mix16B(\r\n input,\r\n length - 16,\r\n secret,\r\n XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET,\r\n seed\r\n )\r\n );\r\n return xxh3Avalanche(acc);\r\n}\r\n\r\nfunction hashShort(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n seed: bigint\r\n): bigint {\r\n if (length <= 16) {\r\n if (length > 8) return len9to16(input, length, secret, seed);\r\n if (length >= 4) return len4to8(input, length, secret, seed);\r\n if (length !== 0) return len1to3(input, length, secret, seed);\r\n return len0(secret, seed);\r\n }\r\n if (length <= 128) return len17to128(input, length, secret, seed);\r\n return len129to240(input, length, secret, seed);\r\n}\r\n\r\n// --- Long input (> 240 bytes) ---\r\n\r\nfunction accumulate512(\r\n acc: bigint[],\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number\r\n): void {\r\n for (let i = 0; i < ACC_NB; i++) {\r\n const dataVal = read64LE(input, inputOff + 8 * i);\r\n const dataKey = dataVal ^ read64LE(secret, secretOff + 8 * i);\r\n acc[i ^ 1] = u64(acc[i ^ 1] + dataVal);\r\n const low32 = dataKey & U32_MASK;\r\n const high32 = dataKey >> 32n;\r\n acc[i] = u64(acc[i] + low32 * high32);\r\n }\r\n}\r\n\r\nfunction scrambleAcc(\r\n acc: bigint[],\r\n secret: Uint8Array,\r\n secretOff: number\r\n): void {\r\n for (let i = 0; i < ACC_NB; i++) {\r\n let x = acc[i];\r\n x ^= x >> 47n;\r\n x ^= read64LE(secret, secretOff + 8 * i);\r\n x = u64(x * BigInt(PRIME32_1));\r\n acc[i] = x;\r\n }\r\n}\r\n\r\nfunction accumulateLoop(\r\n acc: bigint[],\r\n input: Uint8Array,\r\n inputOff: number,\r\n secret: Uint8Array,\r\n secretOff: number,\r\n nbStripes: number\r\n): void {\r\n for (let n = 0; n < nbStripes; n++) {\r\n accumulate512(\r\n acc,\r\n input,\r\n inputOff + n * STRIPE_LEN,\r\n secret,\r\n secretOff + 8 * n\r\n );\r\n }\r\n}\r\n\r\nfunction mix2Accs(\r\n acc: bigint[],\r\n accOff: number,\r\n secret: Uint8Array,\r\n secretOff: number\r\n): bigint {\r\n return mul128Fold64(\r\n acc[accOff] ^ read64LE(secret, secretOff),\r\n acc[accOff + 1] ^ read64LE(secret, secretOff + 8)\r\n );\r\n}\r\n\r\nfunction mergeAccs(\r\n acc: bigint[],\r\n key: Uint8Array,\r\n keyOff: number,\r\n start: bigint\r\n): bigint {\r\n let result = start;\r\n for (let i = 0; i < 4; i++) {\r\n result = u64(result + mix2Accs(acc, 2 * i, key, keyOff + 16 * i));\r\n }\r\n return xxh3Avalanche(result);\r\n}\r\n\r\nfunction hashLong(\r\n input: Uint8Array,\r\n length: number,\r\n secret: Uint8Array,\r\n secretSize: number\r\n): bigint {\r\n const nbRounds = Math.floor(\r\n (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE\r\n );\r\n const blockLen = STRIPE_LEN * nbRounds;\r\n const nbBlocks = Math.floor((length - 1) / blockLen);\r\n const nbStripes = Math.floor(\r\n ((length - 1) - blockLen * nbBlocks) / STRIPE_LEN\r\n );\r\n\r\n const acc: bigint[] = [\r\n BigInt(PRIME32_3),\r\n PRIME64_1,\r\n PRIME64_2,\r\n PRIME64_3,\r\n PRIME64_4,\r\n BigInt(PRIME32_2),\r\n PRIME64_5,\r\n BigInt(PRIME32_1),\r\n ];\r\n\r\n for (let n = 0; n < nbBlocks; n++) {\r\n accumulateLoop(acc, input, n * blockLen, secret, 0, nbRounds);\r\n scrambleAcc(acc, secret, secretSize - STRIPE_LEN);\r\n }\r\n\r\n accumulateLoop(acc, input, nbBlocks * blockLen, secret, 0, nbStripes);\r\n\r\n // Last stripe — always applied (C reference: unconditional)\r\n accumulate512(\r\n acc,\r\n input,\r\n length - STRIPE_LEN,\r\n secret,\r\n secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START\r\n );\r\n\r\n return mergeAccs(\r\n acc,\r\n secret,\r\n XXH_SECRET_MERGEACCS_START,\r\n u64(BigInt(length) * PRIME64_1)\r\n );\r\n}\r\n\r\nfunction hashLongWithSeed(\r\n input: Uint8Array,\r\n length: number,\r\n seed: bigint\r\n): bigint {\r\n const secret = new Uint8Array(XXH_SECRET_DEFAULT_SIZE);\r\n for (let i = 0; i < XXH_SECRET_DEFAULT_SIZE / 16; i++) {\r\n const off = 16 * i;\r\n write64LE(secret, off, u64(read64LE(DEFAULT_SECRET, off) + seed));\r\n write64LE(\r\n secret,\r\n off + 8,\r\n u64(read64LE(DEFAULT_SECRET, off + 8) - seed)\r\n );\r\n }\r\n return hashLong(input, length, secret, secret.length);\r\n}\r\n\r\n// --- Public API ---\r\n\r\nconst textEncoder = new TextEncoder();\r\n\r\n/**\r\n * Compute XXH3-64 hash of a byte array. Returns a BigInt (unsigned 64-bit).\r\n * Pure TypeScript, synchronous, supports any input length.\r\n */\r\nexport function xxh3_64(input: Uint8Array, seed = 0n): bigint {\r\n const len = input.length;\r\n if (len <= XXH3_MIDSIZE_MAX) {\r\n return hashShort(input, len, DEFAULT_SECRET, u64(seed));\r\n }\r\n return hashLongWithSeed(input, len, u64(seed));\r\n}\r\n\r\n/**\r\n * Compute XXH3-64 hash of a UTF-8 string. Returns a BigInt (unsigned 64-bit).\r\n */\r\nexport function xxh3_64str(input: string, seed = 0n): bigint {\r\n return xxh3_64(textEncoder.encode(input), seed);\r\n}\r\n\r\n/**\r\n * Compute the RPC method hash matching .NET's RpcMethodRef.ComputeHashCode.\r\n * Returns a 32-bit signed integer (lower 32 bits of XXH3-64 of [prefix + utf8Name]).\r\n *\r\n * The prefix is uint32 LE of (67211 * utf8Name.length), which ensures methods\r\n * with different name lengths but similar text produce different hashes.\r\n *\r\n * Computed on demand — caller should cache the result per method name.\r\n */\r\nexport function computeMethodHash(methodName: string): number {\r\n const utf8 = textEncoder.encode(methodName);\r\n // Prefix: uint32 LE of (67211 * utf8.length)\r\n const prefixValue = Math.imul(67211, utf8.length) >>> 0;\r\n const prefixed = new Uint8Array(4 + utf8.length);\r\n prefixed[0] = prefixValue & 0xff;\r\n prefixed[1] = (prefixValue >>> 8) & 0xff;\r\n prefixed[2] = (prefixValue >>> 16) & 0xff;\r\n prefixed[3] = (prefixValue >>> 24) & 0xff;\r\n prefixed.set(utf8, 4);\r\n const hash64 = xxh3_64(prefixed);\r\n // Truncate to signed int32 (same as C#'s unchecked((int)hash64))\r\n return Number(hash64 & 0xffffffffn) | 0;\r\n}\r\n","// Method name ↔ hash registry for compact binary serialization.\r\n//\r\n// .NET counterpart: RpcMethodResolver — maintains MethodByHashCode dictionary\r\n// for resolving compact 4-byte method hashes back to full method names.\r\n//\r\n// Hashes are computed on demand (first use) and cached. The computeMethodHash\r\n// function uses BigInt-based XXH3-64, which is pure sync TypeScript.\r\n\r\nimport { getLogs } from './logging.js';\r\nimport { computeMethodHash } from './rpc-xxhash3.js';\r\n\r\nconst { warnLog } = getLogs('RpcMethodRegistry');\r\n\r\n/**\r\n * Registry mapping RPC method wire names to/from their 4-byte XXH3 hashes.\r\n * Hashes match .NET's RpcMethodRef.ComputeHashCode.\r\n * Hashes are computed lazily on first access and cached.\r\n */\r\nexport class RpcMethodRegistry {\r\n private _nameToHash = new Map<string, number>();\r\n private _hashToName = new Map<number, string>();\r\n\r\n /** Register a method name and compute its hash. */\r\n register(methodName: string): void {\r\n if (this._nameToHash.has(methodName)) return;\r\n const hash = computeMethodHash(methodName);\r\n\r\n // Check for hash collisions\r\n const existing = this._hashToName.get(hash);\r\n if (existing !== undefined && existing !== methodName) {\r\n warnLog?.log(`hash collision: \"${methodName}\" and \"${existing}\" both hash to ${hash}`);\r\n return;\r\n }\r\n\r\n this._nameToHash.set(methodName, hash);\r\n this._hashToName.set(hash, methodName);\r\n }\r\n\r\n /** Register all wire method names from a service definition. */\r\n registerService(\r\n serviceName: string,\r\n methods: ReadonlyMap<string, { name: string; wireArgCount: number }>\r\n ): void {\r\n for (const [, def] of methods) {\r\n const wireName = `${serviceName}.${def.name}:${def.wireArgCount}`;\r\n this.register(wireName);\r\n }\r\n }\r\n\r\n /** Get the hash for a method name. Computes on demand if not registered. */\r\n getHash(methodName: string): number {\r\n let hash = this._nameToHash.get(methodName);\r\n if (hash === undefined) {\r\n this.register(methodName);\r\n hash = this._nameToHash.get(methodName)!;\r\n }\r\n return hash;\r\n }\r\n\r\n /** Get the method name for a hash. Returns undefined if not registered. */\r\n getName(hash: number): string | undefined {\r\n return this._hashToName.get(hash);\r\n }\r\n\r\n /** Get the hash for a method name, throwing if not registered. */\r\n requireHash(methodName: string): number {\r\n return this.getHash(methodName);\r\n }\r\n}\r\n","// .NET counterpart:\r\n// RpcHub (141 lines) — a sealed ProcessorBase (IAsyncDisposable) that owns:\r\n// DI-resolved configuration (RpcRegistryOptions, PeerOptions, InboundCallOptions,\r\n// OutboundCallOptions, DiagnosticsOptions), SerializationFormats, Middlewares,\r\n// ClientPeerReconnectDelayer, Limits, SystemClock.\r\n// Manages peers via ConcurrentDictionary<RpcPeerRef, RpcPeer> with GetPeer()\r\n// that lazily creates + starts peers. Also has GetClient<T>() / GetServer<T>()\r\n// that look up services in ServiceRegistry.\r\n//\r\n// Omitted from .NET:\r\n// - DI / IServiceProvider integration — .NET resolves all options, serializers,\r\n// middleware, delayers, etc. from DI. TS constructs everything directly.\r\n// - RpcServiceRegistry — .NET builds a full registry of all RPC services at\r\n// startup via reflection + DI, mapping Type → RpcServiceDef. TS uses explicit\r\n// addService() / addClient() calls.\r\n// - GetClient<T>() / GetServer<T>() — .NET resolves typed client/server proxies\r\n// via the ServiceRegistry + DI. TS uses addClient<T>(peer, def).\r\n// - RpcPeerRef-keyed peer dictionary — .NET peers are keyed by RpcPeerRef (rich\r\n// value object encoding route, version, serialization format). TS uses string\r\n// refs (URL for clients, \"server://{uuid}\" for servers).\r\n// - Lazy peer creation + Start() — .NET's GetPeer auto-creates and starts a\r\n// peer's reconnection loop. TS requires explicit addPeer() + run().\r\n// - DisposeAsync — .NET disposes all peers on hub shutdown. TS has close().\r\n// - Route change detection (RouteState.WhenChanged → Dispose peer) — .NET\r\n// watches for load-balancer route changes and auto-disposes stale peers.\r\n// TS has no routing layer.\r\n// - Middlewares (IRpcMiddleware[]) — ordered middleware pipeline for inbound\r\n// calls. TS dispatches directly to service implementations.\r\n// - RpcConfiguration / Configuration.Freeze() — frozen config snapshot.\r\n// - HostId / SystemClock — infrastructure services.\r\n// - DefaultPeer / LoopbackPeer / LocalPeer / NonePeer — cached well-known peers.\r\n\r\nimport { getLogs } from './logging.js';\r\nimport {\r\n RpcClientPeer,\r\n RpcServerPeer,\r\n type RpcPeer,\r\n type RpcCallOptions,\r\n} from './rpc-peer.js';\r\nimport { RpcLimits } from './rpc-limits.js';\r\nimport { RpcClientPeerReconnectDelayer } from './rpc-client-peer-reconnect-delayer.js';\r\nimport { RpcServiceHost, type RpcServiceImpl } from './rpc-service-host.js';\r\nimport type { RpcServiceDef, RpcMethodDef } from './rpc-service-def.js';\r\nimport { wireMethodName, RpcType, RpcRemoteExecutionMode } from './rpc-service-def.js';\r\nimport { getServiceMeta, getMethodsMeta, type AnyConstructor } from './rpc-decorators.js';\r\nimport { RpcSystemCallSender } from './rpc-system-call-sender.js';\r\nimport { RpcSystemCallHandler } from './rpc-system-call-handler.js';\r\nimport { RpcStream, parseStreamRef, resolveStreamRefs } from './rpc-stream.js';\r\nimport { RpcMethodRegistry } from './rpc-method-registry.js';\r\nimport { RpcSystemCalls } from './rpc-message.js';\r\n\r\nconst { warnLog } = getLogs('RpcHub');\r\n\r\n/** Factory signature shared by all peer lookup/creation methods. Serialization\r\n * format is encoded in the URL via `?f=...` (see {@link RpcPeerRefBuilder}),\r\n * so the factory doesn't need an extra parameter for it. `getClientPeer` /\r\n * `getServerPeer` cast the result, so the factory returns RpcPeer. */\r\nexport type RpcPeerFactory = (hub: RpcHub, ref: string) => RpcPeer;\r\n\r\n/** Central RPC coordinator — manages peers, services, and configuration. */\r\nexport class RpcHub {\r\n readonly hubId: string;\r\n readonly peers = new Map<string, RpcPeer>();\r\n readonly serviceHost: RpcServiceHost;\r\n readonly systemCallSender = new RpcSystemCallSender();\r\n systemCallHandler: RpcSystemCallHandler = new RpcSystemCallHandler();\r\n\r\n /** Method registry for compact format hash ↔ name resolution.\r\n * Created lazily, populated by addService/addClient. */\r\n readonly registry = new RpcMethodRegistry();\r\n\r\n /** Shared reconnect delayer used by every client peer in this hub. Swap in\r\n * a custom subclass (e.g. app-level signal-gated) before peers start. */\r\n reconnectDelayer: RpcClientPeerReconnectDelayer = new RpcClientPeerReconnectDelayer();\r\n\r\n /** Connection-lifecycle timing limits. Peers constructed against this hub\r\n * read their initial `*Ms` field values from this instance; later\r\n * mutations affect only peers built afterward (existing peers can be\r\n * retuned by setting their `*Ms` fields directly). Defaults to the\r\n * process-wide {@link RpcLimits.Default} so a single global override\r\n * propagates to every hub that hasn't replaced this. */\r\n limits: RpcLimits = RpcLimits.Default;\r\n\r\n /** URL used by {@link defaultPeer} to resolve / create the default client\r\n * peer. Must be set before the first {@link defaultPeer} access. */\r\n defaultPeerUrl: string | undefined;\r\n /** Factory used by {@link defaultPeer} when the peer needs to be created.\r\n * If left undefined, `getPeer`'s built-in default (RpcClientPeer with\r\n * format derived from URL or resolver default) is used. */\r\n defaultPeerFactory: RpcPeerFactory | undefined;\r\n\r\n constructor(hubId?: string) {\r\n this.hubId = hubId ?? crypto.randomUUID();\r\n this.serviceHost = new RpcServiceHost();\r\n this.systemCallSender.registry = this.registry;\r\n // Pre-register system call method names for compact format hash resolution\r\n for (const methodName of Object.values(RpcSystemCalls)) {\r\n this.registry.register(methodName);\r\n }\r\n }\r\n\r\n /** Shortcut for the hub's default client peer. Fails if\r\n * {@link defaultPeerUrl} is not set. */\r\n get defaultPeer(): RpcClientPeer {\r\n if (this.defaultPeerUrl === undefined)\r\n throw new Error('RpcHub.defaultPeerUrl is not set.');\r\n return this.getClientPeer(this.defaultPeerUrl, this.defaultPeerFactory);\r\n }\r\n\r\n /** Register a peer under its ref. If a different peer is already registered\r\n * at the same ref, close it first — the hub guarantees at most one live\r\n * peer per ref, and no peer exists outside the hub. */\r\n addPeer(peer: RpcPeer): void {\r\n const existing = this.peers.get(peer.ref);\r\n if (existing !== undefined && existing !== peer)\r\n existing.close(); // close() removes itself from this.peers\r\n this.peers.set(peer.ref, peer);\r\n }\r\n\r\n removePeer(ref: string): void {\r\n const peer = this.peers.get(ref);\r\n if (peer !== undefined) {\r\n peer.close();\r\n this.peers.delete(ref);\r\n // Mirrors RpcHub.cs:137 — \"peer is removed from RpcHub\".\r\n warnLog?.log(`'${ref}': peer is removed from RpcHub`);\r\n }\r\n }\r\n\r\n /** Get or create a peer by ref. If the peer does not exist:\r\n * - with `factory` provided: it is constructed via `factory(this, ref)`;\r\n * - otherwise: server peers (ref starts with `server://`) get a plain\r\n * RpcServerPeer, anything else gets a default-configured RpcClientPeer.\r\n * The created peer is always registered in this hub. */\r\n getPeer(ref: string, factory?: RpcPeerFactory): RpcPeer {\r\n const existing = this.peers.get(ref);\r\n if (existing) return existing;\r\n\r\n const peer = factory\r\n ? factory(this, ref)\r\n : ref.startsWith('server://')\r\n ? new RpcServerPeer(this, ref)\r\n : new RpcClientPeer(this, ref);\r\n this.addPeer(peer);\r\n return peer;\r\n }\r\n\r\n /** Get or create a client peer for the given URL. See {@link getPeer} for\r\n * the factory semantics. */\r\n getClientPeer(ref: string, factory?: RpcPeerFactory): RpcClientPeer {\r\n return this.getPeer(ref, factory) as RpcClientPeer;\r\n }\r\n\r\n /** Get or create a server peer for the given ref. See {@link getPeer} for\r\n * the factory semantics. */\r\n getServerPeer(ref: string, factory?: RpcPeerFactory): RpcServerPeer {\r\n return this.getPeer(ref, factory) as RpcServerPeer;\r\n }\r\n\r\n /** Register a service with optional server method wrapping for custom call types. */\r\n addService(\r\n defOrContract: RpcServiceDef | (AnyConstructor),\r\n impl: RpcServiceImpl\r\n ): void {\r\n const def = this._resolveServiceDef(defOrContract);\r\n const wrappedImpl: RpcServiceImpl = {};\r\n for (const methodDef of def.methods.values()) {\r\n const fn = impl[methodDef.name];\r\n if (!fn) continue;\r\n wrappedImpl[methodDef.name] =\r\n methodDef.callTypeId !== 0\r\n ? this._wrapServerMethod(methodDef, fn, impl as object)\r\n : fn;\r\n }\r\n this.serviceHost.register(def, wrappedImpl);\r\n this.registry.registerService(def.name, def.methods);\r\n }\r\n\r\n /** Create a typed client proxy for a service on a remote peer. */\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type\r\n addClient<T extends object>(\r\n peer: RpcPeer,\r\n defOrContract: RpcServiceDef | (AnyConstructor)\r\n ): T {\r\n const def = this._resolveServiceDef(defOrContract);\r\n this.registry.registerService(def.name, def.methods);\r\n\r\n type RpcClientFn = (...args: unknown[]) => unknown;\r\n\r\n // Group methods by clean name, indexed by argCount for overload resolution\r\n const overloads = new Map<string, Map<number, RpcClientFn>>();\r\n for (const methodDef of def.methods.values()) {\r\n let byArgCount = overloads.get(methodDef.name);\r\n if (!byArgCount) {\r\n byArgCount = new Map<number, RpcClientFn>();\r\n overloads.set(methodDef.name, byArgCount);\r\n }\r\n byArgCount.set(\r\n methodDef.argCount,\r\n this._createClientMethod(peer, methodDef)\r\n );\r\n }\r\n\r\n // Build final proxy methods — single overload: use directly; multiple: resolve by args.length\r\n const methods = new Map<string, RpcClientFn>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n const [[, singleFn]] = byArgCount;\r\n methods.set(name, singleFn);\r\n } else {\r\n methods.set(name, (...args: unknown[]) => {\r\n const fn = byArgCount.get(args.length);\r\n if (!fn)\r\n throw new Error(\r\n `No overload of ${name} accepts ${args.length} arguments`\r\n );\r\n return fn(...args);\r\n });\r\n }\r\n }\r\n\r\n return new Proxy({} as T, {\r\n get: (_target, prop) =>\r\n typeof prop === 'string' ? methods.get(prop) : undefined,\r\n });\r\n }\r\n\r\n close(): void {\r\n for (const peer of this.peers.values()) peer.close();\r\n this.peers.clear();\r\n }\r\n\r\n /** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */\r\n protected _buildServiceDef(\r\n cls: AnyConstructor\r\n ): RpcServiceDef {\r\n const svcMeta = getServiceMeta(cls);\r\n if (svcMeta === undefined)\r\n throw new Error('Contract class missing @rpcService metadata');\r\n\r\n const methodsMeta = getMethodsMeta(cls) ?? {};\r\n const methods = new Map<string, RpcMethodDef>();\r\n\r\n for (const [name, meta] of Object.entries(methodsMeta)) {\r\n const wireArgCount = meta.argCount + 1;\r\n const mapKey = `${name}:${wireArgCount}`;\r\n const isNoWait = meta.returns === RpcType.noWait;\r\n methods.set(mapKey, {\r\n name,\r\n serviceName: svcMeta.name,\r\n argCount: meta.argCount,\r\n wireArgCount,\r\n callTypeId: 0,\r\n stream: meta.returns === RpcType.stream,\r\n noWait: isNoWait,\r\n remoteExecutionMode: isNoWait\r\n ? 0\r\n : (meta.remoteExecutionMode ?? RpcRemoteExecutionMode.Default),\r\n });\r\n }\r\n\r\n return { name: svcMeta.name, methods };\r\n }\r\n\r\n /** Resolve a service def from either a plain RpcServiceDef or a contract class. */\r\n protected _resolveServiceDef(\r\n defOrContract: RpcServiceDef | (AnyConstructor)\r\n ): RpcServiceDef {\r\n if (typeof defOrContract === 'function')\r\n return this._buildServiceDef(defOrContract);\r\n return defOrContract;\r\n }\r\n\r\n /** Override in FusionHub — default is pass-through (for methods with callTypeId !== 0). */\r\n protected _wrapServerMethod(\r\n _methodDef: RpcMethodDef,\r\n fn: (...args: unknown[]) => unknown,\r\n _impl: object\r\n ): (...args: unknown[]) => unknown {\r\n return fn;\r\n }\r\n\r\n /** Create a client method for the given methodDef. Override in FusionHub for custom call types. */\r\n protected _createClientMethod(\r\n peer: RpcPeer,\r\n methodDef: RpcMethodDef\r\n ): (...args: unknown[]) => unknown {\r\n const wireName = wireMethodName(methodDef);\r\n const mode = methodDef.remoteExecutionMode;\r\n const mustCheckConnection = !(mode & RpcRemoteExecutionMode.AwaitForConnection);\r\n\r\n // NoWait methods — remoteExecutionMode is always 0, silently drop if disconnected\r\n if (methodDef.noWait) {\r\n return (...args: unknown[]) => {\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n peer.callNoWait(wireName, callArgs);\r\n };\r\n }\r\n\r\n // Stream methods — call returns an RpcStream<T> (AsyncIterable)\r\n if (methodDef.stream) {\r\n return async (...args: unknown[]) => {\r\n if (mustCheckConnection && !peer.isConnected)\r\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const outboundCall = peer.call(wireName, callArgs);\r\n const result = await outboundCall.result;\r\n const ref = parseStreamRef(result);\r\n if (!ref)\r\n throw new Error(\r\n `Expected stream reference, got: ${JSON.stringify(result)}`\r\n );\r\n const stream = new RpcStream(ref, peer);\r\n peer.remoteObjects.register(stream);\r\n return stream;\r\n };\r\n }\r\n\r\n // Regular methods (including non-zero callTypeId — subclass can override for custom behavior)\r\n const callOptions: RpcCallOptions | undefined =\r\n (methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default)\r\n ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode }\r\n : undefined;\r\n\r\n return async (...args: unknown[]) => {\r\n if (mustCheckConnection && !peer.isConnected)\r\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const outboundCall = peer.call(wireName, callArgs, callOptions);\r\n const result = await outboundCall.result;\r\n return resolveStreamRefs(result, peer);\r\n };\r\n }\r\n}\r\n","// Ref composition helpers for RPC peers.\n//\n// `RpcClientPeer`'s ref is its full WebSocket URL. The serialization format is\n// encoded as the `f=` query parameter (see RpcClientPeer ctor).\n// `RpcServerPeer`'s ref is `server://{id}`.\n//\n// Typical use:\n// rpcHub.defaultPeerUrl = RpcPeerRefBuilder.forClient('wss://host/rpc/ws', 'msgpack6');\n// const serverRef = RpcPeerRefBuilder.forServer(crypto.randomUUID());\n\nexport class RpcPeerRefBuilder {\n /** Build a client peer ref from a WebSocket URL, optionally baking in the\n * serialization format as a `f=` query parameter.\n * @param url Base WebSocket URL, e.g. `wss://host/rpc/ws`.\n * @param serializationFormat Optional format key, e.g. `msgpack6`. When\n * provided, replaces any existing `f=` in the URL. */\n static forClient(url: string, serializationFormat?: string): string {\n if (serializationFormat === undefined) return url;\n const u = new URL(url);\n u.searchParams.set('f', serializationFormat);\n return u.toString();\n }\n\n /** Build a server peer ref from a peer id — returns `server://{id}`. */\n static forServer(id: string): string {\n return `server://${id}`;\n }\n}\n","// .NET counterpart:\r\n// RpcClient — a DI-registered service used by generated interceptor proxies.\r\n// The proxy classes (generated by Interceptor/ProxyGenerator) intercept method\r\n// calls on the service interface, create an RpcOutboundContext, resolve the\r\n// target peer via OutboundCallRouter, and invoke the outbound call pipeline.\r\n//\r\n// Omitted from .NET:\r\n// - Interceptor-based proxy generation — .NET generates proxy classes at startup\r\n// (via Castle DynamicProxy / source generators) that implement the service\r\n// interface. TS uses a runtime Proxy object instead, which achieves the same\r\n// result without code generation.\r\n// - RpcOutboundContext — .NET creates a rich context per call with CancellationToken,\r\n// Headers, CacheInfoCapture, Trace, RoutingMode. TS passes arguments directly.\r\n// - OutboundCallRouter — .NET routes each call to the appropriate peer based on\r\n// sharding/load-balancing rules. TS passes the peer explicitly.\r\n// - This standalone function is superseded by RpcHub.addClient() which adds\r\n// noWait and compute-aware dispatch. Kept for backward compatibility.\r\n\r\nimport type { RpcPeer } from './rpc-peer.js';\r\nimport type { RpcServiceDef, RpcMethodDef } from './rpc-service-def.js';\r\nimport { wireMethodName, RpcRemoteExecutionMode } from './rpc-service-def.js';\r\n\r\ntype RpcClientFn = (...args: unknown[]) => unknown;\r\n\r\n/** Creates a typed RPC client proxy — intercepts method calls and sends them over RPC. */\r\n// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T is used for caller-specified proxy type\r\nexport function createRpcClient<T extends object>(\r\n peer: RpcPeer,\r\n serviceDef: RpcServiceDef\r\n): T {\r\n // Group methods by clean name, indexed by argCount for overload resolution\r\n const overloads = new Map<string, Map<number, RpcMethodDef>>();\r\n for (const methodDef of serviceDef.methods.values()) {\r\n let byArgCount = overloads.get(methodDef.name);\r\n if (!byArgCount) {\r\n byArgCount = new Map<number, RpcMethodDef>();\r\n overloads.set(methodDef.name, byArgCount);\r\n }\r\n byArgCount.set(methodDef.argCount, methodDef);\r\n }\r\n\r\n // Build proxy methods\r\n const methods = new Map<string, RpcClientFn>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n const [[, methodDef]] = byArgCount;\r\n methods.set(name, createMethod(peer, methodDef));\r\n } else {\r\n const fns = new Map<number, RpcClientFn>();\r\n for (const [argCount, methodDef] of byArgCount) {\r\n fns.set(argCount, createMethod(peer, methodDef));\r\n }\r\n methods.set(name, (...args: unknown[]) => {\r\n const fn = fns.get(args.length);\r\n if (!fn)\r\n throw new Error(\r\n `No overload of ${name} accepts ${args.length} arguments`\r\n );\r\n return fn(...args);\r\n });\r\n }\r\n }\r\n\r\n return new Proxy({} as T, {\r\n get(_target, prop) {\r\n if (typeof prop !== 'string') return undefined;\r\n return methods.get(prop);\r\n },\r\n });\r\n}\r\n\r\nfunction createMethod(peer: RpcPeer, methodDef: RpcMethodDef): RpcClientFn {\r\n const mode = methodDef.remoteExecutionMode;\r\n const mustCheckConnection = !(mode & RpcRemoteExecutionMode.AwaitForConnection);\r\n return async (...args: unknown[]) => {\r\n if (mustCheckConnection && !peer.isConnected)\r\n throw new Error('Outbound call failed: not connected and AwaitForConnection is not set.');\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const wireName = wireMethodName(methodDef);\r\n const options =\r\n (methodDef.callTypeId !== 0 || mode !== RpcRemoteExecutionMode.Default)\r\n ? { callTypeId: methodDef.callTypeId, remoteExecutionMode: mode }\r\n : undefined;\r\n const outboundCall = peer.call(wireName, callArgs, options);\r\n // Keep `.promise` here: this is the proxy returning a Promise to user\r\n // code. Returning the PromiseSource directly would expose `resolve()`/\r\n // `reject()` to anyone who happens to type the result as PromiseSource.\r\n return outboundCall.result.promise;\r\n };\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerState\r\n\r\nexport const enum RpcPeerStateKind {\r\n Connected = 0,\r\n JustDisconnected = 1,\r\n Disconnected = 2,\r\n JustConnected = 3,\r\n}\r\n\r\nexport interface RpcPeerState {\r\n readonly kind: RpcPeerStateKind;\r\n readonly lastError?: Error;\r\n readonly reconnectsIn: number; // ms, 0 = no countdown\r\n}\r\n\r\nexport function isConnected(state: RpcPeerState): boolean {\r\n return (\r\n state.kind === RpcPeerStateKind.Connected ||\r\n state.kind === RpcPeerStateKind.JustConnected\r\n );\r\n}\r\n\r\nexport function likelyConnected(state: RpcPeerState): boolean {\r\n return state.kind !== RpcPeerStateKind.Disconnected;\r\n}\r\n\r\nexport function getStateDescription(\r\n state: RpcPeerState,\r\n useLastError = false\r\n): string {\r\n switch (state.kind) {\r\n case RpcPeerStateKind.JustConnected:\r\n return 'Just connected.';\r\n case RpcPeerStateKind.Connected:\r\n return 'Connected.';\r\n case RpcPeerStateKind.JustDisconnected:\r\n return 'Just disconnected, reconnecting...';\r\n case RpcPeerStateKind.Disconnected:\r\n if (state.reconnectsIn > 0) break; // fall through to error message below\r\n return 'Reconnecting...';\r\n }\r\n if (state.lastError == null || !useLastError) return 'Disconnected.';\r\n\r\n let message = state.lastError.message.trim();\r\n if (\r\n !(\r\n message.endsWith('.') ||\r\n message.endsWith('!') ||\r\n message.endsWith('?')\r\n )\r\n )\r\n message += '.';\r\n return 'Disconnected: ' + message;\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerStateMonitor\n// Simplified TS version using EventHandlerSet + setTimeout (no Fusion computed state).\n\nimport { EventHandlerSet } from '@actuallab/core';\nimport { getLogs } from './logging.js';\nimport { RpcConnectionState, type RpcClientPeer } from './rpc-peer.js';\nimport { type RpcPeerState, RpcPeerStateKind } from './rpc-peer-state.js';\n\nconst { infoLog } = getLogs('RpcPeerStateMonitor');\n\nconst JustConnectedPeriodMs = 1500;\nconst JustDisconnectedPeriodMs = 3000;\nconst MinReconnectsInMs = 1000;\n\nexport class RpcPeerStateMonitor {\n readonly peer: RpcClientPeer;\n readonly stateChanged = new EventHandlerSet<RpcPeerState>();\n\n private _rawConnected: boolean;\n private _connectedAt = 0;\n private _disconnectedAt = 0;\n private _lastError: Error | undefined;\n private _state: RpcPeerState;\n private _timer: ReturnType<typeof setTimeout> | undefined;\n\n // Arrow functions to preserve `this` when used as event handlers\n private _onConnectionStateChanged = (state: RpcConnectionState): void => {\n if (state === RpcConnectionState.Connected) {\n this._rawConnected = true;\n this._connectedAt = Date.now();\n this._lastError = undefined;\n } else {\n const wasConnected = this._rawConnected;\n this._rawConnected = false;\n // We no longer receive a close code/reason — only the state.\n // Retain a generic error for UI hints.\n this._lastError = new Error('Connection closed');\n if (wasConnected) {\n // True connected→disconnected transition — start JustDisconnected period\n this._disconnectedAt = Date.now();\n }\n // When wasConnected=false, this is a failed reconnect attempt —\n // keep the original _disconnectedAt so the countdown isn't reset.\n }\n this._recompute();\n };\n\n private _onReconnectsAtChanged = (): void => {\n this._recompute();\n };\n\n private _onCancelDelaysChanged = (): void => {\n this._recompute();\n };\n\n constructor(peer: RpcClientPeer) {\n this.peer = peer;\n this._rawConnected = peer.isConnected;\n\n if (this._rawConnected) {\n this._connectedAt = Date.now();\n this._state = {\n kind: RpcPeerStateKind.JustConnected,\n reconnectsIn: 0,\n };\n } else {\n this._disconnectedAt = Date.now();\n this._state = {\n kind: RpcPeerStateKind.JustDisconnected,\n reconnectsIn: 0,\n };\n }\n\n peer.connectionStateChanged.add(this._onConnectionStateChanged);\n peer.reconnectsAtChanged.add(this._onReconnectsAtChanged);\n peer.hub.reconnectDelayer.cancelDelaysChanged.add(\n this._onCancelDelaysChanged\n );\n\n // Schedule initial transition from Just* states\n this._scheduleRecompute(\n this._rawConnected\n ? JustConnectedPeriodMs\n : JustDisconnectedPeriodMs\n );\n // Mirrors RpcPeerStateMonitor.cs:87 — \"monitor (re)started\".\n infoLog?.log(`'${peer.ref}': monitor started`);\n }\n\n get state(): RpcPeerState {\n return this._state;\n }\n\n dispose(): void {\n if (this._timer !== undefined) {\n clearTimeout(this._timer);\n this._timer = undefined;\n }\n this.peer.connectionStateChanged.remove(this._onConnectionStateChanged);\n this.peer.reconnectsAtChanged.remove(this._onReconnectsAtChanged);\n this.peer.hub.reconnectDelayer.cancelDelaysChanged.remove(\n this._onCancelDelaysChanged\n );\n // Mirrors RpcPeerStateMonitor.cs:125 — \"monitor stopped\".\n infoLog?.log(`'${this.peer.ref}': monitor stopped`);\n }\n\n private _recompute(): void {\n if (this._timer !== undefined) {\n clearTimeout(this._timer);\n this._timer = undefined;\n }\n\n const now = Date.now();\n let newState: RpcPeerState;\n\n if (this._rawConnected) {\n const connectedFor = now - this._connectedAt;\n if (connectedFor < JustConnectedPeriodMs) {\n newState = {\n kind: RpcPeerStateKind.JustConnected,\n reconnectsIn: 0,\n };\n this._scheduleRecompute(JustConnectedPeriodMs - connectedFor);\n } else {\n newState = {\n kind: RpcPeerStateKind.Connected,\n reconnectsIn: 0,\n };\n }\n } else {\n const disconnectedFor = now - this._disconnectedAt;\n if (disconnectedFor < JustDisconnectedPeriodMs) {\n newState = {\n kind: RpcPeerStateKind.JustDisconnected,\n lastError: this._lastError,\n reconnectsIn: 0,\n };\n this._scheduleRecompute(\n JustDisconnectedPeriodMs - disconnectedFor\n );\n } else {\n const reconnectsAt = this.peer.reconnectsAt;\n const reconnectsIn =\n reconnectsAt > 0 ? Math.max(0, reconnectsAt - now) : 0;\n if (reconnectsIn < MinReconnectsInMs) {\n newState = {\n kind: RpcPeerStateKind.Disconnected,\n lastError: this._lastError,\n reconnectsIn: 0,\n };\n } else {\n newState = {\n kind: RpcPeerStateKind.Disconnected,\n lastError: this._lastError,\n reconnectsIn,\n };\n // Recompute periodically to update countdown\n this._scheduleRecompute(\n Math.min(1000, reconnectsIn - MinReconnectsInMs)\n );\n }\n }\n }\n\n this._setState(newState);\n }\n\n private _setState(newState: RpcPeerState): void {\n const old = this._state;\n if (\n old.kind === newState.kind &&\n old.lastError === newState.lastError &&\n old.reconnectsIn === newState.reconnectsIn\n )\n return;\n\n this._state = newState;\n this.stateChanged.trigger(newState);\n }\n\n private _scheduleRecompute(delayMs: number): void {\n if (this._timer !== undefined) clearTimeout(this._timer);\n this._timer = setTimeout(\n () => {\n this._timer = undefined;\n this._recompute();\n },\n Math.max(0, delayMs)\n );\n }\n}\n"],"mappings":"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,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC1F;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EA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","names":["RpcCallTypeId","Encoder","Encoder","Decoder","warnLog","Decoder","EventHandlerSet","EventHandlerSet","PromiseSource","PromiseSource","b","PromiseSource","PromiseSource","warnLog","PromiseSource","warnLog","DEFAULT_ACK_PERIOD","DEFAULT_ACK_ADVANCE","PromiseSource","warnLog","EventHandlerSet","PromiseSource","Decoder","Decoder","debugLog","warnLog","errorLog","EventHandlerSet","RpcConnectionState","PromiseSource","bytes","_toBytes","warnLog","textEncoder","warnLog","warnLog","RpcPeerStateKind","EventHandlerSet","infoLog","EventHandlerSet"]}