@actuallab/rpc 12.1.100 → 12.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/rpc-message.ts","../src/rpc-serialization.ts","../src/rpc-connection.ts","../src/rpc-message-channel-connection.ts","../src/rpc-service-def.ts","../src/rpc-call-tracker.ts","../src/rpc-stream.ts","../src/rpc-object.ts","../src/rpc-system-call-handler.ts","../src/rpc-system-call-sender.ts","../src/rpc-stream-sender.ts","../src/rpc-remote-object-tracker.ts","../src/rpc-shared-object-tracker.ts","../src/rpc-peer.ts","../src/rpc-client-peer-reconnect-delayer.ts","../src/rpc-service-host.ts","../src/rpc-decorators.ts","../src/rpc-hub.ts","../src/rpc-client.ts","../src/rpc-peer-state.ts","../src/rpc-peer-state-monitor.ts"],"sourcesContent":["export {\r\n RpcCallTypeId,\r\n RpcSystemCalls,\r\n ENVELOPE_DELIMITER,\r\n ARG_DELIMITER,\r\n FRAME_DELIMITER,\r\n} from \"./rpc-message.js\";\r\nexport type { RpcMessage } from \"./rpc-message.js\";\r\n\r\nexport {\r\n serializeMessage,\r\n serializeFrame,\r\n splitFrame,\r\n deserializeMessage,\r\n} from \"./rpc-serialization.js\";\r\n\r\nexport type { WebSocketLike, RpcConnection } from \"./rpc-connection.js\";\r\nexport { WebSocketState, RpcWebSocketConnection } from \"./rpc-connection.js\";\r\n\r\nexport { RpcMessageChannelConnection, createMessageChannelPair } from \"./rpc-message-channel-connection.js\";\r\n\r\nexport type { RpcMethodDef, RpcServiceDef, RpcMethodDefInput } from \"./rpc-service-def.js\";\r\nexport { RpcType, defineRpcService, wireMethodName } from \"./rpc-service-def.js\";\r\n\r\nexport {\r\n RpcOutboundCall,\r\n RpcOutboundCallTracker,\r\n RpcInboundCall,\r\n RpcInboundCallTracker,\r\n} from \"./rpc-call-tracker.js\";\r\n\r\nexport { RpcSystemCallHandler } from \"./rpc-system-call-handler.js\";\r\nexport { RpcSystemCallSender } from \"./rpc-system-call-sender.js\";\r\n\r\nexport type { RpcObjectId, IRpcObject } from \"./rpc-object.js\";\r\nexport { RpcObjectKind } from \"./rpc-object.js\";\r\nexport { RpcStream, parseStreamRef, resolveStreamRefs } from \"./rpc-stream.js\";\r\nexport type { RpcStreamRef } from \"./rpc-stream.js\";\r\nexport { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\nexport { RpcRemoteObjectTracker } from \"./rpc-remote-object-tracker.js\";\r\nexport { RpcSharedObjectTracker } from \"./rpc-shared-object-tracker.js\";\r\n\r\nexport { RpcPeer, RpcPeerConnectionKind, RpcClientPeer, RpcServerPeer, DEFAULT_SERIALIZATION_FORMAT, defaultConnectionUrlResolver } from \"./rpc-peer.js\";\r\nexport type { RemoteHandshake, RpcCallOptions, RpcConnectionUrlResolver } from \"./rpc-peer.js\";\r\nexport { RpcHub } from \"./rpc-hub.js\";\r\nexport { RpcServiceHost } from \"./rpc-service-host.js\";\r\nexport type { RpcServiceImpl, RpcDispatchContext } from \"./rpc-service-host.js\";\r\nexport { createRpcClient } from \"./rpc-client.js\";\r\nexport type { WebSocketServer } from \"./rpc-server.js\";\r\n\r\nexport { rpcService, rpcMethod, getServiceMeta, getMethodsMeta } from \"./rpc-decorators.js\";\r\nexport type { MethodMeta, ServiceMeta } from \"./rpc-decorators.js\";\r\n\r\nexport { RpcClientPeerReconnectDelayer } from \"./rpc-client-peer-reconnect-delayer.js\";\r\nexport type { RpcPeerState } from \"./rpc-peer-state.js\";\r\nexport { RpcPeerStateKind, isConnected, likelyConnected, getStateDescription } from \"./rpc-peer-state.js\";\r\nexport { RpcPeerStateMonitor } from \"./rpc-peer-state-monitor.js\";\r\n","// .NET counterparts:\r\n// RpcMessage — .NET has no single \"message\" type; the wire format is built by\r\n// RpcMessageSerializer / RpcOutboundMessage / RpcInboundMessage. In .NET the\r\n// envelope carries MethodRef, RelatedId, Headers, CallTypeId, and serialized\r\n// argument data as binary or text segments. In TS we use a simpler JSON\r\n// envelope + delimiter-separated args.\r\n//\r\n// Omitted from .NET:\r\n// - RpcCallTypeId Reliable (stage-based reconnection protocol) — the TS client\r\n// is browser-only; reconnection replays entire calls rather than resuming at\r\n// a stage, so Reliable call type adds no value.\r\n// - RpcHeader / RpcHeaders — .NET passes typed header arrays on every message\r\n// (Hash, ActivityContext, etc.). TS has no RPC caching, OpenTelemetry\r\n// propagation, or middleware pipeline, so headers aren't needed yet. The\r\n// Headers field on RpcMessage is reserved for future use.\r\n// - $sys.M (Match) / $sys.NotFound / $sys.Disconnect / Stream calls\r\n// (Ack, AckEnd, I, B, End) — Match is for hash-based response caching\r\n// (bandwidth optimisation); NotFound is a server-side \"service not registered\"\r\n// response; Disconnect is for RpcSharedObject lifetime management; the stream\r\n// calls are for RpcStream (server→client IAsyncEnumerable). None of these\r\n// are needed in the TS client today because:\r\n// * No response caching → no Match.\r\n// * Service-not-found is sent as $sys.Error.\r\n// * No shared-object tracking (server-side concept) → no Disconnect.\r\n// * No RpcStream yet → no Ack/AckEnd/I/B/End.\r\n// They can be added when streaming or caching is ported.\r\n// - Binary serialization variants (RpcByteMessageSerializer) — TS uses\r\n// text (JSON) exclusively; binary MessagePack/MemoryPack require native\r\n// codecs that aren't available in browsers.\r\n\r\n/** Identifies the type of an RPC call on the wire. */\r\nexport const enum RpcCallTypeId {\r\n Regular = 0,\r\n Reliable = 1,\r\n}\r\n\r\n/** Identifies system call methods — names include :argCount suffix for .NET interop. */\r\nexport const RpcSystemCalls = {\r\n handshake: \"$sys.Handshake:1\",\r\n ok: \"$sys.Ok:1\",\r\n error: \"$sys.Error:1\",\r\n cancel: \"$sys.Cancel:0\",\r\n keepAlive: \"$sys.KeepAlive:1\",\r\n reconnect: \"$sys.Reconnect:3\",\r\n ack: \"$sys.Ack:2\",\r\n ackEnd: \"$sys.AckEnd:1\",\r\n item: \"$sys.I:2\",\r\n batch: \"$sys.B:2\",\r\n end: \"$sys.End:2\",\r\n} as const;\r\n\r\n/** Wire-format RPC message envelope. */\r\nexport interface RpcMessage {\r\n CallType?: number;\r\n RelatedId?: number;\r\n Method?: string;\r\n Headers?: unknown[];\r\n}\r\n\r\n// Delimiters used in the wire format\r\nexport const ENVELOPE_DELIMITER = \"\\n\"; // \\x0A between envelope and args\r\nexport const ARG_DELIMITER = \"\\x1F\"; // Unit Separator between args\r\nexport const FRAME_DELIMITER = \"\\n\\x1E\"; // \\x0A\\x1E between messages in a frame\r\n","// .NET counterparts:\r\n// RpcMessageSerializer (abstract) — serialises a complete outbound message (method\r\n// ref + headers + argument data) into a frame. Has binary (V4/V5 compact) and\r\n// text (V3) implementations.\r\n// RpcArgumentSerializer (abstract) — serialises/deserialises individual argument\r\n// lists using MessagePack, MemoryPack, or System.Text.Json.\r\n// RpcTextMessageSerializerV3 — the text-based wire format: JSON envelope line,\r\n// delimiter, JSON-encoded argument segments.\r\n//\r\n// Omitted from .NET:\r\n// - Polymorphic argument handling — .NET's json5/njson5 formats inspect each\r\n// argument's runtime type and may wrap it with type info for polymorphic\r\n// deserialization. TS uses plain JSON.stringify (no static type info),\r\n// which matches the json5np/njson5np \"no polymorphism\" formats.\r\n// - Binary serialization (RpcByteMessageSerializer, MessagePack, MemoryPack) —\r\n// not available in browsers; JSON is the only format TS supports.\r\n// - ArgumentData / ReadOnlyMemory<byte> lazy deserialization — .NET defers\r\n// deserializing argument bytes until the call handler runs (important for\r\n// zero-copy binary paths). TS deserializes eagerly since JSON.parse is cheap\r\n// and there's no memory-ownership concern in JS.\r\n// - RpcSerializationFormat / RpcSerializationFormatResolver — the protocol\r\n// negotiation that picks byte-vs-text, V3-vs-V4-vs-V5. TS speaks text-V3\r\n// only; no negotiation needed.\r\n// - Size limits (MaxArgumentDataSize) — .NET enforces 130 MB caps. TS relies\r\n// on the WebSocket library's built-in frame limits; adding an explicit cap\r\n// would be straightforward but isn't necessary for the current client use case.\r\n\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\n/** Serializes an RpcMessage + args into the json5 wire format. */\r\nexport function serializeMessage(message: RpcMessage, args?: unknown[]): 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): { message: RpcMessage; args: unknown[] } {\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 return s; // Return raw string if not valid JSON\r\n }\r\n });\r\n\r\n return { message, args };\r\n}\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 { PromiseSource, EventHandlerSet } from \"@actuallab/core\";\r\nimport { splitFrame, serializeFrame } from \"./rpc-serialization.js\";\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 send(data: string): 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/** Abstract RPC connection — transport-agnostic interface for sending/receiving messages. */\r\nexport interface RpcConnection {\r\n readonly isOpen: boolean;\r\n readonly whenConnected: Promise<void>;\r\n readonly messageReceived: EventHandlerSet<string>;\r\n readonly closed: EventHandlerSet<{ code: number; reason: string }>;\r\n send(serializedMessage: string): void;\r\n close(code?: number, reason?: string): void;\r\n}\r\n\r\n/** WebSocket-based RpcConnection — handles frame splitting and message queueing. */\r\nexport class RpcWebSocketConnection implements RpcConnection {\r\n private _ws: WebSocketLike;\r\n private _sendBuffer: string[] = [];\r\n private _connected = new PromiseSource<void>();\r\n\r\n readonly messageReceived = new EventHandlerSet<string>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly error = new EventHandlerSet<unknown>();\r\n\r\n constructor(ws: WebSocketLike) {\r\n this._ws = ws;\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 const data = 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) this.messageReceived.trigger(msg);\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 try {\r\n if (this._ws.readyState === WebSocketState.OPEN)\r\n this._ws.send(serializedMessage);\r\n else if (this._ws.readyState === WebSocketState.CONNECTING)\r\n this._sendBuffer.push(serializedMessage);\r\n // CLOSING/CLOSED: silently drop\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n\r\n sendBatch(messages: string[]): void {\r\n const frame = serializeFrame(messages);\r\n this.send(frame);\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 _flush(): void {\r\n if (this._sendBuffer.length === 0) return;\r\n const frame = serializeFrame(this._sendBuffer);\r\n this._sendBuffer = [];\r\n try {\r\n this._ws.send(frame);\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 } 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 messageReceived = new EventHandlerSet<string>();\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 = typeof ev.data === \"string\" ? ev.data : String(ev.data);\r\n for (const msg of splitFrame(data))\r\n if (msg.length > 0) this.messageReceived.trigger(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 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(): [RpcMessageChannelConnection, RpcMessageChannelConnection] {\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/** 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}\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}\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 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: input.returns === RpcType.noWait,\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\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\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 message — stored for re-sending on reconnect. */\r\n serializedMessage = \"\";\r\n\r\n constructor(callId: number, method: string) {\r\n this.callId = callId;\r\n this.method = method;\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) this._calls.delete(callId);\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","import { PromiseSource } from \"@actuallab/core\";\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\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}\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 if (typeof value !== \"string\") return null;\r\n const parts = value.split(\",\");\r\n if (parts.length < 4 || parts.length > 5) 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)) return null;\r\n const allowReconnect = parts.length < 5 || parts[4] !== \"0\";\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect };\r\n}\r\n\r\n/**\r\n * Client-side RPC stream — consumes items sent by the server via system calls\r\n * ($sys.I, $sys.B, $sys.End) and implements AsyncIterable<T> for for-await-of consumption.\r\n *\r\n * Wire protocol:\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\nexport class RpcStream<T> implements AsyncIterable<T>, IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Remote;\r\n readonly allowReconnect: boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n\r\n private _buffer: 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\r\n constructor(ref: RpcStreamRef, peer: RpcPeer) {\r\n this.id = { hostId: ref.hostId, localId: ref.localId };\r\n this.allowReconnect = ref.allowReconnect;\r\n this.peer = peer;\r\n this.ackPeriod = ref.ackPeriod;\r\n this.ackAdvance = ref.ackAdvance;\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 // Gap — request reset from the server\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(index + 1);\r\n return;\r\n }\r\n\r\n this._buffer.push(item);\r\n this._nextExpectedIndex = index + 1;\r\n this._maybeSendAck(this._nextExpectedIndex);\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 this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n this._maybeSendAck(index + items.length);\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._maybeSendAck(this._nextExpectedIndex);\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.allowReconnect) {\r\n // Re-request from where we left off\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._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 if (this._iterating) throw new Error(\"RpcStream can only be iterated once.\");\r\n this._iterating = true;\r\n\r\n let bufferIndex = 0;\r\n let consumedIndex = 0;\r\n\r\n const self = this;\r\n\r\n return {\r\n async next(): Promise<IteratorResult<T>> {\r\n // Lazy start: send initial ack on first next() call\r\n if (!self._started) {\r\n self._started = true;\r\n self._sendAck(0, true);\r\n }\r\n\r\n // Read from buffer or wait for new data\r\n while (true) {\r\n if (bufferIndex < self._buffer.length) {\r\n const value = self._buffer[bufferIndex]!;\r\n bufferIndex++;\r\n consumedIndex++;\r\n return { value, done: false };\r\n }\r\n\r\n if (self._completed) {\r\n if (self._completionError) throw self._completionError;\r\n return { value: undefined as any, done: true };\r\n }\r\n\r\n // Wait for more data\r\n self._consumerWaiting = new PromiseSource<void>();\r\n await self._consumerWaiting.promise;\r\n }\r\n },\r\n\r\n async return(): Promise<IteratorResult<T>> {\r\n self._sendAckEnd();\r\n self.dispose();\r\n return { value: undefined as any, 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 this.peer.remoteObjects.unregister(this);\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 // Send ack when we've consumed enough items since the last ack\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 _sendAck(nextIndex: number, _mustReset: boolean): void {\r\n this._ackSentUpTo = nextIndex;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);\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(conn, this.id.localId, this.id.hostId);\r\n }\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 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], 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","// .NET counterpart:\r\n// RpcSystemCalls (239 lines) — implements IRpcSystemCalls interface; handles\r\n// all inbound system messages as regular RPC method calls dispatched through\r\n// the same inbound-call pipeline as user calls.\r\n//\r\n// Omitted from .NET:\r\n// - Reconnect() handler — receives compressed call-ID sets grouped by stage,\r\n// looks up each inbound call, calls TryReprocess(), and returns the IDs of\r\n// unknown calls. TS doesn't implement the Reliable reconnection protocol.\r\n// - Cancel() handler — .NET finds the inbound call and cancels its\r\n// CancellationTokenSource, aborting the running handler. TS removes the\r\n// inbound call from the tracker but does not yet propagate cancellation\r\n// to the service handler (no AbortSignal threading through dispatch).\r\n// - M() (Match) handler — tells an outbound call to use its cached result\r\n// instead of the response payload. TS has no response caching.\r\n// - NotFound() — throws an EndpointNotFound error. TS sends this as a regular\r\n// $sys.Error; no separate system call type.\r\n// - KeepAlive() / Disconnect() — manage RpcSharedObject lifetimes. TS has no\r\n// shared-object tracker.\r\n// - IRpcPolymorphicArgumentHandler.IsValidCall — resolves the concrete\r\n// deserialization type for polymorphic Ok/Item/Batch arguments by looking up\r\n// the related outbound call's return type. TS deserializes all args as\r\n// unknown via JSON.parse (inherently polymorphic).\r\n// - DI / IServiceProvider / RpcServiceBase — .NET system calls are resolved via\r\n// DI. TS uses a class with a handle() method.\r\n\r\nimport { RpcSystemCalls, type RpcMessage } from \"./rpc-message.js\";\r\nimport type { RpcPeer } from \"./rpc-peer.js\";\r\nimport type { RpcStream } from \"./rpc-stream.js\";\r\nimport { resolveStreamRefs } from \"./rpc-stream.js\";\r\nimport type { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\n\r\n/** Handles incoming system call messages — class-based equivalent of the former standalone function. */\r\nexport class RpcSystemCallHandler {\r\n handle(message: RpcMessage, args: unknown[], peer: RpcPeer): void {\r\n const method = message.Method;\r\n const relatedId = message.RelatedId ?? 0;\r\n\r\n switch (method) {\r\n case RpcSystemCalls.ok: {\r\n const call = peer.outbound.get(relatedId);\r\n if (call !== undefined) {\r\n if (call.removeOnOk) {\r\n peer.outbound.remove(relatedId);\r\n }\r\n call.result.resolve(args[0]);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.error: {\r\n const call = peer.outbound.remove(relatedId);\r\n if (call !== undefined) {\r\n const errorInfo = args[0] as Record<string, unknown> | undefined;\r\n const msg = (errorInfo?.Message ?? errorInfo?.message ?? \"RPC error\") as string;\r\n call.result.reject(new Error(msg));\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.cancel: {\r\n // Remote peer is cancelling a call it asked us to process — remove\r\n // from the inbound tracker. Full cancellation propagation (aborting\r\n // the running service handler) is not yet implemented; this just\r\n // unregisters the call so we don't send a response for it.\r\n peer.inbound.remove(relatedId);\r\n break;\r\n }\r\n case RpcSystemCalls.keepAlive: {\r\n // Remote keep-alive — nothing to do, just acknowledges the connection is alive\r\n break;\r\n }\r\n case RpcSystemCalls.item: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) stream.onItem(args[0] as number, resolveStreamRefs(args[1], peer));\r\n break;\r\n }\r\n case RpcSystemCalls.batch: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) {\r\n const items = args[1] as unknown[];\r\n for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i]!, peer);\r\n stream.onBatch(args[0] as number, items);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.end: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) {\r\n // .NET ExceptionInfo is a struct — even for normal completion, it serializes\r\n // as a non-null object with empty fields (e.g. { \"message\": \"\", \"typeRef\": {...} }).\r\n // Check both PascalCase and camelCase, and treat empty messages as no error.\r\n const errorInfo = args[1] as Record<string, unknown> | null;\r\n const msg = (errorInfo?.Message ?? errorInfo?.message) as string | undefined;\r\n const error = msg ? new Error(msg) : null;\r\n stream.onEnd(args[0] as number, error);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.ack: {\r\n const sender = peer.sharedObjects.get(relatedId) as RpcStreamSender<unknown> | undefined;\r\n sender?.onAck(args[0] as number, args[1] as string);\r\n break;\r\n }\r\n case RpcSystemCalls.ackEnd: {\r\n const sender = peer.sharedObjects.get(relatedId) as RpcStreamSender<unknown> | undefined;\r\n sender?.onAckEnd(args[0] as string);\r\n break;\r\n }\r\n }\r\n }\r\n}\r\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 { RpcSystemCalls } from \"./rpc-message.js\";\r\nimport { serializeMessage } from \"./rpc-serialization.js\";\r\nimport type { RpcConnection } from \"./rpc-connection.js\";\r\n\r\n/** Sends system RPC messages — like .NET's RpcSystemCallSender. */\r\nexport class RpcSystemCallSender {\r\n handshake(conn: RpcConnection, peerId: string, hubId: string, index: number): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.handshake }, [{\r\n RemotePeerId: peerId,\r\n RemoteApiVersionSet: null,\r\n RemoteHubId: hubId,\r\n ProtocolVersion: 2,\r\n Index: index,\r\n }]);\r\n conn.send(msg);\r\n }\r\n\r\n ok(conn: RpcConnection, relatedId: number, result: unknown): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.ok, RelatedId: relatedId }, [result]);\r\n conn.send(msg);\r\n }\r\n\r\n error(conn: RpcConnection, relatedId: number, error: unknown): void {\r\n const message = error instanceof Error ? error.message : String(error);\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.error, RelatedId: relatedId },\r\n [{ Message: message }],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n cancel(conn: RpcConnection, relatedId: number): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.cancel, RelatedId: relatedId });\r\n conn.send(msg);\r\n }\r\n\r\n keepAlive(conn: RpcConnection, activeCallIds: number[]): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);\r\n conn.send(msg);\r\n }\r\n\r\n item(conn: RpcConnection, localId: number, index: number, item: unknown): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.item, RelatedId: localId },\r\n [index, item],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n batch(conn: RpcConnection, localId: number, index: number, items: unknown[]): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.batch, RelatedId: localId },\r\n [index, items],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n end(conn: RpcConnection, localId: number, index: number, error: { Message: string } | null = null): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.end, RelatedId: localId },\r\n [index, error],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n ack(conn: RpcConnection, localId: number, nextIndex: number, hostId: string): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.ack, RelatedId: localId },\r\n [nextIndex, hostId],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n ackEnd(conn: RpcConnection, localId: number, hostId: string): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.ackEnd, RelatedId: localId },\r\n [hostId],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n}\r\n","import { PromiseSource } from \"@actuallab/core\";\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\n\r\n/** Default ack period for server-side streams. */\r\nconst DEFAULT_ACK_PERIOD = 256;\r\n/** Default ack advance 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\nexport class RpcStreamSender<T> implements IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Local;\r\n readonly allowReconnect: boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n\r\n private _nextIndex = 0;\r\n private _ended = false;\r\n private _started = new PromiseSource<void>();\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 ) {\r\n const localId = peer.sharedObjects.nextId();\r\n this.id = { hostId: peer.hub.hubId, localId };\r\n this.allowReconnect = allowReconnect;\r\n this.peer = peer;\r\n this.ackPeriod = ackPeriod;\r\n this.ackAdvance = ackAdvance;\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\"}`;\r\n }\r\n\r\n /** Called by system call handler when $sys.Ack is received from the client. */\r\n onAck(_nextIndex: number, _hostId: string): void {\r\n if (!this._started.isCompleted) {\r\n this._started.resolve();\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._ended = true;\r\n this.peer.sharedObjects.unregister(this);\r\n }\r\n\r\n /** Send a single item to the client. */\r\n sendItem(item: T): void {\r\n if (this._ended) return;\r\n const conn = this.peer.connection;\r\n if (!conn) return;\r\n this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);\r\n this._nextIndex++;\r\n }\r\n\r\n /** Send a batch of items to the client. */\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) return;\r\n this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);\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 const errorInfo = error ? { Message: error.message } : null;\r\n this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);\r\n }\r\n\r\n /**\r\n * Consume an AsyncIterable and send all items to the client.\r\n * Waits for the client's initial Ack before starting to pump items.\r\n */\r\n async writeFrom(source: AsyncIterable<T>): Promise<void> {\r\n await this._started.promise;\r\n\r\n try {\r\n for await (const item of source) {\r\n if (this._ended) return;\r\n this.sendItem(item);\r\n }\r\n if (!this._ended) {\r\n this.sendEnd();\r\n }\r\n } catch (e) {\r\n if (!this._ended) {\r\n this.sendEnd(e instanceof Error ? e : new Error(String(e)));\r\n }\r\n }\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 this._ended = true;\r\n if (!this._started.isCompleted) {\r\n // Resolve (not reject) — writeFrom() will see _ended=true and exit cleanly.\r\n // Rejecting would cause unhandled rejections when writeFrom() is void-called.\r\n this._started.resolve();\r\n }\r\n this.peer.sharedObjects.unregister(this);\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 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)\r\n obj.reconnect();\r\n else\r\n 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 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 } from \"@actuallab/core\";\r\nimport { RpcClientPeerReconnectDelayer } from \"./rpc-client-peer-reconnect-delayer.js\";\r\nimport { RpcWebSocketConnection, type RpcConnection, type WebSocketLike } 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 {\r\n serializeMessage,\r\n deserializeMessage,\r\n} 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 { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\n\r\n/**\r\n * Default serialization format for RpcClientPeer connections.\r\n * TS uses plain JSON.stringify without polymorphic type wrapping,\r\n * so \"json5np\" (System.Text.Json, no polymorphism) is the correct match.\r\n */\r\nexport const DEFAULT_SERIALIZATION_FORMAT = \"json5np\";\r\n\r\n/** Builds the WebSocket connection URL for an RpcClientPeer. */\r\nexport type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string;\r\n\r\n/** Default connection URL provider — appends `clientId` and `f` query parameters. */\r\nexport const defaultConnectionUrlResolver: RpcConnectionUrlResolver = (peer) => {\r\n const sep = peer.ref.includes('?') ? '&' : '?';\r\n return peer.ref + sep + `clientId=${peer.clientId}&f=${peer.serializationFormat}`;\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}\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 /** 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 protected _hub: RpcHub;\r\n protected _connection: RpcConnection | undefined;\r\n readonly outbound = new RpcOutboundCallTracker();\r\n readonly inbound = new RpcInboundCallTracker();\r\n readonly remoteObjects = new RpcRemoteObjectTracker();\r\n readonly sharedObjects = new RpcSharedObjectTracker();\r\n\r\n readonly connected = new EventHandlerSet<void>();\r\n readonly disconnected = new EventHandlerSet<{ code: number; reason: string }>();\r\n\r\n protected _pendingSends: RpcOutboundCall[] = [];\r\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\r\n\r\n constructor(ref: string, hub: RpcHub) {\r\n this.ref = ref;\r\n this._hub = hub;\r\n }\r\n\r\n get hub(): RpcHub {\r\n return this._hub;\r\n }\r\n\r\n get connection(): RpcConnection | undefined {\r\n return this._connection;\r\n }\r\n\r\n get isConnected(): boolean {\r\n return this._connection?.isOpen ?? false;\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 this._connection = undefined;\r\n this._stopKeepAlive();\r\n this.disconnected.trigger(ev);\r\n });\r\n\r\n void conn.whenConnected.then(() => {\r\n this.connected.trigger();\r\n this._startKeepAlive();\r\n });\r\n }\r\n\r\n call(method: string, args?: unknown[], options?: RpcCallOptions): RpcOutboundCall {\r\n const callId = this.outbound.nextId();\r\n const outboundCall = options?.outboundCallFactory\r\n ? options.outboundCallFactory(callId, method)\r\n : new RpcOutboundCall(callId, method);\r\n\r\n const msg = serializeMessage(\r\n { Method: method, RelatedId: callId, CallType: options?.callTypeId ?? 0 },\r\n args,\r\n );\r\n outboundCall.serializedMessage = msg;\r\n if (this._connection !== undefined) {\r\n this.outbound.register(outboundCall);\r\n this._connection.send(msg);\r\n } else {\r\n this._pendingSends.push(outboundCall);\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 peer = this;\r\n const hub = this._hub;\r\n const tracker = this.outbound;\r\n const onAbort = () => {\r\n if (tracker.remove(callId) !== undefined) {\r\n outboundCall.result.reject(new Error(\"Call cancelled.\"));\r\n outboundCall.onDisconnect();\r\n if (peer._connection !== undefined)\r\n hub.systemCallSender.cancel(peer._connection, callId);\r\n } else {\r\n const idx = peer._pendingSends.findIndex(c => c.callId === callId);\r\n if (idx !== -1) {\r\n peer._pendingSends.splice(idx, 1);\r\n outboundCall.result.reject(new Error(\"Call cancelled.\"));\r\n outboundCall.onDisconnect();\r\n }\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._connection === undefined) return; // silently drop\r\n const msg = serializeMessage({ Method: method, RelatedId: 0 }, args);\r\n this._connection.send(msg); // never throws\r\n }\r\n\r\n close(): void {\r\n this._stopKeepAlive();\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n for (const call of this._pendingSends) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\"Peer closed.\"));\r\n call.onDisconnect();\r\n }\r\n this._pendingSends.length = 0;\r\n this.outbound.rejectAll(new Error(\"Peer closed.\"));\r\n this.outbound.invalidateAll();\r\n this._connection?.close();\r\n this._hub.peers.delete(this.ref);\r\n }\r\n\r\n /** Send any messages buffered while disconnected. Call after connection + handshake are ready. */\r\n protected _flushPendingSends(): void {\r\n if (this._pendingSends.length === 0 || this._connection === undefined) return;\r\n for (const call of this._pendingSends) {\r\n this.outbound.register(call);\r\n this._connection.send(call.serializedMessage);\r\n }\r\n this._pendingSends.length = 0;\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(raw: string): void {\r\n const { message, args } = deserializeMessage(raw);\r\n const method = message.Method ?? \"\";\r\n\r\n // Handshake — dispatch to subclass handler\r\n if (method === RpcSystemCalls.handshake) {\r\n const handshake = args[0] as RemoteHandshake | undefined;\r\n if (handshake !== undefined)\r\n this._onHandshakeReceived(handshake);\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.inbound.register(call);\r\n }\r\n\r\n // Dispatch to the hub's service host\r\n const serviceHost = this._hub.serviceHost;\r\n if (serviceHost !== undefined) {\r\n void (async () => {\r\n try {\r\n const context = this._connection !== undefined\r\n ? { __rpcDispatch: true as const, callId: relatedId, connection: this._connection }\r\n : undefined;\r\n const result = await serviceHost.dispatch(method, args, context);\r\n if (methodDef?.stream === true && this._connection !== undefined) {\r\n // Stream method — create sender, send reference, pump items\r\n const sender = new RpcStreamSender(this);\r\n this.sharedObjects.register(sender);\r\n this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());\r\n void sender.writeFrom(result as AsyncIterable<unknown>);\r\n } else if (!isNoWait && this._connection !== undefined) {\r\n this._hub.systemCallSender.ok(this._connection, relatedId, result);\r\n }\r\n } catch (e) {\r\n if (!isNoWait && this._connection !== undefined) {\r\n this._hub.systemCallSender.error(this._connection, relatedId, e);\r\n }\r\n } finally {\r\n if (!isNoWait) {\r\n this.inbound.remove(relatedId);\r\n }\r\n }\r\n })();\r\n }\r\n }\r\n\r\n private _startKeepAlive(): void {\r\n this._keepAliveTimer = setInterval(() => {\r\n if (this._connection !== undefined) {\r\n this._hub.systemCallSender.keepAlive(this._connection, this.outbound.activeCallIds());\r\n }\r\n }, 15_000);\r\n }\r\n\r\n private _stopKeepAlive(): void {\r\n if (this._keepAliveTimer !== undefined) {\r\n clearInterval(this._keepAliveTimer);\r\n this._keepAliveTimer = undefined;\r\n }\r\n }\r\n}\r\n\r\n/** Connection state for RpcClientPeer. */\r\nexport const enum RpcPeerConnectionKind {\r\n Disconnected = 0,\r\n Connecting = 1,\r\n Connected = 2,\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 _connectionKind = RpcPeerConnectionKind.Disconnected;\r\n private _tryIndex = 0;\r\n private _handshakeIndex = 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 /**\r\n * Serialization format key sent to the server via `f=` query parameter.\r\n * TS uses plain JSON.stringify (no polymorphic type wrapping), so `json5np`\r\n * (no-polymorphism) is the correct default.\r\n */\r\n readonly serializationFormat: string;\r\n /** Builds the WebSocket URL for this peer. Replace to customize URL construction. */\r\n connectionUrlResolver: RpcConnectionUrlResolver = defaultConnectionUrlResolver;\r\n readonly peerChanged = new EventHandlerSet<void>();\r\n readonly reconnectDelayer = new RpcClientPeerReconnectDelayer();\r\n readonly reconnectsAtChanged = new EventHandlerSet<void>();\r\n\r\n get reconnectsAt(): number { return this._reconnectsAt; }\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 constructor(hub: RpcHub, url: string, serializationFormat?: string) {\r\n super(url, hub);\r\n this.clientId = guidToBase64Url(this.id);\r\n this.serializationFormat = serializationFormat ?? DEFAULT_SERIALIZATION_FORMAT;\r\n }\r\n\r\n get connectionKind(): RpcPeerConnectionKind {\r\n return this._connectionKind;\r\n }\r\n\r\n /** One-shot connection for tests — no reconnection loop, no handshake. */\r\n connectWith(conn: RpcConnection): void {\r\n // Set up new connection and re-send outbound calls (treat as peer change —\r\n // no handshake exchange to determine same-peer, so stage-3 compute calls\r\n // are self-invalidated while regular in-flight calls are re-sent).\r\n this.setupConnection(conn);\r\n this._reconnect(true);\r\n }\r\n\r\n /** Start the reconnection loop — runs until disposed. */\r\n async run(wsFactory?: (url: string) => WebSocketLike): Promise<void> {\r\n const connUrl = this.connectionUrlResolver(this);\r\n while (!this._disposed) {\r\n try {\r\n const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl) as unknown as WebSocketLike;\r\n const conn = new RpcWebSocketConnection(ws);\r\n this._connectionKind = RpcPeerConnectionKind.Connecting;\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 // Keep _connection undefined until handshake completes — prevents\r\n // calls from being sent through the connection before the handshake\r\n // (which the .NET server cannot process). Calls made during this\r\n // window go to _pendingSends and are flushed after handshake.\r\n this._connection = undefined;\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 const closedRejection = new Promise<never>((_, reject) =>\r\n conn.closed.add(() => reject(new Error(\"Connection failed\"))));\r\n closedRejection.catch(() => {}); // prevent unhandled rejection when conn closes normally\r\n await Promise.race([conn.whenConnected, closedRejection]);\r\n\r\n // Send our handshake, then wait for the server's response.\r\n this._hub.systemCallSender.handshake(conn, this.id, this._hub.hubId, ++this._handshakeIndex);\r\n const remoteHandshake = await Promise.race([\r\n this._pendingHandshake.promise,\r\n closedRejection,\r\n ]);\r\n this._pendingHandshake = undefined;\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 = this._lastRemoteHubId !== undefined && this._lastRemoteHubId !== remoteHubId;\r\n if (isPeerChanged) {\r\n // Server identity changed — clear inbound state\r\n this.inbound.clear();\r\n this.peerChanged.trigger();\r\n }\r\n this._lastRemoteHubId = remoteHubId;\r\n }\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._connection = conn;\r\n this._connectionKind = RpcPeerConnectionKind.Connected;\r\n this._tryIndex = 0;\r\n this._reconnect(isPeerChanged);\r\n\r\n // Wait until disconnected\r\n await new Promise<void>(r => conn.closed.add(() => r()));\r\n } catch {\r\n // connection failed or handshake failed\r\n }\r\n\r\n this._connectionKind = RpcPeerConnectionKind.Disconnected;\r\n if (this._disposed) break;\r\n\r\n this._tryIndex++;\r\n const delay = this.reconnectDelayer.getDelay(this._tryIndex);\r\n if (delay.isLimitExceeded) { this._disposed = true; break; }\r\n this._setReconnectsAt(delay.endsAt);\r\n try { await delay.promise; }\r\n finally { this._setReconnectsAt(0); }\r\n }\r\n }\r\n\r\n /** Re-send outbound calls after reconnection + flush pending sends.\r\n * Stage-3 compute calls are always self-invalidated: without $sys.Reconnect\r\n * protocol, the server's invalidation tracking is lost on disconnect, and\r\n * re-sending would get a duplicate $sys.Ok that is ignored (PromiseSource\r\n * already resolved). Self-invalidation forces a fresh recompute that\r\n * establishes new invalidation tracking on the new connection.\r\n * Regular in-flight calls are re-sent transparently. */\r\n private _reconnect(_isPeerChanged: boolean): void {\r\n if (this._connection === undefined) return;\r\n const conn = this._connection;\r\n\r\n // Handle remote objects on reconnect\r\n if (_isPeerChanged) {\r\n this.remoteObjects.disconnectAll();\r\n } else {\r\n this.remoteObjects.reconnectAll();\r\n }\r\n\r\n // Re-send existing tracker calls (self-invalidate stage-3 compute calls)\r\n const trackerCalls = [...this.outbound.values()];\r\n for (const call of trackerCalls) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call: self-invalidate, forcing fresh recompute\r\n call.onDisconnect();\r\n this.outbound.remove(call.callId);\r\n } else {\r\n // Regular call or in-flight compute call: re-send\r\n conn.send(call.serializedMessage);\r\n }\r\n }\r\n\r\n // Flush calls buffered while disconnected\r\n this._flushPendingSends();\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 constructor(hub: RpcHub, ref: string) {\r\n super(ref, hub);\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.setupConnection(conn);\r\n }\r\n\r\n protected override _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Client sent its handshake → respond with our own\r\n if (this._connection !== undefined) {\r\n this._hub.systemCallSender.handshake(this._connection, this.id, this._hub.hubId, 0);\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).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\n","// .NET counterpart: ActualLab.Rpc.RpcClientPeerReconnectDelayer\r\n\r\nimport { RetryDelaySeq, RetryDelayer } from \"@actuallab/core\";\r\n\r\nexport class RpcClientPeerReconnectDelayer extends RetryDelayer {\r\n constructor() {\r\n super();\r\n this.delays = RetryDelaySeq.exp(1000, 60_000); // 1s min, 60s max\r\n }\r\n}\r\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 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\nexport type RpcServiceImpl = Record<string, (...args: unknown[]) => unknown>;\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<string, { def: RpcMethodDef; fn: (...args: unknown[]) => unknown }>();\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), { def: methodDef, fn });\r\n }\r\n }\r\n\r\n async dispatch(wireMethod: string, args: unknown[], context?: RpcDispatchContext): Promise<unknown> {\r\n const entry = this._methods.get(wireMethod);\r\n if (entry === undefined) throw new Error(`Method not found: ${wireMethod}`);\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 [key: symbol]: unknown;\r\n}\r\n\r\nexport interface ServiceMeta {\r\n name: string;\r\n}\r\n\r\n/** Read method metadata from a decorated class. */\r\nexport function getMethodsMeta(cls: abstract new (...args: any[]) => any): Record<string, MethodMeta> | undefined {\r\n return (cls as any)[Symbol.metadata]?.[METHODS_META];\r\n}\r\n\r\n/** Read service metadata from a decorated class. */\r\nexport function getServiceMeta(cls: abstract new (...args: any[]) => any): ServiceMeta | undefined {\r\n return (cls as any)[Symbol.metadata]?.[SERVICE_META];\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 abstract new (...args: any[]) => any>(\r\n _target: T,\r\n context: ClassDecoratorContext<T>,\r\n ): void {\r\n const meta: ServiceMeta = ((context.metadata as any)[SERVICE_META] ??= {} as ServiceMeta);\r\n meta.name = serviceName;\r\n };\r\n}\r\n\r\n/** Method decorator — stores RPC method metadata (argCount, returns). Does NOT wrap the method. */\r\nexport function rpcMethod(options?: { returns?: symbol }) {\r\n return function<This, Args extends unknown[], Return>(\r\n target: (this: This, ...args: Args) => Return,\r\n context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>,\r\n ): (this: This, ...args: Args) => Return {\r\n const methodName = String(context.name);\r\n const methods: Record<string, MethodMeta> = ((context.metadata as any)[METHODS_META] ??= {});\r\n methods[methodName] = {\r\n ...methods[methodName],\r\n argCount: target.length,\r\n returns: options?.returns,\r\n };\r\n return target; // no wrapping — RPC proxy handles invocation\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// - Limits (RpcLimits) — configurable limits for call timeout, handshake\r\n// timeout, max message size, etc. TS uses hardcoded values.\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 { RpcClientPeer, RpcServerPeer, type RpcPeer, type RpcCallOptions } from \"./rpc-peer.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 } from \"./rpc-service-def.js\";\r\nimport { getServiceMeta, getMethodsMeta } 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\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 constructor(hubId?: string) {\r\n this.hubId = hubId ?? crypto.randomUUID();\r\n this.serviceHost = new RpcServiceHost();\r\n }\r\n\r\n addPeer(peer: RpcPeer): void {\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 }\r\n }\r\n\r\n /** Get or create a peer by ref. Server peers have \"server://\" prefix, everything else is a client peer. */\r\n getPeer(ref: string): RpcPeer {\r\n let peer = this.peers.get(ref);\r\n if (peer) return peer;\r\n peer = 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. */\r\n getClientPeer(ref: string): RpcClientPeer {\r\n return this.getPeer(ref) as RpcClientPeer;\r\n }\r\n\r\n /** Get or create a server peer for the given ref. */\r\n getServerPeer(ref: string): RpcServerPeer {\r\n return this.getPeer(ref) as RpcServerPeer;\r\n }\r\n\r\n /** Register a service with optional server method wrapping for custom call types. */\r\n addService(defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any), impl: RpcServiceImpl): 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] = 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 }\r\n\r\n /** Create a typed client proxy for a service on a remote peer. */\r\n addClient<T extends object>(peer: RpcPeer, defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any)): T {\r\n const def = this._resolveServiceDef(defOrContract);\r\n\r\n // Group methods by clean name, indexed by argCount for overload resolution\r\n const overloads = new Map<string, Map<number, Function>>();\r\n for (const methodDef of def.methods.values()) {\r\n let byArgCount = overloads.get(methodDef.name);\r\n if (!byArgCount) { byArgCount = new Map(); overloads.set(methodDef.name, byArgCount); }\r\n byArgCount.set(methodDef.argCount, this._createClientMethod(peer, methodDef));\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, Function>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n methods.set(name, byArgCount.values().next().value!);\r\n } else {\r\n methods.set(name, (...args: unknown[]) => {\r\n const fn = byArgCount.get(args.length);\r\n if (!fn) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);\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) => 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(cls: abstract new (...args: any[]) => any): RpcServiceDef {\r\n const svcMeta = getServiceMeta(cls);\r\n if (svcMeta === undefined) 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 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: meta.returns === RpcType.noWait,\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(defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any)): 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, _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, methodDef: RpcMethodDef,\r\n ): (...args: unknown[]) => unknown {\r\n const wireName = wireMethodName(methodDef);\r\n\r\n // NoWait methods\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 const callArgs = args.slice(0, methodDef.argCount);\r\n const outboundCall = peer.call(wireName, callArgs);\r\n const result = await outboundCall.result.promise;\r\n const ref = parseStreamRef(result);\r\n if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);\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 = methodDef.callTypeId !== 0\r\n ? { callTypeId: methodDef.callTypeId }\r\n : undefined;\r\n\r\n return async (...args: unknown[]) => {\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.promise;\r\n return resolveStreamRefs(result, peer);\r\n };\r\n }\r\n}\r\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 } from \"./rpc-service-def.js\";\r\n\r\n/** Creates a typed RPC client proxy — intercepts method calls and sends them over RPC. */\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) { byArgCount = new Map(); overloads.set(methodDef.name, byArgCount); }\r\n byArgCount.set(methodDef.argCount, methodDef);\r\n }\r\n\r\n // Build proxy methods\r\n const methods = new Map<string, Function>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n const methodDef = byArgCount.values().next().value!;\r\n methods.set(name, createMethod(peer, methodDef));\r\n } else {\r\n const fns = new Map<number, Function>();\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) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);\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): Function {\r\n return async (...args: unknown[]) => {\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const wireName = wireMethodName(methodDef);\r\n const options = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : 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 state.kind === RpcPeerStateKind.Connected || state.kind === RpcPeerStateKind.JustConnected;\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(state: RpcPeerState, useLastError = false): 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)\r\n break; // fall through to error message below\r\n return \"Reconnecting...\";\r\n }\r\n if (state.lastError == null || !useLastError)\r\n return \"Disconnected.\";\r\n\r\n let message = state.lastError.message.trim();\r\n if (!(message.endsWith(\".\") || message.endsWith(\"!\") || message.endsWith(\"?\")))\r\n message += \".\";\r\n return \"Disconnected: \" + message;\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerStateMonitor\r\n// Simplified TS version using EventHandlerSet + setTimeout (no Fusion computed state).\r\n\r\nimport { EventHandlerSet } from \"@actuallab/core\";\r\nimport type { RpcClientPeer } from \"./rpc-peer.js\";\r\nimport { type RpcPeerState, RpcPeerStateKind } from \"./rpc-peer-state.js\";\r\n\r\nconst JustConnectedPeriodMs = 1500;\r\nconst JustDisconnectedPeriodMs = 3000;\r\nconst MinReconnectsInMs = 1000;\r\n\r\nexport class RpcPeerStateMonitor {\r\n readonly peer: RpcClientPeer;\r\n readonly stateChanged = new EventHandlerSet<RpcPeerState>();\r\n\r\n private _rawConnected: boolean;\r\n private _connectedAt = 0;\r\n private _disconnectedAt = 0;\r\n private _lastError: Error | undefined;\r\n private _state: RpcPeerState;\r\n private _timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n // Arrow functions to preserve `this` when used as event handlers\r\n private _onConnected = (): void => {\r\n this._rawConnected = true;\r\n this._connectedAt = Date.now();\r\n this._lastError = undefined;\r\n this._recompute();\r\n };\r\n\r\n private _onDisconnected = (ev: { code: number; reason: string }): void => {\r\n const wasConnected = this._rawConnected;\r\n this._rawConnected = false;\r\n this._lastError = new Error(`Connection closed: ${ev.reason} (code ${ev.code})`);\r\n if (wasConnected) {\r\n // True connected→disconnected transition — start JustDisconnected period\r\n this._disconnectedAt = Date.now();\r\n }\r\n // When wasConnected=false, this is a failed reconnect attempt —\r\n // keep the original _disconnectedAt so the countdown isn't reset.\r\n this._recompute();\r\n };\r\n\r\n private _onReconnectsAtChanged = (): void => {\r\n this._recompute();\r\n };\r\n\r\n private _onCancelDelaysChanged = (): void => {\r\n this._recompute();\r\n };\r\n\r\n constructor(peer: RpcClientPeer) {\r\n this.peer = peer;\r\n this._rawConnected = peer.isConnected;\r\n\r\n if (this._rawConnected) {\r\n this._connectedAt = Date.now();\r\n this._state = { kind: RpcPeerStateKind.JustConnected, reconnectsIn: 0 };\r\n } else {\r\n this._disconnectedAt = Date.now();\r\n this._state = { kind: RpcPeerStateKind.JustDisconnected, reconnectsIn: 0 };\r\n }\r\n\r\n peer.connected.add(this._onConnected);\r\n peer.disconnected.add(this._onDisconnected);\r\n peer.reconnectsAtChanged.add(this._onReconnectsAtChanged);\r\n peer.reconnectDelayer.cancelDelaysChanged.add(this._onCancelDelaysChanged);\r\n\r\n // Schedule initial transition from Just* states\r\n this._scheduleRecompute(this._rawConnected ? JustConnectedPeriodMs : JustDisconnectedPeriodMs);\r\n }\r\n\r\n get state(): RpcPeerState {\r\n return this._state;\r\n }\r\n\r\n dispose(): void {\r\n if (this._timer !== undefined) {\r\n clearTimeout(this._timer);\r\n this._timer = undefined;\r\n }\r\n this.peer.connected.remove(this._onConnected);\r\n this.peer.disconnected.remove(this._onDisconnected);\r\n this.peer.reconnectsAtChanged.remove(this._onReconnectsAtChanged);\r\n this.peer.reconnectDelayer.cancelDelaysChanged.remove(this._onCancelDelaysChanged);\r\n }\r\n\r\n private _recompute(): void {\r\n if (this._timer !== undefined) {\r\n clearTimeout(this._timer);\r\n this._timer = undefined;\r\n }\r\n\r\n const now = Date.now();\r\n let newState: RpcPeerState;\r\n\r\n if (this._rawConnected) {\r\n const connectedFor = now - this._connectedAt;\r\n if (connectedFor < JustConnectedPeriodMs) {\r\n newState = { kind: RpcPeerStateKind.JustConnected, reconnectsIn: 0 };\r\n this._scheduleRecompute(JustConnectedPeriodMs - connectedFor);\r\n } else {\r\n newState = { kind: RpcPeerStateKind.Connected, reconnectsIn: 0 };\r\n }\r\n } else {\r\n const disconnectedFor = now - this._disconnectedAt;\r\n if (disconnectedFor < JustDisconnectedPeriodMs) {\r\n newState = { kind: RpcPeerStateKind.JustDisconnected, lastError: this._lastError, reconnectsIn: 0 };\r\n this._scheduleRecompute(JustDisconnectedPeriodMs - disconnectedFor);\r\n } else {\r\n const reconnectsAt = this.peer.reconnectsAt;\r\n const reconnectsIn = reconnectsAt > 0 ? Math.max(0, reconnectsAt - now) : 0;\r\n if (reconnectsIn < MinReconnectsInMs) {\r\n newState = { kind: RpcPeerStateKind.Disconnected, lastError: this._lastError, reconnectsIn: 0 };\r\n } else {\r\n newState = { kind: RpcPeerStateKind.Disconnected, lastError: this._lastError, reconnectsIn };\r\n // Recompute periodically to update countdown\r\n this._scheduleRecompute(Math.min(1000, reconnectsIn - MinReconnectsInMs));\r\n }\r\n }\r\n }\r\n\r\n this._setState(newState);\r\n }\r\n\r\n private _setState(newState: RpcPeerState): void {\r\n const old = this._state;\r\n if (old.kind === newState.kind\r\n && old.lastError === newState.lastError\r\n && old.reconnectsIn === newState.reconnectsIn)\r\n return;\r\n\r\n this._state = newState;\r\n this.stateChanged.trigger(newState);\r\n }\r\n\r\n private _scheduleRecompute(delayMs: number): void {\r\n if (this._timer !== undefined) clearTimeout(this._timer);\r\n this._timer = setTimeout(() => {\r\n this._timer = undefined;\r\n this._recompute();\r\n }, Math.max(0, delayMs));\r\n }\r\n}\r\n"],"mappings":"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","names":["RpcCallTypeId","import_core","import_core","import_core","import_core","import_core","import_core","RpcPeerConnectionKind","RpcPeerStateKind","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/rpc-message.ts","../src/rpc-serialization.ts","../src/rpc-connection.ts","../src/rpc-message-channel-connection.ts","../src/rpc-service-def.ts","../src/rpc-call-tracker.ts","../src/rpc-stream.ts","../src/rpc-object.ts","../src/rpc-system-call-handler.ts","../src/rpc-system-call-sender.ts","../src/rpc-stream-sender.ts","../src/rpc-remote-object-tracker.ts","../src/rpc-shared-object-tracker.ts","../src/rpc-peer.ts","../src/rpc-client-peer-reconnect-delayer.ts","../src/rpc-service-host.ts","../src/rpc-decorators.ts","../src/rpc-hub.ts","../src/rpc-client.ts","../src/rpc-peer-state.ts","../src/rpc-peer-state-monitor.ts"],"sourcesContent":["export {\r\n RpcCallTypeId,\r\n RpcSystemCalls,\r\n ENVELOPE_DELIMITER,\r\n ARG_DELIMITER,\r\n FRAME_DELIMITER,\r\n} from \"./rpc-message.js\";\r\nexport type { RpcMessage } from \"./rpc-message.js\";\r\n\r\nexport {\r\n serializeMessage,\r\n serializeFrame,\r\n splitFrame,\r\n deserializeMessage,\r\n} from \"./rpc-serialization.js\";\r\n\r\nexport type { WebSocketLike, RpcConnection } from \"./rpc-connection.js\";\r\nexport { WebSocketState, RpcWebSocketConnection } from \"./rpc-connection.js\";\r\n\r\nexport { RpcMessageChannelConnection, createMessageChannelPair } from \"./rpc-message-channel-connection.js\";\r\n\r\nexport type { RpcMethodDef, RpcServiceDef, RpcMethodDefInput } from \"./rpc-service-def.js\";\r\nexport { RpcType, defineRpcService, wireMethodName } from \"./rpc-service-def.js\";\r\n\r\nexport {\r\n RpcOutboundCall,\r\n RpcOutboundCallTracker,\r\n RpcInboundCall,\r\n RpcInboundCallTracker,\r\n} from \"./rpc-call-tracker.js\";\r\n\r\nexport { RpcSystemCallHandler } from \"./rpc-system-call-handler.js\";\r\nexport { RpcSystemCallSender } from \"./rpc-system-call-sender.js\";\r\n\r\nexport type { RpcObjectId, IRpcObject } from \"./rpc-object.js\";\r\nexport { RpcObjectKind } from \"./rpc-object.js\";\r\nexport { RpcStream, parseStreamRef, resolveStreamRefs } from \"./rpc-stream.js\";\r\nexport type { RpcStreamRef } from \"./rpc-stream.js\";\r\nexport { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\nexport { RpcRemoteObjectTracker } from \"./rpc-remote-object-tracker.js\";\r\nexport { RpcSharedObjectTracker } from \"./rpc-shared-object-tracker.js\";\r\n\r\nexport { RpcPeer, RpcPeerConnectionKind, RpcClientPeer, RpcServerPeer, DEFAULT_SERIALIZATION_FORMAT, RPC_CLOSE_CODE_UNSUPPORTED_FORMAT, defaultConnectionUrlResolver } from \"./rpc-peer.js\";\r\nexport type { RemoteHandshake, RpcCallOptions, RpcConnectionUrlResolver } from \"./rpc-peer.js\";\r\nexport { RpcHub } from \"./rpc-hub.js\";\r\nexport { RpcServiceHost } from \"./rpc-service-host.js\";\r\nexport type { RpcServiceImpl, RpcDispatchContext } from \"./rpc-service-host.js\";\r\nexport { createRpcClient } from \"./rpc-client.js\";\r\nexport type { WebSocketServer } from \"./rpc-server.js\";\r\n\r\nexport { rpcService, rpcMethod, getServiceMeta, getMethodsMeta } from \"./rpc-decorators.js\";\r\nexport type { MethodMeta, ServiceMeta } from \"./rpc-decorators.js\";\r\n\r\nexport { RpcClientPeerReconnectDelayer } from \"./rpc-client-peer-reconnect-delayer.js\";\r\nexport type { RpcPeerState } from \"./rpc-peer-state.js\";\r\nexport { RpcPeerStateKind, isConnected, likelyConnected, getStateDescription } from \"./rpc-peer-state.js\";\r\nexport { RpcPeerStateMonitor } from \"./rpc-peer-state-monitor.js\";\r\n","// .NET counterparts:\r\n// RpcMessage — .NET has no single \"message\" type; the wire format is built by\r\n// RpcMessageSerializer / RpcOutboundMessage / RpcInboundMessage. In .NET the\r\n// envelope carries MethodRef, RelatedId, Headers, CallTypeId, and serialized\r\n// argument data as binary or text segments. In TS we use a simpler JSON\r\n// envelope + delimiter-separated args.\r\n//\r\n// Omitted from .NET:\r\n// - RpcCallTypeId Reliable (stage-based reconnection protocol) — the TS client\r\n// is browser-only; reconnection replays entire calls rather than resuming at\r\n// a stage, so Reliable call type adds no value.\r\n// - RpcHeader / RpcHeaders — .NET passes typed header arrays on every message\r\n// (Hash, ActivityContext, etc.). TS has no RPC caching, OpenTelemetry\r\n// propagation, or middleware pipeline, so headers aren't needed yet. The\r\n// Headers field on RpcMessage is reserved for future use.\r\n// - $sys.M (Match) / $sys.NotFound / $sys.Disconnect / Stream calls\r\n// (Ack, AckEnd, I, B, End) — Match is for hash-based response caching\r\n// (bandwidth optimisation); NotFound is a server-side \"service not registered\"\r\n// response; Disconnect is for RpcSharedObject lifetime management; the stream\r\n// calls are for RpcStream (server→client IAsyncEnumerable). None of these\r\n// are needed in the TS client today because:\r\n// * No response caching → no Match.\r\n// * Service-not-found is sent as $sys.Error.\r\n// * No shared-object tracking (server-side concept) → no Disconnect.\r\n// * No RpcStream yet → no Ack/AckEnd/I/B/End.\r\n// They can be added when streaming or caching is ported.\r\n// - Binary serialization variants (RpcByteMessageSerializer) — TS uses\r\n// text (JSON) exclusively; binary MessagePack/MemoryPack require native\r\n// codecs that aren't available in browsers.\r\n\r\n/** Identifies the type of an RPC call on the wire. */\r\nexport const enum RpcCallTypeId {\r\n Regular = 0,\r\n Reliable = 1,\r\n}\r\n\r\n/** Identifies system call methods — names include :argCount suffix for .NET interop. */\r\nexport const RpcSystemCalls = {\r\n handshake: \"$sys.Handshake:1\",\r\n ok: \"$sys.Ok:1\",\r\n error: \"$sys.Error:1\",\r\n cancel: \"$sys.Cancel:0\",\r\n keepAlive: \"$sys.KeepAlive:1\",\r\n reconnect: \"$sys.Reconnect:3\",\r\n ack: \"$sys.Ack:2\",\r\n ackEnd: \"$sys.AckEnd:1\",\r\n item: \"$sys.I:2\",\r\n batch: \"$sys.B:2\",\r\n end: \"$sys.End:2\",\r\n} as const;\r\n\r\n/** Wire-format RPC message envelope. */\r\nexport interface RpcMessage {\r\n CallType?: number;\r\n RelatedId?: number;\r\n Method?: string;\r\n Headers?: unknown[];\r\n}\r\n\r\n// Delimiters used in the wire format\r\nexport const ENVELOPE_DELIMITER = \"\\n\"; // \\x0A between envelope and args\r\nexport const ARG_DELIMITER = \"\\x1F\"; // Unit Separator between args\r\nexport const FRAME_DELIMITER = \"\\n\\x1E\"; // \\x0A\\x1E between messages in a frame\r\n","// .NET counterparts:\r\n// RpcMessageSerializer (abstract) — serialises a complete outbound message (method\r\n// ref + headers + argument data) into a frame. Has binary (V4/V5 compact) and\r\n// text (V3) implementations.\r\n// RpcArgumentSerializer (abstract) — serialises/deserialises individual argument\r\n// lists using MessagePack, MemoryPack, or System.Text.Json.\r\n// RpcTextMessageSerializerV3 — the text-based wire format: JSON envelope line,\r\n// delimiter, JSON-encoded argument segments.\r\n//\r\n// Omitted from .NET:\r\n// - Polymorphic argument handling — .NET's json5/njson5 formats inspect each\r\n// argument's runtime type and may wrap it with type info for polymorphic\r\n// deserialization. TS uses plain JSON.stringify (no static type info),\r\n// which matches the json5np/njson5np \"no polymorphism\" formats.\r\n// - Binary serialization (RpcByteMessageSerializer, MessagePack, MemoryPack) —\r\n// not available in browsers; JSON is the only format TS supports.\r\n// - ArgumentData / ReadOnlyMemory<byte> lazy deserialization — .NET defers\r\n// deserializing argument bytes until the call handler runs (important for\r\n// zero-copy binary paths). TS deserializes eagerly since JSON.parse is cheap\r\n// and there's no memory-ownership concern in JS.\r\n// - RpcSerializationFormat / RpcSerializationFormatResolver — the protocol\r\n// negotiation that picks byte-vs-text, V3-vs-V4-vs-V5. TS speaks text-V3\r\n// only; no negotiation needed.\r\n// - Size limits (MaxArgumentDataSize) — .NET enforces 130 MB caps. TS relies\r\n// on the WebSocket library's built-in frame limits; adding an explicit cap\r\n// would be straightforward but isn't necessary for the current client use case.\r\n\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\n/** Serializes an RpcMessage + args into the json5 wire format. */\r\nexport function serializeMessage(message: RpcMessage, args?: unknown[]): 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): { message: RpcMessage; args: unknown[] } {\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 return s; // Return raw string if not valid JSON\r\n }\r\n });\r\n\r\n return { message, args };\r\n}\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 { PromiseSource, EventHandlerSet } from \"@actuallab/core\";\r\nimport { splitFrame, serializeFrame } from \"./rpc-serialization.js\";\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 send(data: string): 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/** Abstract RPC connection — transport-agnostic interface for sending/receiving messages. */\r\nexport interface RpcConnection {\r\n readonly isOpen: boolean;\r\n readonly whenConnected: Promise<void>;\r\n readonly messageReceived: EventHandlerSet<string>;\r\n readonly closed: EventHandlerSet<{ code: number; reason: string }>;\r\n send(serializedMessage: string): void;\r\n close(code?: number, reason?: string): void;\r\n}\r\n\r\n/** WebSocket-based RpcConnection — handles frame splitting and message queueing. */\r\nexport class RpcWebSocketConnection implements RpcConnection {\r\n private _ws: WebSocketLike;\r\n private _sendBuffer: string[] = [];\r\n private _connected = new PromiseSource<void>();\r\n\r\n readonly messageReceived = new EventHandlerSet<string>();\r\n readonly closed = new EventHandlerSet<{ code: number; reason: string }>();\r\n readonly error = new EventHandlerSet<unknown>();\r\n\r\n constructor(ws: WebSocketLike) {\r\n this._ws = ws;\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 const data = 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) this.messageReceived.trigger(msg);\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 try {\r\n if (this._ws.readyState === WebSocketState.OPEN)\r\n this._ws.send(serializedMessage);\r\n else if (this._ws.readyState === WebSocketState.CONNECTING)\r\n this._sendBuffer.push(serializedMessage);\r\n // CLOSING/CLOSED: silently drop\r\n } catch {\r\n // Swallow — disconnect event handles cleanup\r\n }\r\n }\r\n\r\n sendBatch(messages: string[]): void {\r\n const frame = serializeFrame(messages);\r\n this.send(frame);\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 _flush(): void {\r\n if (this._sendBuffer.length === 0) return;\r\n const frame = serializeFrame(this._sendBuffer);\r\n this._sendBuffer = [];\r\n try {\r\n this._ws.send(frame);\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 } 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 messageReceived = new EventHandlerSet<string>();\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 = typeof ev.data === \"string\" ? ev.data : String(ev.data);\r\n for (const msg of splitFrame(data))\r\n if (msg.length > 0) this.messageReceived.trigger(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 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(): [RpcMessageChannelConnection, RpcMessageChannelConnection] {\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/** 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}\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}\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 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: input.returns === RpcType.noWait,\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\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\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 message — stored for re-sending on reconnect. */\r\n serializedMessage = \"\";\r\n\r\n constructor(callId: number, method: string) {\r\n this.callId = callId;\r\n this.method = method;\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) this._calls.delete(callId);\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","import { PromiseSource } from \"@actuallab/core\";\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\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}\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 if (typeof value !== \"string\") return null;\r\n const parts = value.split(\",\");\r\n if (parts.length < 4 || parts.length > 5) 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)) return null;\r\n const allowReconnect = parts.length < 5 || parts[4] !== \"0\";\r\n return { hostId, localId, ackPeriod, ackAdvance, allowReconnect };\r\n}\r\n\r\n/**\r\n * Client-side RPC stream — consumes items sent by the server via system calls\r\n * ($sys.I, $sys.B, $sys.End) and implements AsyncIterable<T> for for-await-of consumption.\r\n *\r\n * Wire protocol:\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\nexport class RpcStream<T> implements AsyncIterable<T>, IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Remote;\r\n readonly allowReconnect: boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n\r\n private _buffer: 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\r\n constructor(ref: RpcStreamRef, peer: RpcPeer) {\r\n this.id = { hostId: ref.hostId, localId: ref.localId };\r\n this.allowReconnect = ref.allowReconnect;\r\n this.peer = peer;\r\n this.ackPeriod = ref.ackPeriod;\r\n this.ackAdvance = ref.ackAdvance;\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 // Gap — request reset from the server\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(index + 1);\r\n return;\r\n }\r\n\r\n this._buffer.push(item);\r\n this._nextExpectedIndex = index + 1;\r\n this._maybeSendAck(this._nextExpectedIndex);\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 this._sendAck(this._nextExpectedIndex, true);\r\n return;\r\n }\r\n if (index < this._nextExpectedIndex) {\r\n this._maybeSendAck(index + items.length);\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._maybeSendAck(this._nextExpectedIndex);\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.allowReconnect) {\r\n // Re-request from where we left off\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._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 if (this._iterating) throw new Error(\"RpcStream can only be iterated once.\");\r\n this._iterating = true;\r\n\r\n let bufferIndex = 0;\r\n let consumedIndex = 0;\r\n\r\n const self = this;\r\n\r\n return {\r\n async next(): Promise<IteratorResult<T>> {\r\n // Lazy start: send initial ack on first next() call\r\n if (!self._started) {\r\n self._started = true;\r\n self._sendAck(0, true);\r\n }\r\n\r\n // Read from buffer or wait for new data\r\n while (true) {\r\n if (bufferIndex < self._buffer.length) {\r\n const value = self._buffer[bufferIndex]!;\r\n bufferIndex++;\r\n consumedIndex++;\r\n return { value, done: false };\r\n }\r\n\r\n if (self._completed) {\r\n if (self._completionError) throw self._completionError;\r\n return { value: undefined as any, done: true };\r\n }\r\n\r\n // Wait for more data\r\n self._consumerWaiting = new PromiseSource<void>();\r\n await self._consumerWaiting.promise;\r\n }\r\n },\r\n\r\n async return(): Promise<IteratorResult<T>> {\r\n self._sendAckEnd();\r\n self.dispose();\r\n return { value: undefined as any, 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 this.peer.remoteObjects.unregister(this);\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 // Send ack when we've consumed enough items since the last ack\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 _sendAck(nextIndex: number, _mustReset: boolean): void {\r\n this._ackSentUpTo = nextIndex;\r\n const conn = this.peer.connection;\r\n if (conn) {\r\n this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);\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(conn, this.id.localId, this.id.hostId);\r\n }\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 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], 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","// .NET counterpart:\r\n// RpcSystemCalls (239 lines) — implements IRpcSystemCalls interface; handles\r\n// all inbound system messages as regular RPC method calls dispatched through\r\n// the same inbound-call pipeline as user calls.\r\n//\r\n// Omitted from .NET:\r\n// - Reconnect() handler — receives compressed call-ID sets grouped by stage,\r\n// looks up each inbound call, calls TryReprocess(), and returns the IDs of\r\n// unknown calls. TS doesn't implement the Reliable reconnection protocol.\r\n// - Cancel() handler — .NET finds the inbound call and cancels its\r\n// CancellationTokenSource, aborting the running handler. TS removes the\r\n// inbound call from the tracker but does not yet propagate cancellation\r\n// to the service handler (no AbortSignal threading through dispatch).\r\n// - M() (Match) handler — tells an outbound call to use its cached result\r\n// instead of the response payload. TS has no response caching.\r\n// - NotFound() — throws an EndpointNotFound error. TS sends this as a regular\r\n// $sys.Error; no separate system call type.\r\n// - KeepAlive() / Disconnect() — manage RpcSharedObject lifetimes. TS has no\r\n// shared-object tracker.\r\n// - IRpcPolymorphicArgumentHandler.IsValidCall — resolves the concrete\r\n// deserialization type for polymorphic Ok/Item/Batch arguments by looking up\r\n// the related outbound call's return type. TS deserializes all args as\r\n// unknown via JSON.parse (inherently polymorphic).\r\n// - DI / IServiceProvider / RpcServiceBase — .NET system calls are resolved via\r\n// DI. TS uses a class with a handle() method.\r\n\r\nimport { RpcSystemCalls, type RpcMessage } from \"./rpc-message.js\";\r\nimport type { RpcPeer } from \"./rpc-peer.js\";\r\nimport type { RpcStream } from \"./rpc-stream.js\";\r\nimport { resolveStreamRefs } from \"./rpc-stream.js\";\r\nimport type { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\n\r\n/** Handles incoming system call messages — class-based equivalent of the former standalone function. */\r\nexport class RpcSystemCallHandler {\r\n handle(message: RpcMessage, args: unknown[], peer: RpcPeer): void {\r\n const method = message.Method;\r\n const relatedId = message.RelatedId ?? 0;\r\n\r\n switch (method) {\r\n case RpcSystemCalls.ok: {\r\n const call = peer.outbound.get(relatedId);\r\n if (call !== undefined) {\r\n if (call.removeOnOk) {\r\n peer.outbound.remove(relatedId);\r\n }\r\n call.result.resolve(args[0]);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.error: {\r\n const call = peer.outbound.remove(relatedId);\r\n if (call !== undefined) {\r\n const errorInfo = args[0] as Record<string, unknown> | undefined;\r\n const msg = (errorInfo?.Message ?? errorInfo?.message ?? \"RPC error\") as string;\r\n call.result.reject(new Error(msg));\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.cancel: {\r\n // Remote peer is cancelling a call it asked us to process — remove\r\n // from the inbound tracker. Full cancellation propagation (aborting\r\n // the running service handler) is not yet implemented; this just\r\n // unregisters the call so we don't send a response for it.\r\n peer.inbound.remove(relatedId);\r\n break;\r\n }\r\n case RpcSystemCalls.keepAlive: {\r\n // Remote keep-alive — nothing to do, just acknowledges the connection is alive\r\n break;\r\n }\r\n case RpcSystemCalls.item: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) stream.onItem(args[0] as number, resolveStreamRefs(args[1], peer));\r\n break;\r\n }\r\n case RpcSystemCalls.batch: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) {\r\n const items = args[1] as unknown[];\r\n for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i]!, peer);\r\n stream.onBatch(args[0] as number, items);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.end: {\r\n const stream = peer.remoteObjects.get(relatedId) as RpcStream<unknown> | undefined;\r\n if (stream) {\r\n // .NET ExceptionInfo is a struct — even for normal completion, it serializes\r\n // as a non-null object with empty fields (e.g. { \"message\": \"\", \"typeRef\": {...} }).\r\n // Check both PascalCase and camelCase, and treat empty messages as no error.\r\n const errorInfo = args[1] as Record<string, unknown> | null;\r\n const msg = (errorInfo?.Message ?? errorInfo?.message) as string | undefined;\r\n const error = msg ? new Error(msg) : null;\r\n stream.onEnd(args[0] as number, error);\r\n }\r\n break;\r\n }\r\n case RpcSystemCalls.ack: {\r\n const sender = peer.sharedObjects.get(relatedId) as RpcStreamSender<unknown> | undefined;\r\n sender?.onAck(args[0] as number, args[1] as string);\r\n break;\r\n }\r\n case RpcSystemCalls.ackEnd: {\r\n const sender = peer.sharedObjects.get(relatedId) as RpcStreamSender<unknown> | undefined;\r\n sender?.onAckEnd(args[0] as string);\r\n break;\r\n }\r\n }\r\n }\r\n}\r\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 { RpcSystemCalls } from \"./rpc-message.js\";\r\nimport { serializeMessage } from \"./rpc-serialization.js\";\r\nimport type { RpcConnection } from \"./rpc-connection.js\";\r\n\r\n/** Sends system RPC messages — like .NET's RpcSystemCallSender. */\r\nexport class RpcSystemCallSender {\r\n handshake(conn: RpcConnection, peerId: string, hubId: string, index: number): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.handshake }, [{\r\n RemotePeerId: peerId,\r\n RemoteApiVersionSet: null,\r\n RemoteHubId: hubId,\r\n ProtocolVersion: 2,\r\n Index: index,\r\n }]);\r\n conn.send(msg);\r\n }\r\n\r\n ok(conn: RpcConnection, relatedId: number, result: unknown): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.ok, RelatedId: relatedId }, [result]);\r\n conn.send(msg);\r\n }\r\n\r\n error(conn: RpcConnection, relatedId: number, error: unknown): void {\r\n const message = error instanceof Error ? error.message : String(error);\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.error, RelatedId: relatedId },\r\n [{ Message: message }],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n cancel(conn: RpcConnection, relatedId: number): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.cancel, RelatedId: relatedId });\r\n conn.send(msg);\r\n }\r\n\r\n keepAlive(conn: RpcConnection, activeCallIds: number[]): void {\r\n const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);\r\n conn.send(msg);\r\n }\r\n\r\n item(conn: RpcConnection, localId: number, index: number, item: unknown): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.item, RelatedId: localId },\r\n [index, item],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n batch(conn: RpcConnection, localId: number, index: number, items: unknown[]): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.batch, RelatedId: localId },\r\n [index, items],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n end(conn: RpcConnection, localId: number, index: number, error: { Message: string } | null = null): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.end, RelatedId: localId },\r\n [index, error],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n ack(conn: RpcConnection, localId: number, nextIndex: number, hostId: string): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.ack, RelatedId: localId },\r\n [nextIndex, hostId],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n ackEnd(conn: RpcConnection, localId: number, hostId: string): void {\r\n const msg = serializeMessage(\r\n { Method: RpcSystemCalls.ackEnd, RelatedId: localId },\r\n [hostId],\r\n );\r\n conn.send(msg);\r\n }\r\n\r\n}\r\n","import { PromiseSource } from \"@actuallab/core\";\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\n\r\n/** Default ack period for server-side streams. */\r\nconst DEFAULT_ACK_PERIOD = 256;\r\n/** Default ack advance 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\nexport class RpcStreamSender<T> implements IRpcObject {\r\n readonly id: RpcObjectId;\r\n readonly kind = RpcObjectKind.Local;\r\n readonly allowReconnect: boolean;\r\n readonly peer: RpcPeer;\r\n readonly ackPeriod: number;\r\n readonly ackAdvance: number;\r\n\r\n private _nextIndex = 0;\r\n private _ended = false;\r\n private _started = new PromiseSource<void>();\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 ) {\r\n const localId = peer.sharedObjects.nextId();\r\n this.id = { hostId: peer.hub.hubId, localId };\r\n this.allowReconnect = allowReconnect;\r\n this.peer = peer;\r\n this.ackPeriod = ackPeriod;\r\n this.ackAdvance = ackAdvance;\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\"}`;\r\n }\r\n\r\n /** Called by system call handler when $sys.Ack is received from the client. */\r\n onAck(_nextIndex: number, _hostId: string): void {\r\n if (!this._started.isCompleted) {\r\n this._started.resolve();\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._ended = true;\r\n this.peer.sharedObjects.unregister(this);\r\n }\r\n\r\n /** Send a single item to the client. */\r\n sendItem(item: T): void {\r\n if (this._ended) return;\r\n const conn = this.peer.connection;\r\n if (!conn) return;\r\n this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);\r\n this._nextIndex++;\r\n }\r\n\r\n /** Send a batch of items to the client. */\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) return;\r\n this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);\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 const errorInfo = error ? { Message: error.message } : null;\r\n this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);\r\n }\r\n\r\n /**\r\n * Consume an AsyncIterable and send all items to the client.\r\n * Waits for the client's initial Ack before starting to pump items.\r\n */\r\n async writeFrom(source: AsyncIterable<T>): Promise<void> {\r\n await this._started.promise;\r\n\r\n try {\r\n for await (const item of source) {\r\n if (this._ended) return;\r\n this.sendItem(item);\r\n }\r\n if (!this._ended) {\r\n this.sendEnd();\r\n }\r\n } catch (e) {\r\n if (!this._ended) {\r\n this.sendEnd(e instanceof Error ? e : new Error(String(e)));\r\n }\r\n }\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 this._ended = true;\r\n if (!this._started.isCompleted) {\r\n // Resolve (not reject) — writeFrom() will see _ended=true and exit cleanly.\r\n // Rejecting would cause unhandled rejections when writeFrom() is void-called.\r\n this._started.resolve();\r\n }\r\n this.peer.sharedObjects.unregister(this);\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 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)\r\n obj.reconnect();\r\n else\r\n 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 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 } from \"@actuallab/core\";\r\nimport { RpcClientPeerReconnectDelayer } from \"./rpc-client-peer-reconnect-delayer.js\";\r\nimport { RpcWebSocketConnection, type RpcConnection, type WebSocketLike } 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 {\r\n serializeMessage,\r\n deserializeMessage,\r\n} 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 { RpcStreamSender } from \"./rpc-stream-sender.js\";\r\n\r\n/**\r\n * Default serialization format for RpcClientPeer connections.\r\n * TS uses plain JSON.stringify without polymorphic type wrapping,\r\n * so \"json5np\" (System.Text.Json, no polymorphism) is the correct match.\r\n */\r\nexport const DEFAULT_SERIALIZATION_FORMAT = \"json5np\";\r\n\r\n/** WebSocket close code sent by the server when the client's serialization format is unsupported. */\r\nexport const RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;\r\n\r\n/** Builds the WebSocket connection URL for an RpcClientPeer. */\r\nexport type RpcConnectionUrlResolver = (peer: RpcClientPeer) => string;\r\n\r\n/** Default connection URL provider — appends `clientId` and `f` query parameters. */\r\nexport const defaultConnectionUrlResolver: RpcConnectionUrlResolver = (peer) => {\r\n const sep = peer.ref.includes('?') ? '&' : '?';\r\n return peer.ref + sep + `clientId=${peer.clientId}&f=${peer.serializationFormat}`;\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}\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 /** 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 protected _hub: RpcHub;\r\n protected _connection: RpcConnection | undefined;\r\n readonly outbound = new RpcOutboundCallTracker();\r\n readonly inbound = new RpcInboundCallTracker();\r\n readonly remoteObjects = new RpcRemoteObjectTracker();\r\n readonly sharedObjects = new RpcSharedObjectTracker();\r\n\r\n readonly connected = new EventHandlerSet<void>();\r\n readonly disconnected = new EventHandlerSet<{ code: number; reason: string }>();\r\n\r\n protected _pendingSends: RpcOutboundCall[] = [];\r\n private _keepAliveTimer: ReturnType<typeof setInterval> | undefined;\r\n\r\n constructor(ref: string, hub: RpcHub) {\r\n this.ref = ref;\r\n this._hub = hub;\r\n }\r\n\r\n get hub(): RpcHub {\r\n return this._hub;\r\n }\r\n\r\n get connection(): RpcConnection | undefined {\r\n return this._connection;\r\n }\r\n\r\n get isConnected(): boolean {\r\n return this._connection?.isOpen ?? false;\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 this._connection = undefined;\r\n this._stopKeepAlive();\r\n this.disconnected.trigger(ev);\r\n });\r\n\r\n void conn.whenConnected.then(() => {\r\n this.connected.trigger();\r\n this._startKeepAlive();\r\n });\r\n }\r\n\r\n call(method: string, args?: unknown[], options?: RpcCallOptions): RpcOutboundCall {\r\n const callId = this.outbound.nextId();\r\n const outboundCall = options?.outboundCallFactory\r\n ? options.outboundCallFactory(callId, method)\r\n : new RpcOutboundCall(callId, method);\r\n\r\n const msg = serializeMessage(\r\n { Method: method, RelatedId: callId, CallType: options?.callTypeId ?? 0 },\r\n args,\r\n );\r\n outboundCall.serializedMessage = msg;\r\n if (this._connection !== undefined) {\r\n this.outbound.register(outboundCall);\r\n this._connection.send(msg);\r\n } else {\r\n this._pendingSends.push(outboundCall);\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 peer = this;\r\n const hub = this._hub;\r\n const tracker = this.outbound;\r\n const onAbort = () => {\r\n if (tracker.remove(callId) !== undefined) {\r\n outboundCall.result.reject(new Error(\"Call cancelled.\"));\r\n outboundCall.onDisconnect();\r\n if (peer._connection !== undefined)\r\n hub.systemCallSender.cancel(peer._connection, callId);\r\n } else {\r\n const idx = peer._pendingSends.findIndex(c => c.callId === callId);\r\n if (idx !== -1) {\r\n peer._pendingSends.splice(idx, 1);\r\n outboundCall.result.reject(new Error(\"Call cancelled.\"));\r\n outboundCall.onDisconnect();\r\n }\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._connection === undefined) return; // silently drop\r\n const msg = serializeMessage({ Method: method, RelatedId: 0 }, args);\r\n this._connection.send(msg); // never throws\r\n }\r\n\r\n close(): void {\r\n this._stopKeepAlive();\r\n this.remoteObjects.disconnectAll();\r\n this.sharedObjects.disconnectAll();\r\n for (const call of this._pendingSends) {\r\n if (!call.result.isCompleted)\r\n call.result.reject(new Error(\"Peer closed.\"));\r\n call.onDisconnect();\r\n }\r\n this._pendingSends.length = 0;\r\n this.outbound.rejectAll(new Error(\"Peer closed.\"));\r\n this.outbound.invalidateAll();\r\n this._connection?.close();\r\n this._hub.peers.delete(this.ref);\r\n }\r\n\r\n /** Send any messages buffered while disconnected. Call after connection + handshake are ready. */\r\n protected _flushPendingSends(): void {\r\n if (this._pendingSends.length === 0 || this._connection === undefined) return;\r\n for (const call of this._pendingSends) {\r\n this.outbound.register(call);\r\n this._connection.send(call.serializedMessage);\r\n }\r\n this._pendingSends.length = 0;\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(raw: string): void {\r\n const { message, args } = deserializeMessage(raw);\r\n const method = message.Method ?? \"\";\r\n\r\n // Handshake — dispatch to subclass handler\r\n if (method === RpcSystemCalls.handshake) {\r\n const handshake = args[0] as RemoteHandshake | undefined;\r\n if (handshake !== undefined)\r\n this._onHandshakeReceived(handshake);\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.inbound.register(call);\r\n }\r\n\r\n // Dispatch to the hub's service host\r\n const serviceHost = this._hub.serviceHost;\r\n if (serviceHost !== undefined) {\r\n void (async () => {\r\n try {\r\n const context = this._connection !== undefined\r\n ? { __rpcDispatch: true as const, callId: relatedId, connection: this._connection }\r\n : undefined;\r\n const result = await serviceHost.dispatch(method, args, context);\r\n if (methodDef?.stream === true && this._connection !== undefined) {\r\n // Stream method — create sender, send reference, pump items\r\n const sender = new RpcStreamSender(this);\r\n this.sharedObjects.register(sender);\r\n this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());\r\n void sender.writeFrom(result as AsyncIterable<unknown>);\r\n } else if (!isNoWait && this._connection !== undefined) {\r\n this._hub.systemCallSender.ok(this._connection, relatedId, result);\r\n }\r\n } catch (e) {\r\n if (!isNoWait && this._connection !== undefined) {\r\n this._hub.systemCallSender.error(this._connection, relatedId, e);\r\n }\r\n } finally {\r\n if (!isNoWait) {\r\n this.inbound.remove(relatedId);\r\n }\r\n }\r\n })();\r\n }\r\n }\r\n\r\n private _startKeepAlive(): void {\r\n this._keepAliveTimer = setInterval(() => {\r\n if (this._connection !== undefined) {\r\n this._hub.systemCallSender.keepAlive(this._connection, this.outbound.activeCallIds());\r\n }\r\n }, 15_000);\r\n }\r\n\r\n private _stopKeepAlive(): void {\r\n if (this._keepAliveTimer !== undefined) {\r\n clearInterval(this._keepAliveTimer);\r\n this._keepAliveTimer = undefined;\r\n }\r\n }\r\n}\r\n\r\n/** Connection state for RpcClientPeer. */\r\nexport const enum RpcPeerConnectionKind {\r\n Disconnected = 0,\r\n Connecting = 1,\r\n Connected = 2,\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 _connectionKind = RpcPeerConnectionKind.Disconnected;\r\n private _tryIndex = 0;\r\n private _handshakeIndex = 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 /**\r\n * Serialization format key sent to the server via `f=` query parameter.\r\n * TS uses plain JSON.stringify (no polymorphic type wrapping), so `json5np`\r\n * (no-polymorphism) is the correct default.\r\n */\r\n readonly serializationFormat: string;\r\n /** Builds the WebSocket URL for this peer. Replace to customize URL construction. */\r\n connectionUrlResolver: RpcConnectionUrlResolver = defaultConnectionUrlResolver;\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 reconnectDelayer = new RpcClientPeerReconnectDelayer();\r\n readonly reconnectsAtChanged = new EventHandlerSet<void>();\r\n\r\n get reconnectsAt(): number { return this._reconnectsAt; }\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 constructor(hub: RpcHub, url: string, serializationFormat?: string) {\r\n super(url, hub);\r\n this.clientId = guidToBase64Url(this.id);\r\n this.serializationFormat = serializationFormat ?? DEFAULT_SERIALIZATION_FORMAT;\r\n }\r\n\r\n get connectionKind(): RpcPeerConnectionKind {\r\n return this._connectionKind;\r\n }\r\n\r\n /** One-shot connection for tests — no reconnection loop, no handshake. */\r\n connectWith(conn: RpcConnection): void {\r\n // Set up new connection and re-send outbound calls (treat as peer change —\r\n // no handshake exchange to determine same-peer, so stage-3 compute calls\r\n // are self-invalidated while regular in-flight calls are re-sent).\r\n this.setupConnection(conn);\r\n this._reconnect(true);\r\n }\r\n\r\n /** Start the reconnection loop — runs until disposed. */\r\n async run(wsFactory?: (url: string) => WebSocketLike): Promise<void> {\r\n const connUrl = this.connectionUrlResolver(this);\r\n while (!this._disposed) {\r\n let lastCloseCode = 0;\r\n let lastCloseReason = \"\";\r\n try {\r\n const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl) as unknown as WebSocketLike;\r\n const conn = new RpcWebSocketConnection(ws);\r\n this._connectionKind = RpcPeerConnectionKind.Connecting;\r\n\r\n // Track close info for unsupported format detection\r\n conn.closed.add((ev) => {\r\n lastCloseCode = ev.code;\r\n lastCloseReason = ev.reason;\r\n });\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 // Keep _connection undefined until handshake completes — prevents\r\n // calls from being sent through the connection before the handshake\r\n // (which the .NET server cannot process). Calls made during this\r\n // window go to _pendingSends and are flushed after handshake.\r\n this._connection = undefined;\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 const closedRejection = new Promise<never>((_, reject) =>\r\n conn.closed.add(() => reject(new Error(\"Connection failed\"))));\r\n closedRejection.catch(() => {}); // prevent unhandled rejection when conn closes normally\r\n await Promise.race([conn.whenConnected, closedRejection]);\r\n\r\n // Send our handshake, then wait for the server's response.\r\n this._hub.systemCallSender.handshake(conn, this.id, this._hub.hubId, ++this._handshakeIndex);\r\n const remoteHandshake = await Promise.race([\r\n this._pendingHandshake.promise,\r\n closedRejection,\r\n ]);\r\n this._pendingHandshake = undefined;\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 = this._lastRemoteHubId !== undefined && this._lastRemoteHubId !== remoteHubId;\r\n if (isPeerChanged) {\r\n // Server identity changed — clear inbound state\r\n this.inbound.clear();\r\n this.peerChanged.trigger();\r\n }\r\n this._lastRemoteHubId = remoteHubId;\r\n }\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._connection = conn;\r\n this._connectionKind = RpcPeerConnectionKind.Connected;\r\n this._tryIndex = 0;\r\n this._reconnect(isPeerChanged);\r\n\r\n // Wait until disconnected\r\n await new Promise<void>(r => conn.closed.add(() => r()));\r\n } catch {\r\n // connection failed or handshake failed\r\n }\r\n\r\n this._connectionKind = RpcPeerConnectionKind.Disconnected;\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 this.unsupportedFormat.trigger({ reason: lastCloseReason });\r\n this._disposed = true;\r\n break;\r\n }\r\n\r\n this._tryIndex++;\r\n const delay = this.reconnectDelayer.getDelay(this._tryIndex);\r\n if (delay.isLimitExceeded) { this._disposed = true; break; }\r\n this._setReconnectsAt(delay.endsAt);\r\n try { await delay.promise; }\r\n finally { this._setReconnectsAt(0); }\r\n }\r\n }\r\n\r\n /** Re-send outbound calls after reconnection + flush pending sends.\r\n * Stage-3 compute calls are always self-invalidated: without $sys.Reconnect\r\n * protocol, the server's invalidation tracking is lost on disconnect, and\r\n * re-sending would get a duplicate $sys.Ok that is ignored (PromiseSource\r\n * already resolved). Self-invalidation forces a fresh recompute that\r\n * establishes new invalidation tracking on the new connection.\r\n * Regular in-flight calls are re-sent transparently. */\r\n private _reconnect(_isPeerChanged: boolean): void {\r\n if (this._connection === undefined) return;\r\n const conn = this._connection;\r\n\r\n // Handle remote objects on reconnect\r\n if (_isPeerChanged) {\r\n this.remoteObjects.disconnectAll();\r\n } else {\r\n this.remoteObjects.reconnectAll();\r\n }\r\n\r\n // Re-send existing tracker calls (self-invalidate stage-3 compute calls)\r\n const trackerCalls = [...this.outbound.values()];\r\n for (const call of trackerCalls) {\r\n if (!call.removeOnOk && call.result.isCompleted) {\r\n // Stage-3 compute call: self-invalidate, forcing fresh recompute\r\n call.onDisconnect();\r\n this.outbound.remove(call.callId);\r\n } else {\r\n // Regular call or in-flight compute call: re-send\r\n conn.send(call.serializedMessage);\r\n }\r\n }\r\n\r\n // Flush calls buffered while disconnected\r\n this._flushPendingSends();\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 constructor(hub: RpcHub, ref: string) {\r\n super(ref, hub);\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.setupConnection(conn);\r\n }\r\n\r\n protected override _onHandshakeReceived(_handshake: RemoteHandshake): void {\r\n // Client sent its handshake → respond with our own\r\n if (this._connection !== undefined) {\r\n this._hub.systemCallSender.handshake(this._connection, this.id, this._hub.hubId, 0);\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).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\n","// .NET counterpart: ActualLab.Rpc.RpcClientPeerReconnectDelayer\r\n\r\nimport { RetryDelaySeq, RetryDelayer } from \"@actuallab/core\";\r\n\r\nexport class RpcClientPeerReconnectDelayer extends RetryDelayer {\r\n constructor() {\r\n super();\r\n this.delays = RetryDelaySeq.exp(1000, 60_000); // 1s min, 60s max\r\n }\r\n}\r\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 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\nexport type RpcServiceImpl = Record<string, (...args: unknown[]) => unknown>;\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<string, { def: RpcMethodDef; fn: (...args: unknown[]) => unknown }>();\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), { def: methodDef, fn });\r\n }\r\n }\r\n\r\n async dispatch(wireMethod: string, args: unknown[], context?: RpcDispatchContext): Promise<unknown> {\r\n const entry = this._methods.get(wireMethod);\r\n if (entry === undefined) throw new Error(`Method not found: ${wireMethod}`);\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 [key: symbol]: unknown;\r\n}\r\n\r\nexport interface ServiceMeta {\r\n name: string;\r\n}\r\n\r\n/** Read method metadata from a decorated class. */\r\nexport function getMethodsMeta(cls: abstract new (...args: any[]) => any): Record<string, MethodMeta> | undefined {\r\n return (cls as any)[Symbol.metadata]?.[METHODS_META];\r\n}\r\n\r\n/** Read service metadata from a decorated class. */\r\nexport function getServiceMeta(cls: abstract new (...args: any[]) => any): ServiceMeta | undefined {\r\n return (cls as any)[Symbol.metadata]?.[SERVICE_META];\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 abstract new (...args: any[]) => any>(\r\n _target: T,\r\n context: ClassDecoratorContext<T>,\r\n ): void {\r\n const meta: ServiceMeta = ((context.metadata as any)[SERVICE_META] ??= {} as ServiceMeta);\r\n meta.name = serviceName;\r\n };\r\n}\r\n\r\n/** Method decorator — stores RPC method metadata (argCount, returns). Does NOT wrap the method. */\r\nexport function rpcMethod(options?: { returns?: symbol }) {\r\n return function<This, Args extends unknown[], Return>(\r\n target: (this: This, ...args: Args) => Return,\r\n context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>,\r\n ): (this: This, ...args: Args) => Return {\r\n const methodName = String(context.name);\r\n const methods: Record<string, MethodMeta> = ((context.metadata as any)[METHODS_META] ??= {});\r\n methods[methodName] = {\r\n ...methods[methodName],\r\n argCount: target.length,\r\n returns: options?.returns,\r\n };\r\n return target; // no wrapping — RPC proxy handles invocation\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// - Limits (RpcLimits) — configurable limits for call timeout, handshake\r\n// timeout, max message size, etc. TS uses hardcoded values.\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 { RpcClientPeer, RpcServerPeer, type RpcPeer, type RpcCallOptions } from \"./rpc-peer.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 } from \"./rpc-service-def.js\";\r\nimport { getServiceMeta, getMethodsMeta } 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\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 constructor(hubId?: string) {\r\n this.hubId = hubId ?? crypto.randomUUID();\r\n this.serviceHost = new RpcServiceHost();\r\n }\r\n\r\n addPeer(peer: RpcPeer): void {\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 }\r\n }\r\n\r\n /** Get or create a peer by ref. Server peers have \"server://\" prefix, everything else is a client peer. */\r\n getPeer(ref: string): RpcPeer {\r\n let peer = this.peers.get(ref);\r\n if (peer) return peer;\r\n peer = 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. */\r\n getClientPeer(ref: string): RpcClientPeer {\r\n return this.getPeer(ref) as RpcClientPeer;\r\n }\r\n\r\n /** Get or create a server peer for the given ref. */\r\n getServerPeer(ref: string): RpcServerPeer {\r\n return this.getPeer(ref) as RpcServerPeer;\r\n }\r\n\r\n /** Register a service with optional server method wrapping for custom call types. */\r\n addService(defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any), impl: RpcServiceImpl): 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] = 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 }\r\n\r\n /** Create a typed client proxy for a service on a remote peer. */\r\n addClient<T extends object>(peer: RpcPeer, defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any)): T {\r\n const def = this._resolveServiceDef(defOrContract);\r\n\r\n // Group methods by clean name, indexed by argCount for overload resolution\r\n const overloads = new Map<string, Map<number, Function>>();\r\n for (const methodDef of def.methods.values()) {\r\n let byArgCount = overloads.get(methodDef.name);\r\n if (!byArgCount) { byArgCount = new Map(); overloads.set(methodDef.name, byArgCount); }\r\n byArgCount.set(methodDef.argCount, this._createClientMethod(peer, methodDef));\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, Function>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n methods.set(name, byArgCount.values().next().value!);\r\n } else {\r\n methods.set(name, (...args: unknown[]) => {\r\n const fn = byArgCount.get(args.length);\r\n if (!fn) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);\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) => 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(cls: abstract new (...args: any[]) => any): RpcServiceDef {\r\n const svcMeta = getServiceMeta(cls);\r\n if (svcMeta === undefined) 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 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: meta.returns === RpcType.noWait,\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(defOrContract: RpcServiceDef | (abstract new (...args: any[]) => any)): 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, _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, methodDef: RpcMethodDef,\r\n ): (...args: unknown[]) => unknown {\r\n const wireName = wireMethodName(methodDef);\r\n\r\n // NoWait methods\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 const callArgs = args.slice(0, methodDef.argCount);\r\n const outboundCall = peer.call(wireName, callArgs);\r\n const result = await outboundCall.result.promise;\r\n const ref = parseStreamRef(result);\r\n if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);\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 = methodDef.callTypeId !== 0\r\n ? { callTypeId: methodDef.callTypeId }\r\n : undefined;\r\n\r\n return async (...args: unknown[]) => {\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.promise;\r\n return resolveStreamRefs(result, peer);\r\n };\r\n }\r\n}\r\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 } from \"./rpc-service-def.js\";\r\n\r\n/** Creates a typed RPC client proxy — intercepts method calls and sends them over RPC. */\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) { byArgCount = new Map(); overloads.set(methodDef.name, byArgCount); }\r\n byArgCount.set(methodDef.argCount, methodDef);\r\n }\r\n\r\n // Build proxy methods\r\n const methods = new Map<string, Function>();\r\n for (const [name, byArgCount] of overloads) {\r\n if (byArgCount.size === 1) {\r\n const methodDef = byArgCount.values().next().value!;\r\n methods.set(name, createMethod(peer, methodDef));\r\n } else {\r\n const fns = new Map<number, Function>();\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) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);\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): Function {\r\n return async (...args: unknown[]) => {\r\n const callArgs = args.slice(0, methodDef.argCount);\r\n const wireName = wireMethodName(methodDef);\r\n const options = methodDef.callTypeId !== 0 ? { callTypeId: methodDef.callTypeId } : 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 state.kind === RpcPeerStateKind.Connected || state.kind === RpcPeerStateKind.JustConnected;\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(state: RpcPeerState, useLastError = false): 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)\r\n break; // fall through to error message below\r\n return \"Reconnecting...\";\r\n }\r\n if (state.lastError == null || !useLastError)\r\n return \"Disconnected.\";\r\n\r\n let message = state.lastError.message.trim();\r\n if (!(message.endsWith(\".\") || message.endsWith(\"!\") || message.endsWith(\"?\")))\r\n message += \".\";\r\n return \"Disconnected: \" + message;\r\n}\r\n","// .NET counterpart: ActualLab.Fusion.Extensions.RpcPeerStateMonitor\r\n// Simplified TS version using EventHandlerSet + setTimeout (no Fusion computed state).\r\n\r\nimport { EventHandlerSet } from \"@actuallab/core\";\r\nimport type { RpcClientPeer } from \"./rpc-peer.js\";\r\nimport { type RpcPeerState, RpcPeerStateKind } from \"./rpc-peer-state.js\";\r\n\r\nconst JustConnectedPeriodMs = 1500;\r\nconst JustDisconnectedPeriodMs = 3000;\r\nconst MinReconnectsInMs = 1000;\r\n\r\nexport class RpcPeerStateMonitor {\r\n readonly peer: RpcClientPeer;\r\n readonly stateChanged = new EventHandlerSet<RpcPeerState>();\r\n\r\n private _rawConnected: boolean;\r\n private _connectedAt = 0;\r\n private _disconnectedAt = 0;\r\n private _lastError: Error | undefined;\r\n private _state: RpcPeerState;\r\n private _timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n // Arrow functions to preserve `this` when used as event handlers\r\n private _onConnected = (): void => {\r\n this._rawConnected = true;\r\n this._connectedAt = Date.now();\r\n this._lastError = undefined;\r\n this._recompute();\r\n };\r\n\r\n private _onDisconnected = (ev: { code: number; reason: string }): void => {\r\n const wasConnected = this._rawConnected;\r\n this._rawConnected = false;\r\n this._lastError = new Error(`Connection closed: ${ev.reason} (code ${ev.code})`);\r\n if (wasConnected) {\r\n // True connected→disconnected transition — start JustDisconnected period\r\n this._disconnectedAt = Date.now();\r\n }\r\n // When wasConnected=false, this is a failed reconnect attempt —\r\n // keep the original _disconnectedAt so the countdown isn't reset.\r\n this._recompute();\r\n };\r\n\r\n private _onReconnectsAtChanged = (): void => {\r\n this._recompute();\r\n };\r\n\r\n private _onCancelDelaysChanged = (): void => {\r\n this._recompute();\r\n };\r\n\r\n constructor(peer: RpcClientPeer) {\r\n this.peer = peer;\r\n this._rawConnected = peer.isConnected;\r\n\r\n if (this._rawConnected) {\r\n this._connectedAt = Date.now();\r\n this._state = { kind: RpcPeerStateKind.JustConnected, reconnectsIn: 0 };\r\n } else {\r\n this._disconnectedAt = Date.now();\r\n this._state = { kind: RpcPeerStateKind.JustDisconnected, reconnectsIn: 0 };\r\n }\r\n\r\n peer.connected.add(this._onConnected);\r\n peer.disconnected.add(this._onDisconnected);\r\n peer.reconnectsAtChanged.add(this._onReconnectsAtChanged);\r\n peer.reconnectDelayer.cancelDelaysChanged.add(this._onCancelDelaysChanged);\r\n\r\n // Schedule initial transition from Just* states\r\n this._scheduleRecompute(this._rawConnected ? JustConnectedPeriodMs : JustDisconnectedPeriodMs);\r\n }\r\n\r\n get state(): RpcPeerState {\r\n return this._state;\r\n }\r\n\r\n dispose(): void {\r\n if (this._timer !== undefined) {\r\n clearTimeout(this._timer);\r\n this._timer = undefined;\r\n }\r\n this.peer.connected.remove(this._onConnected);\r\n this.peer.disconnected.remove(this._onDisconnected);\r\n this.peer.reconnectsAtChanged.remove(this._onReconnectsAtChanged);\r\n this.peer.reconnectDelayer.cancelDelaysChanged.remove(this._onCancelDelaysChanged);\r\n }\r\n\r\n private _recompute(): void {\r\n if (this._timer !== undefined) {\r\n clearTimeout(this._timer);\r\n this._timer = undefined;\r\n }\r\n\r\n const now = Date.now();\r\n let newState: RpcPeerState;\r\n\r\n if (this._rawConnected) {\r\n const connectedFor = now - this._connectedAt;\r\n if (connectedFor < JustConnectedPeriodMs) {\r\n newState = { kind: RpcPeerStateKind.JustConnected, reconnectsIn: 0 };\r\n this._scheduleRecompute(JustConnectedPeriodMs - connectedFor);\r\n } else {\r\n newState = { kind: RpcPeerStateKind.Connected, reconnectsIn: 0 };\r\n }\r\n } else {\r\n const disconnectedFor = now - this._disconnectedAt;\r\n if (disconnectedFor < JustDisconnectedPeriodMs) {\r\n newState = { kind: RpcPeerStateKind.JustDisconnected, lastError: this._lastError, reconnectsIn: 0 };\r\n this._scheduleRecompute(JustDisconnectedPeriodMs - disconnectedFor);\r\n } else {\r\n const reconnectsAt = this.peer.reconnectsAt;\r\n const reconnectsIn = reconnectsAt > 0 ? Math.max(0, reconnectsAt - now) : 0;\r\n if (reconnectsIn < MinReconnectsInMs) {\r\n newState = { kind: RpcPeerStateKind.Disconnected, lastError: this._lastError, reconnectsIn: 0 };\r\n } else {\r\n newState = { kind: RpcPeerStateKind.Disconnected, lastError: this._lastError, reconnectsIn };\r\n // Recompute periodically to update countdown\r\n this._scheduleRecompute(Math.min(1000, reconnectsIn - MinReconnectsInMs));\r\n }\r\n }\r\n }\r\n\r\n this._setState(newState);\r\n }\r\n\r\n private _setState(newState: RpcPeerState): void {\r\n const old = this._state;\r\n if (old.kind === newState.kind\r\n && old.lastError === newState.lastError\r\n && old.reconnectsIn === newState.reconnectsIn)\r\n return;\r\n\r\n this._state = newState;\r\n this.stateChanged.trigger(newState);\r\n }\r\n\r\n private _scheduleRecompute(delayMs: number): void {\r\n if (this._timer !== undefined) clearTimeout(this._timer);\r\n this._timer = setTimeout(() => {\r\n this._timer = undefined;\r\n this._recompute();\r\n }, Math.max(0, delayMs));\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC+BO,IAAW,gBAAX,kBAAWA,mBAAX;AACL,EAAAA,8BAAA,aAAU,KAAV;AACA,EAAAA,8BAAA,cAAW,KAAX;AAFgB,SAAAA;AAAA,GAAA;AAMX,IAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,WAAW;AAAA,EACX,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AACP;AAWO,IAAM,qBAAqB;AAC3B,IAAM,gBAAgB;AACtB,IAAM,kBAAkB;;;AC3BxB,SAAS,iBAAiB,SAAqB,MAA0B;AAC9E,QAAM,WAAW,KAAK,UAAU,OAAO;AACvC,MAAI,SAAS,UAAa,KAAK,WAAW,GAAG;AAC3C,WAAO,WAAW;AAAA,EACpB;AACA,QAAM,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,aAAa;AACrE,SAAO,WAAW,qBAAqB;AACzC;AAGO,SAAS,eAAe,UAA4B;AACzD,SAAO,SAAS,KAAK,eAAe;AACtC;AAGO,SAAS,WAAW,OAAyB;AAClD,SAAO,MAAM,MAAM,eAAe;AACpC;AAGO,SAAS,mBAAmB,KAAuD;AACxF,QAAM,UAAU,IAAI,QAAQ,kBAAkB;AAC9C,MAAI,YAAY,IAAI;AAClB,WAAO,EAAE,SAAS,KAAK,MAAM,GAAG,GAAiB,MAAM,CAAC,EAAE;AAAA,EAC5D;AAEA,QAAM,cAAc,IAAI,UAAU,GAAG,OAAO;AAC5C,QAAM,UAAU,IAAI,UAAU,UAAU,CAAC;AACzC,QAAM,UAAU,KAAK,MAAM,WAAW;AAEtC,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,SAAS,MAAM,CAAC,EAAE;AAAA,EAC7B;AAEA,QAAM,OAAO,QAAQ,MAAM,aAAa,EAAE,IAAI,CAAC,MAAM;AACnD,QAAI;AACF,aAAO,KAAK,MAAM,CAAC;AAAA,IACrB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,SAAO,EAAE,SAAS,KAAK;AACzB;;;ACtDA,kBAA+C;AAcxC,IAAM,iBAAiB;AAAA,EAC5B,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AACV;AAaO,IAAM,yBAAN,MAAsD;AAAA,EACnD;AAAA,EACA,cAAwB,CAAC;AAAA,EACzB,aAAa,IAAI,0BAAoB;AAAA,EAEpC,kBAAkB,IAAI,4BAAwB;AAAA,EAC9C,SAAS,IAAI,4BAAkD;AAAA,EAC/D,QAAQ,IAAI,4BAAyB;AAAA,EAE9C,YAAY,IAAmB;AAC7B,SAAK,MAAM;AAEX,QAAI,GAAG,eAAe,eAAe,MAAM;AACzC,WAAK,WAAW,QAAQ;AACxB,WAAK,OAAO;AAAA,IACd;AAEA,OAAG,SAAS,MAAM;AAChB,WAAK,WAAW,QAAQ;AACxB,WAAK,OAAO;AAAA,IACd;AAEA,OAAG,YAAY,CAAC,OAAO;AACrB,YAAM,OAAO,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO,OAAO,GAAG,IAAI;AACnE,YAAM,WAAW,WAAW,IAAI;AAChC,iBAAW,OAAO,UAAU;AAC1B,YAAI,IAAI,SAAS,EAAG,MAAK,gBAAgB,QAAQ,GAAG;AAAA,MACtD;AAAA,IACF;AAEA,OAAG,UAAU,CAAC,OAAO;AACnB,WAAK,OAAO,QAAQ,EAAE,MAAM,GAAG,MAAM,QAAQ,GAAG,OAAO,CAAC;AAAA,IAC1D;AAEA,OAAG,UAAU,CAAC,OAAO;AACnB,WAAK,MAAM,QAAQ,EAAE;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,IAAI,SAAkB;AACpB,WAAO,KAAK,IAAI,eAAe,eAAe;AAAA,EAChD;AAAA,EAEA,IAAI,gBAA+B;AACjC,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,KAAK,mBAAiC;AACpC,QAAI;AACF,UAAI,KAAK,IAAI,eAAe,eAAe;AACzC,aAAK,IAAI,KAAK,iBAAiB;AAAA,eACxB,KAAK,IAAI,eAAe,eAAe;AAC9C,aAAK,YAAY,KAAK,iBAAiB;AAAA,IAE3C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,UAAU,UAA0B;AAClC,UAAM,QAAQ,eAAe,QAAQ;AACrC,SAAK,KAAK,KAAK;AAAA,EACjB;AAAA,EAEA,MAAM,MAAe,QAAuB;AAC1C,SAAK,IAAI,MAAM,MAAM,MAAM;AAAA,EAC7B;AAAA,EAEQ,SAAe;AACrB,QAAI,KAAK,YAAY,WAAW,EAAG;AACnC,UAAM,QAAQ,eAAe,KAAK,WAAW;AAC7C,SAAK,cAAc,CAAC;AACpB,QAAI;AACF,WAAK,IAAI,KAAK,KAAK;AAAA,IACrB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AC/HA,IAAAC,eAAgC;AAKzB,IAAM,8BAAN,MAA2D;AAAA,EACxD;AAAA,EACA,QAAQ;AAAA,EAEP,kBAAkB,IAAI,6BAAwB;AAAA,EAC9C,SAAS,IAAI,6BAAkD;AAAA,EAC/D,gBAA+B,QAAQ,QAAQ;AAAA;AAAA,EAExD,YAAY,MAAmB;AAC7B,SAAK,QAAQ;AACb,SAAK,YAAY,CAAC,OAAqB;AACrC,YAAM,OAAO,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO,OAAO,GAAG,IAAI;AACnE,iBAAW,OAAO,WAAW,IAAI;AAC/B,YAAI,IAAI,SAAS,EAAG,MAAK,gBAAgB,QAAQ,GAAG;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,KAAK,mBAAiC;AACpC,QAAI,CAAC,KAAK,MAAO;AACjB,QAAI;AACF,WAAK,MAAM,YAAY,iBAAiB;AAAA,IAC1C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,MAAe,QAAuB;AAC1C,QAAI,CAAC,KAAK,MAAO;AACjB,SAAK,QAAQ;AACb,SAAK,MAAM,MAAM;AACjB,SAAK,OAAO,QAAQ,EAAE,MAAM,QAAQ,KAAM,QAAQ,UAAU,GAAG,CAAC;AAAA,EAClE;AACF;AAGO,SAAS,2BAAuF;AACrG,QAAM,UAAU,IAAI,eAAe;AACnC,SAAO;AAAA,IACL,IAAI,4BAA4B,QAAQ,KAAK;AAAA,IAC7C,IAAI,4BAA4B,QAAQ,KAAK;AAAA,EAC/C;AACF;;;ACGO,IAAM,UAAU;AAAA,EACrB,QAAQ,uBAAO,QAAQ;AAAA,EACvB,QAAQ,uBAAO,QAAQ;AAAA,EACvB,QAAQ,uBAAO,QAAQ;AAAA,EACvB,MAAM,uBAAO,MAAM;AACrB;AAGO,SAAS,eAAe,KAA2B;AACxD,SAAO,GAAG,IAAI,WAAW,IAAI,IAAI,IAAI,IAAI,IAAI,YAAY;AAC3D;AAEO,SAAS,iBACd,MACA,SACe;AACf,QAAM,MAAM,oBAAI,IAA0B;AAC1C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAIlD,UAAM,aAAa,IAAI,QAAQ,GAAG;AAClC,UAAM,YAAY,cAAc,IAAI,IAAI,UAAU,GAAG,UAAU,IAAI;AACnE,UAAM,eAAe,MAAM,gBAAiB,MAAM,KAAK,SAAS;AAChE,UAAM,SAAS,GAAG,SAAS,IAAI,YAAY;AAC3C,QAAI,IAAI,QAAQ;AAAA,MACd,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU,MAAM,KAAK;AAAA,MACrB;AAAA,MACA,YAAY,MAAM,cAAc;AAAA,MAChC,QAAQ,MAAM,YAAY,QAAQ;AAAA,MAClC,QAAQ,MAAM,YAAY,QAAQ;AAAA,IACpC,CAAC;AAAA,EACH;AACA,SAAO,EAAE,MAAM,SAAS,IAAI;AAC9B;;;AChDA,IAAAC,eAA8B;AAGvB,IAAM,kBAAN,MAAsB;AAAA,EAClB;AAAA,EACA;AAAA,EACA,SAAS,IAAI,2BAAuB;AAAA;AAAA;AAAA,EAIpC,aAAsB;AAAA;AAAA,EAG/B,oBAAoB;AAAA,EAEpB,YAAY,QAAgB,QAAgB;AAC1C,SAAK,SAAS;AACd,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,eAAqB;AAAA,EAErB;AACF;AAGO,IAAM,yBAAN,MAA6B;AAAA,EAC1B,SAAS,oBAAI,IAA6B;AAAA,EAC1C,UAAU;AAAA,EAElB,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAS,MAA6B;AACpC,SAAK,OAAO,IAAI,KAAK,QAAQ,IAAI;AAAA,EACnC;AAAA,EAEA,IAAI,QAA6C;AAC/C,WAAO,KAAK,OAAO,IAAI,MAAM;AAAA,EAC/B;AAAA,EAEA,OAAO,QAA6C;AAClD,UAAM,OAAO,KAAK,OAAO,IAAI,MAAM;AACnC,QAAI,SAAS,OAAW,MAAK,OAAO,OAAO,MAAM;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,SAA4C;AAC1C,WAAO,KAAK,OAAO,OAAO;AAAA,EAC5B;AAAA,EAEA,gBAA0B;AACxB,WAAO,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA,EAIA,UAAU,OAAoB;AAC5B,eAAW,CAAC,IAAI,IAAI,KAAK,KAAK,QAAQ;AACpC,UAAI,CAAC,KAAK,cAAc,KAAK,OAAO,aAAa;AAE/C;AAAA,MACF;AACA,WAAK,OAAO,OAAO,KAAK;AACxB,WAAK,aAAa;AAClB,WAAK,OAAO,OAAO,EAAE;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGA,gBAAsB;AACpB,eAAW,QAAQ,KAAK,OAAO,OAAO,GAAG;AACvC,WAAK,aAAa;AAAA,IACpB;AACA,SAAK,OAAO,MAAM;AAAA,EACpB;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;AAGO,IAAM,iBAAN,MAAqB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,QAAgB,MAAiB;AAC3D,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,wBAAN,MAA4B;AAAA,EACzB,SAAS,oBAAI,IAA4B;AAAA,EAEjD,IAAI,OAAe;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,SAAS,MAA4B;AACnC,SAAK,OAAO,IAAI,KAAK,QAAQ,IAAI;AAAA,EACnC;AAAA,EAEA,IAAI,QAA4C;AAC9C,WAAO,KAAK,OAAO,IAAI,MAAM;AAAA,EAC/B;AAAA,EAEA,OAAO,QAA4C;AACjD,UAAM,OAAO,KAAK,OAAO,IAAI,MAAM;AACnC,QAAI,SAAS,OAAW,MAAK,OAAO,OAAO,MAAM;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;;;AC9KA,IAAAC,eAA8B;;;ACKvB,IAAM,gBAAgB,EAAE,OAAO,GAAG,QAAQ,EAAE;;;ADa5C,SAAS,eAAe,OAAqC;AAClE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,SAAS,KAAK,MAAM,SAAS,EAAG,QAAO;AACjD,QAAM,SAAS,MAAM,CAAC;AACtB,QAAM,UAAU,SAAS,MAAM,CAAC,GAAI,EAAE;AACtC,QAAM,YAAY,SAAS,MAAM,CAAC,GAAI,EAAE;AACxC,QAAM,aAAa,SAAS,MAAM,CAAC,GAAI,EAAE;AACzC,MAAI,MAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,UAAU,EAAG,QAAO;AACpE,QAAM,iBAAiB,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM;AACxD,SAAO,EAAE,QAAQ,SAAS,WAAW,YAAY,eAAe;AAClE;AAaO,IAAM,YAAN,MAA2D;AAAA,EACvD;AAAA,EACA,OAAO,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAED,UAAe,CAAC;AAAA,EAChB,qBAAqB;AAAA,EACrB,mBAA+C;AAAA,EAC/C,aAAa;AAAA,EACb,mBAAiC;AAAA,EACjC,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EAEvB,YAAY,KAAmB,MAAe;AAC5C,SAAK,KAAK,EAAE,QAAQ,IAAI,QAAQ,SAAS,IAAI,QAAQ;AACrD,SAAK,iBAAiB,IAAI;AAC1B,SAAK,OAAO;AACZ,SAAK,YAAY,IAAI;AACrB,SAAK,aAAa,IAAI;AAAA,EACxB;AAAA;AAAA,EAGA,OAAO,OAAe,MAAe;AACnC,QAAI,KAAK,aAAa,KAAK,WAAY;AAEvC,QAAI,QAAQ,KAAK,oBAAoB;AAEnC,WAAK,SAAS,KAAK,oBAAoB,IAAI;AAC3C;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,oBAAoB;AAEnC,WAAK,cAAc,QAAQ,CAAC;AAC5B;AAAA,IACF;AAEA,SAAK,QAAQ,KAAK,IAAI;AACtB,SAAK,qBAAqB,QAAQ;AAClC,SAAK,cAAc,KAAK,kBAAkB;AAC1C,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAGA,QAAQ,OAAe,OAAkB;AACvC,QAAI,KAAK,aAAa,KAAK,WAAY;AAEvC,QAAI,QAAQ,KAAK,oBAAoB;AACnC,WAAK,SAAS,KAAK,oBAAoB,IAAI;AAC3C;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,oBAAoB;AACnC,WAAK,cAAc,QAAQ,MAAM,MAAM;AACvC;AAAA,IACF;AAEA,eAAW,QAAQ,MAAO,MAAK,QAAQ,KAAK,IAAI;AAChD,SAAK,qBAAqB,QAAQ,MAAM;AACxC,SAAK,cAAc,KAAK,kBAAkB;AAC1C,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAGA,MAAM,OAAe,OAA2B;AAC9C,QAAI,KAAK,aAAa,KAAK,WAAY;AACvC,SAAK,aAAa;AAClB,SAAK,mBAAmB;AACxB,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,YAAkB;AAChB,QAAI,KAAK,gBAAgB;AAEvB,WAAK,SAAS,KAAK,oBAAoB,IAAI;AAAA,IAC7C,OAAO;AACL,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,aAAmB;AACjB,QAAI,KAAK,WAAY;AACrB,SAAK,aAAa;AAClB,SAAK,mBAAmB,IAAI,MAAM,oBAAoB;AACtD,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,CAAC,OAAO,aAAa,IAAsB;AACzC,QAAI,KAAK,WAAY,OAAM,IAAI,MAAM,sCAAsC;AAC3E,SAAK,aAAa;AAElB,QAAI,cAAc;AAClB,QAAI,gBAAgB;AAEpB,UAAM,OAAO;AAEb,WAAO;AAAA,MACL,MAAM,OAAmC;AAEvC,YAAI,CAAC,KAAK,UAAU;AAClB,eAAK,WAAW;AAChB,eAAK,SAAS,GAAG,IAAI;AAAA,QACvB;AAGA,eAAO,MAAM;AACX,cAAI,cAAc,KAAK,QAAQ,QAAQ;AACrC,kBAAM,QAAQ,KAAK,QAAQ,WAAW;AACtC;AACA;AACA,mBAAO,EAAE,OAAO,MAAM,MAAM;AAAA,UAC9B;AAEA,cAAI,KAAK,YAAY;AACnB,gBAAI,KAAK,iBAAkB,OAAM,KAAK;AACtC,mBAAO,EAAE,OAAO,QAAkB,MAAM,KAAK;AAAA,UAC/C;AAGA,eAAK,mBAAmB,IAAI,2BAAoB;AAChD,gBAAM,KAAK,iBAAiB;AAAA,QAC9B;AAAA,MACF;AAAA,MAEA,MAAM,SAAqC;AACzC,aAAK,YAAY;AACjB,aAAK,QAAQ;AACb,eAAO,EAAE,OAAO,QAAkB,MAAM,KAAK;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,KAAK,cAAc,WAAW,IAAI;AACvC,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIQ,kBAAwB;AAC9B,QAAI,KAAK,qBAAqB,MAAM;AAClC,WAAK,iBAAiB,QAAQ;AAC9B,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,cAAc,WAAyB;AAC7C,QAAI,KAAK,aAAa,EAAG;AAEzB,UAAM,YAAY,KAAK,eAAe,KAAK;AAC3C,QAAI,aAAa,WAAW;AAC1B,WAAK,SAAS,WAAW,KAAK;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,SAAS,WAAmB,YAA2B;AAC7D,SAAK,eAAe;AACpB,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,MAAM;AACR,WAAK,KAAK,IAAI,iBAAiB,IAAI,MAAM,KAAK,GAAG,SAAS,WAAW,KAAK,GAAG,MAAM;AAAA,IACrF;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,MAAM;AACR,WAAK,KAAK,IAAI,iBAAiB,OAAO,MAAM,KAAK,GAAG,SAAS,KAAK,GAAG,MAAM;AAAA,IAC7E;AAAA,EACF;AACF;AAQO,SAAS,kBAAkB,OAAgB,MAAwB;AACxE,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAElD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,MAAM,eAAe,KAAK;AAChC,QAAI,QAAQ,MAAM;AAChB,YAAM,SAAS,IAAI,UAAU,KAAK,IAAI;AACtC,WAAK,cAAc,SAAS,MAAM;AAClC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,CAAC,IAAI,kBAAkB,MAAM,CAAC,GAAG,IAAI;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,eAAW,OAAO,OAAO,KAAK,KAAgC,GAAG;AAC/D,MAAC,MAAkC,GAAG,IAAI;AAAA,QACvC,MAAkC,GAAG;AAAA,QAAG;AAAA,MAC3C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;AElOO,IAAM,uBAAN,MAA2B;AAAA,EAChC,OAAO,SAAqB,MAAiB,MAAqB;AAChE,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,QAAQ,aAAa;AAEvC,YAAQ,QAAQ;AAAA,MACd,KAAK,eAAe,IAAI;AACtB,cAAM,OAAO,KAAK,SAAS,IAAI,SAAS;AACxC,YAAI,SAAS,QAAW;AACtB,cAAI,KAAK,YAAY;AACnB,iBAAK,SAAS,OAAO,SAAS;AAAA,UAChC;AACA,eAAK,OAAO,QAAQ,KAAK,CAAC,CAAC;AAAA,QAC7B;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe,OAAO;AACzB,cAAM,OAAO,KAAK,SAAS,OAAO,SAAS;AAC3C,YAAI,SAAS,QAAW;AACtB,gBAAM,YAAY,KAAK,CAAC;AACxB,gBAAM,MAAO,WAAW,WAAW,WAAW,WAAW;AACzD,eAAK,OAAO,OAAO,IAAI,MAAM,GAAG,CAAC;AAAA,QACnC;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe,QAAQ;AAK1B,aAAK,QAAQ,OAAO,SAAS;AAC7B;AAAA,MACF;AAAA,MACA,KAAK,eAAe,WAAW;AAE7B;AAAA,MACF;AAAA,MACA,KAAK,eAAe,MAAM;AACxB,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,YAAI,OAAQ,QAAO,OAAO,KAAK,CAAC,GAAa,kBAAkB,KAAK,CAAC,GAAG,IAAI,CAAC;AAC7E;AAAA,MACF;AAAA,MACA,KAAK,eAAe,OAAO;AACzB,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,YAAI,QAAQ;AACV,gBAAM,QAAQ,KAAK,CAAC;AACpB,mBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,OAAM,CAAC,IAAI,kBAAkB,MAAM,CAAC,GAAI,IAAI;AACnF,iBAAO,QAAQ,KAAK,CAAC,GAAa,KAAK;AAAA,QACzC;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe,KAAK;AACvB,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,YAAI,QAAQ;AAIV,gBAAM,YAAY,KAAK,CAAC;AACxB,gBAAM,MAAO,WAAW,WAAW,WAAW;AAC9C,gBAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,IAAI;AACrC,iBAAO,MAAM,KAAK,CAAC,GAAa,KAAK;AAAA,QACvC;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe,KAAK;AACvB,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,gBAAQ,MAAM,KAAK,CAAC,GAAa,KAAK,CAAC,CAAW;AAClD;AAAA,MACF;AAAA,MACA,KAAK,eAAe,QAAQ;AAC1B,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,gBAAQ,SAAS,KAAK,CAAC,CAAW;AAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC9EO,IAAM,sBAAN,MAA0B;AAAA,EAC/B,UAAU,MAAqB,QAAgB,OAAe,OAAqB;AACjF,UAAM,MAAM,iBAAiB,EAAE,QAAQ,eAAe,UAAU,GAAG,CAAC;AAAA,MAClE,cAAc;AAAA,MACd,qBAAqB;AAAA,MACrB,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB,OAAO;AAAA,IACT,CAAC,CAAC;AACF,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,GAAG,MAAqB,WAAmB,QAAuB;AAChE,UAAM,MAAM,iBAAiB,EAAE,QAAQ,eAAe,IAAI,WAAW,UAAU,GAAG,CAAC,MAAM,CAAC;AAC1F,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,MAAM,MAAqB,WAAmB,OAAsB;AAClE,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,OAAO,WAAW,UAAU;AAAA,MACrD,CAAC,EAAE,SAAS,QAAQ,CAAC;AAAA,IACvB;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,OAAO,MAAqB,WAAyB;AACnD,UAAM,MAAM,iBAAiB,EAAE,QAAQ,eAAe,QAAQ,WAAW,UAAU,CAAC;AACpF,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,UAAU,MAAqB,eAA+B;AAC5D,UAAM,MAAM,iBAAiB,EAAE,QAAQ,eAAe,UAAU,GAAG,CAAC,aAAa,CAAC;AAClF,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,KAAK,MAAqB,SAAiB,OAAe,MAAqB;AAC7E,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,MAAM,WAAW,QAAQ;AAAA,MAClD,CAAC,OAAO,IAAI;AAAA,IACd;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,MAAM,MAAqB,SAAiB,OAAe,OAAwB;AACjF,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,OAAO,WAAW,QAAQ;AAAA,MACnD,CAAC,OAAO,KAAK;AAAA,IACf;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,IAAI,MAAqB,SAAiB,OAAe,QAAoC,MAAY;AACvG,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,KAAK,WAAW,QAAQ;AAAA,MACjD,CAAC,OAAO,KAAK;AAAA,IACf;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,IAAI,MAAqB,SAAiB,WAAmB,QAAsB;AACjF,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,KAAK,WAAW,QAAQ;AAAA,MACjD,CAAC,WAAW,MAAM;AAAA,IACpB;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAAA,EAEA,OAAO,MAAqB,SAAiB,QAAsB;AACjE,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,eAAe,QAAQ,WAAW,QAAQ;AAAA,MACpD,CAAC,MAAM;AAAA,IACT;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AAEF;;;AC3GA,IAAAC,eAA8B;AAM9B,IAAM,qBAAqB;AAE3B,IAAM,sBAAsB;AAcrB,IAAM,kBAAN,MAA+C;AAAA,EAC3C;AAAA,EACA,OAAO,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAED,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAAW,IAAI,2BAAoB;AAAA,EAE3C,YACI,MACA,YAAY,oBACZ,aAAa,qBACb,iBAAiB,MACnB;AACA,UAAM,UAAU,KAAK,cAAc,OAAO;AAC1C,SAAK,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,QAAQ;AAC5C,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,QAAgB;AACd,WAAO,GAAG,KAAK,GAAG,MAAM,IAAI,KAAK,GAAG,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,UAAU,IAAI,KAAK,iBAAiB,MAAM,GAAG;AAAA,EACrH;AAAA;AAAA,EAGA,MAAM,YAAoB,SAAuB;AAC/C,QAAI,CAAC,KAAK,SAAS,aAAa;AAC9B,WAAK,SAAS,QAAQ;AAAA,IACxB;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,SAAuB;AAC9B,SAAK,SAAS;AACd,SAAK,KAAK,cAAc,WAAW,IAAI;AAAA,EACzC;AAAA;AAAA,EAGA,SAAS,MAAe;AACtB,QAAI,KAAK,OAAQ;AACjB,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,CAAC,KAAM;AACX,SAAK,KAAK,IAAI,iBAAiB,KAAK,MAAM,KAAK,GAAG,SAAS,KAAK,YAAY,IAAI;AAChF,SAAK;AAAA,EACP;AAAA;AAAA,EAGA,UAAU,OAAkB;AAC1B,QAAI,KAAK,UAAU,MAAM,WAAW,EAAG;AACvC,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,CAAC,KAAM;AACX,SAAK,KAAK,IAAI,iBAAiB,MAAM,MAAM,KAAK,GAAG,SAAS,KAAK,YAAY,KAAK;AAClF,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA,EAGA,QAAQ,OAA4B;AAClC,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,CAAC,KAAM;AACX,UAAM,YAAY,QAAQ,EAAE,SAAS,MAAM,QAAQ,IAAI;AACvD,SAAK,KAAK,IAAI,iBAAiB,IAAI,MAAM,KAAK,GAAG,SAAS,KAAK,YAAY,SAAS;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,QAAyC;AACvD,UAAM,KAAK,SAAS;AAEpB,QAAI;AACF,uBAAiB,QAAQ,QAAQ;AAC/B,YAAI,KAAK,OAAQ;AACjB,aAAK,SAAS,IAAI;AAAA,MACpB;AACA,UAAI,CAAC,KAAK,QAAQ;AAChB,aAAK,QAAQ;AAAA,MACf;AAAA,IACF,SAAS,GAAG;AACV,UAAI,CAAC,KAAK,QAAQ;AAChB,aAAK,QAAQ,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIA,YAAkB;AAAA,EAGlB;AAAA,EAEA,aAAmB;AACjB,SAAK,SAAS;AACd,QAAI,CAAC,KAAK,SAAS,aAAa;AAG9B,WAAK,SAAS,QAAQ;AAAA,IACxB;AACA,SAAK,KAAK,cAAc,WAAW,IAAI;AAAA,EACzC;AACF;;;AClIO,IAAM,yBAAN,MAA6B;AAAA,EAC1B,WAAW,oBAAI,IAAwB;AAAA,EAE/C,SAAS,KAAuB;AAC9B,SAAK,SAAS,IAAI,IAAI,GAAG,SAAS,GAAG;AAAA,EACvC;AAAA,EAEA,IAAI,SAAyC;AAC3C,WAAO,KAAK,SAAS,IAAI,OAAO;AAAA,EAClC;AAAA,EAEA,WAAW,KAAuB;AAChC,SAAK,SAAS,OAAO,IAAI,GAAG,OAAO;AAAA,EACrC;AAAA,EAEA,gBAAsB;AACpB,eAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,UAAI,WAAW;AAAA,IACjB;AACA,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA,EAEA,eAAqB;AACnB,eAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,UAAI,IAAI;AACN,YAAI,UAAU;AAAA;AAEd,YAAI,WAAW;AAAA,IACnB;AAAA,EACF;AACF;;;AC9BO,IAAM,yBAAN,MAA6B;AAAA,EAC1B,UAAU;AAAA,EACV,WAAW,oBAAI,IAAwB;AAAA,EAE/C,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAS,KAAuB;AAC9B,SAAK,SAAS,IAAI,IAAI,GAAG,SAAS,GAAG;AAAA,EACvC;AAAA,EAEA,IAAI,SAAyC;AAC3C,WAAO,KAAK,SAAS,IAAI,OAAO;AAAA,EAClC;AAAA,EAEA,WAAW,KAAuB;AAChC,SAAK,SAAS,OAAO,IAAI,GAAG,OAAO;AAAA,EACrC;AAAA,EAEA,gBAAsB;AACpB,eAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,UAAI,WAAW;AAAA,IACjB;AACA,SAAK,SAAS,MAAM;AAAA,EACtB;AACF;;;ACiDA,IAAAC,eAA+C;;;AC3E/C,IAAAC,eAA4C;AAErC,IAAM,gCAAN,cAA4C,0BAAa;AAAA,EAC9D,cAAc;AACZ,UAAM;AACN,SAAK,SAAS,2BAAc,IAAI,KAAM,GAAM;AAAA,EAC9C;AACF;;;AD4FO,IAAM,+BAA+B;AAGrC,IAAM,oCAAoC;AAM1C,IAAM,+BAAyD,CAAC,SAAS;AAC9E,QAAM,MAAM,KAAK,IAAI,SAAS,GAAG,IAAI,MAAM;AAC3C,SAAO,KAAK,MAAM,MAAM,YAAY,KAAK,QAAQ,MAAM,KAAK,mBAAmB;AACjF;AAqBO,IAAe,UAAf,MAAuB;AAAA;AAAA,EAEnB;AAAA;AAAA,EAEA,KAAa,OAAO,WAAW;AAAA,EAC9B;AAAA,EACA;AAAA,EACD,WAAW,IAAI,uBAAuB;AAAA,EACtC,UAAU,IAAI,sBAAsB;AAAA,EACpC,gBAAgB,IAAI,uBAAuB;AAAA,EAC3C,gBAAgB,IAAI,uBAAuB;AAAA,EAE3C,YAAY,IAAI,6BAAsB;AAAA,EACtC,eAAe,IAAI,6BAAkD;AAAA,EAEpE,gBAAmC,CAAC;AAAA,EACtC;AAAA,EAER,YAAY,KAAa,KAAa;AACpC,SAAK,MAAM;AACX,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,IAAI,MAAc;AAChB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAAwC;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,cAAuB;AACzB,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA,EAEU,gBAAgB,MAA2B;AACnD,SAAK,cAAc;AAEnB,SAAK,gBAAgB,IAAI,CAAC,QAAQ,KAAK,eAAe,GAAG,CAAC;AAC1D,SAAK,OAAO,IAAI,CAAC,OAAO;AACtB,WAAK,cAAc;AACnB,WAAK,eAAe;AACpB,WAAK,aAAa,QAAQ,EAAE;AAAA,IAC9B,CAAC;AAED,SAAK,KAAK,cAAc,KAAK,MAAM;AACjC,WAAK,UAAU,QAAQ;AACvB,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,QAAgB,MAAkB,SAA2C;AAChF,UAAM,SAAS,KAAK,SAAS,OAAO;AACpC,UAAM,eAAe,SAAS,sBAC1B,QAAQ,oBAAoB,QAAQ,MAAM,IAC1C,IAAI,gBAAgB,QAAQ,MAAM;AAEtC,UAAM,MAAM;AAAA,MACV,EAAE,QAAQ,QAAQ,WAAW,QAAQ,UAAU,SAAS,cAAc,EAAE;AAAA,MACxE;AAAA,IACF;AACA,iBAAa,oBAAoB;AACjC,QAAI,KAAK,gBAAgB,QAAW;AAClC,WAAK,SAAS,SAAS,YAAY;AACnC,WAAK,YAAY,KAAK,GAAG;AAAA,IAC3B,OAAO;AACL,WAAK,cAAc,KAAK,YAAY;AAAA,IACtC;AAGA,UAAM,SAAS,SAAS;AACxB,QAAI,WAAW,QAAW;AACxB,YAAM,OAAO;AACb,YAAM,MAAM,KAAK;AACjB,YAAM,UAAU,KAAK;AACrB,YAAM,UAAU,MAAM;AACpB,YAAI,QAAQ,OAAO,MAAM,MAAM,QAAW;AACxC,uBAAa,OAAO,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACvD,uBAAa,aAAa;AAC1B,cAAI,KAAK,gBAAgB;AACvB,gBAAI,iBAAiB,OAAO,KAAK,aAAa,MAAM;AAAA,QACxD,OAAO;AACL,gBAAM,MAAM,KAAK,cAAc,UAAU,OAAK,EAAE,WAAW,MAAM;AACjE,cAAI,QAAQ,IAAI;AACd,iBAAK,cAAc,OAAO,KAAK,CAAC;AAChC,yBAAa,OAAO,OAAO,IAAI,MAAM,iBAAiB,CAAC;AACvD,yBAAa,aAAa;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AACA,aAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAExD,mBAAa,OAAO,QACjB,KAAK,MAAM,OAAO,oBAAoB,SAAS,OAAO,CAAC,EACvD,MAAM,MAAM,OAAO,oBAAoB,SAAS,OAAO,CAAC;AAAA,IAC7D;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,QAAgB,MAAwB;AACjD,QAAI,KAAK,gBAAgB,OAAW;AACpC,UAAM,MAAM,iBAAiB,EAAE,QAAQ,QAAQ,WAAW,EAAE,GAAG,IAAI;AACnE,SAAK,YAAY,KAAK,GAAG;AAAA,EAC3B;AAAA,EAEA,QAAc;AACZ,SAAK,eAAe;AACpB,SAAK,cAAc,cAAc;AACjC,SAAK,cAAc,cAAc;AACjC,eAAW,QAAQ,KAAK,eAAe;AACrC,UAAI,CAAC,KAAK,OAAO;AACf,aAAK,OAAO,OAAO,IAAI,MAAM,cAAc,CAAC;AAC9C,WAAK,aAAa;AAAA,IACpB;AACA,SAAK,cAAc,SAAS;AAC5B,SAAK,SAAS,UAAU,IAAI,MAAM,cAAc,CAAC;AACjD,SAAK,SAAS,cAAc;AAC5B,SAAK,aAAa,MAAM;AACxB,SAAK,KAAK,MAAM,OAAO,KAAK,GAAG;AAAA,EACjC;AAAA;AAAA,EAGU,qBAA2B;AACnC,QAAI,KAAK,cAAc,WAAW,KAAK,KAAK,gBAAgB,OAAW;AACvE,eAAW,QAAQ,KAAK,eAAe;AACrC,WAAK,SAAS,SAAS,IAAI;AAC3B,WAAK,YAAY,KAAK,KAAK,iBAAiB;AAAA,IAC9C;AACA,SAAK,cAAc,SAAS;AAAA,EAC9B;AAAA;AAAA,EAGU,qBAAqB,YAAmC;AAAA,EAGlE;AAAA,EAEQ,eAAe,KAAmB;AACxC,UAAM,EAAE,SAAS,KAAK,IAAI,mBAAmB,GAAG;AAChD,UAAM,SAAS,QAAQ,UAAU;AAGjC,QAAI,WAAW,eAAe,WAAW;AACvC,YAAM,YAAY,KAAK,CAAC;AACxB,UAAI,cAAc;AAChB,aAAK,qBAAqB,SAAS;AACrC;AAAA,IACF;AAGA,QAAI,OAAO,WAAW,MAAM,GAAG;AAC7B,WAAK,KAAK,kBAAkB,OAAO,SAAS,MAAM,IAAI;AACtD;AAAA,IACF;AAGA,SAAK,eAAe,SAAS,IAAI;AAAA,EACnC;AAAA,EAEQ,eAAe,SAAqB,MAAuB;AACjE,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,YAAY,QAAQ,aAAa;AAEvC,UAAM,OAAO,IAAI,eAAe,WAAW,QAAQ,IAAI;AACvD,UAAM,YAAY,KAAK,KAAK,YAAY,aAAa,MAAM;AAG3D,UAAM,WAAW,WAAW,WAAW;AACvC,QAAI,CAAC,UAAU;AACb,WAAK,QAAQ,SAAS,IAAI;AAAA,IAC5B;AAGA,UAAM,cAAc,KAAK,KAAK;AAC9B,QAAI,gBAAgB,QAAW;AAC7B,YAAM,YAAY;AAChB,YAAI;AACF,gBAAM,UAAU,KAAK,gBAAgB,SACjC,EAAE,eAAe,MAAe,QAAQ,WAAW,YAAY,KAAK,YAAY,IAChF;AACJ,gBAAM,SAAS,MAAM,YAAY,SAAS,QAAQ,MAAM,OAAO;AAC/D,cAAI,WAAW,WAAW,QAAQ,KAAK,gBAAgB,QAAW;AAEhE,kBAAM,SAAS,IAAI,gBAAgB,IAAI;AACvC,iBAAK,cAAc,SAAS,MAAM;AAClC,iBAAK,KAAK,iBAAiB,GAAG,KAAK,aAAa,WAAW,OAAO,MAAM,CAAC;AACzE,iBAAK,OAAO,UAAU,MAAgC;AAAA,UACxD,WAAW,CAAC,YAAY,KAAK,gBAAgB,QAAW;AACtD,iBAAK,KAAK,iBAAiB,GAAG,KAAK,aAAa,WAAW,MAAM;AAAA,UACnE;AAAA,QACF,SAAS,GAAG;AACV,cAAI,CAAC,YAAY,KAAK,gBAAgB,QAAW;AAC/C,iBAAK,KAAK,iBAAiB,MAAM,KAAK,aAAa,WAAW,CAAC;AAAA,UACjE;AAAA,QACF,UAAE;AACA,cAAI,CAAC,UAAU;AACb,iBAAK,QAAQ,OAAO,SAAS;AAAA,UAC/B;AAAA,QACF;AAAA,MACF,GAAG;AAAA,IACL;AAAA,EACF;AAAA,EAEQ,kBAAwB;AAC9B,SAAK,kBAAkB,YAAY,MAAM;AACvC,UAAI,KAAK,gBAAgB,QAAW;AAClC,aAAK,KAAK,iBAAiB,UAAU,KAAK,aAAa,KAAK,SAAS,cAAc,CAAC;AAAA,MACtF;AAAA,IACF,GAAG,IAAM;AAAA,EACX;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,KAAK,oBAAoB,QAAW;AACtC,oBAAc,KAAK,eAAe;AAClC,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AACF;AAGO,IAAW,wBAAX,kBAAWC,2BAAX;AACL,EAAAA,8CAAA,kBAAe,KAAf;AACA,EAAAA,8CAAA,gBAAa,KAAb;AACA,EAAAA,8CAAA,eAAY,KAAZ;AAHgB,SAAAA;AAAA,GAAA;AAOX,IAAM,gBAAN,cAA4B,QAAQ;AAAA,EACjC,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA;AAAA,EAGf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA;AAAA,EAET,wBAAkD;AAAA,EACzC,cAAc,IAAI,6BAAsB;AAAA;AAAA,EAExC,oBAAoB,IAAI,6BAAoC;AAAA,EAC5D,mBAAmB,IAAI,8BAA8B;AAAA,EACrD,sBAAsB,IAAI,6BAAsB;AAAA,EAEzD,IAAI,eAAuB;AAAE,WAAO,KAAK;AAAA,EAAe;AAAA,EAEhD,iBAAiB,OAAqB;AAC5C,QAAI,KAAK,kBAAkB,MAAO;AAClC,SAAK,gBAAgB;AACrB,SAAK,oBAAoB,QAAQ;AAAA,EACnC;AAAA,EAEA,YAAY,KAAa,KAAa,qBAA8B;AAClE,UAAM,KAAK,GAAG;AACd,SAAK,WAAW,gBAAgB,KAAK,EAAE;AACvC,SAAK,sBAAsB,uBAAuB;AAAA,EACpD;AAAA,EAEA,IAAI,iBAAwC;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,YAAY,MAA2B;AAIrC,SAAK,gBAAgB,IAAI;AACzB,SAAK,WAAW,IAAI;AAAA,EACtB;AAAA;AAAA,EAGA,MAAM,IAAI,WAA2D;AACnE,UAAM,UAAU,KAAK,sBAAsB,IAAI;AAC/C,WAAO,CAAC,KAAK,WAAW;AACtB,UAAI,gBAAgB;AACpB,UAAI,kBAAkB;AACtB,UAAI;AACF,cAAM,KAAK,YAAY,OAAO,KAAK,IAAI,UAAU,OAAO;AACxD,cAAM,OAAO,IAAI,uBAAuB,EAAE;AAC1C,aAAK,kBAAkB;AAGvB,aAAK,OAAO,IAAI,CAAC,OAAO;AACtB,0BAAgB,GAAG;AACnB,4BAAkB,GAAG;AAAA,QACvB,CAAC;AAKD,aAAK,oBAAoB,IAAI,2BAA+B;AAC5D,aAAK,gBAAgB,IAAI;AAKzB,aAAK,cAAc;AAInB,cAAM,kBAAkB,IAAI,QAAe,CAAC,GAAG,WAC7C,KAAK,OAAO,IAAI,MAAM,OAAO,IAAI,MAAM,mBAAmB,CAAC,CAAC,CAAC;AAC/D,wBAAgB,MAAM,MAAM;AAAA,QAAC,CAAC;AAC9B,cAAM,QAAQ,KAAK,CAAC,KAAK,eAAe,eAAe,CAAC;AAGxD,aAAK,KAAK,iBAAiB,UAAU,MAAM,KAAK,IAAI,KAAK,KAAK,OAAO,EAAE,KAAK,eAAe;AAC3F,cAAM,kBAAkB,MAAM,QAAQ,KAAK;AAAA,UACzC,KAAK,kBAAkB;AAAA,UACvB;AAAA,QACF,CAAC;AACD,aAAK,oBAAoB;AAGzB,cAAM,cAAc,gBAAgB;AACpC,YAAI,gBAAgB;AACpB,YAAI,gBAAgB,QAAW;AAC7B,0BAAgB,KAAK,qBAAqB,UAAa,KAAK,qBAAqB;AACjF,cAAI,eAAe;AAEjB,iBAAK,QAAQ,MAAM;AACnB,iBAAK,YAAY,QAAQ;AAAA,UAC3B;AACA,eAAK,mBAAmB;AAAA,QAC1B;AAIA,aAAK,cAAc;AACnB,aAAK,kBAAkB;AACvB,aAAK,YAAY;AACjB,aAAK,WAAW,aAAa;AAG7B,cAAM,IAAI,QAAc,OAAK,KAAK,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;AAAA,MACzD,QAAQ;AAAA,MAER;AAEA,WAAK,kBAAkB;AACvB,UAAI,KAAK,UAAW;AAGpB,UAAI,kBAAkB,mCAAmC;AACvD,aAAK,kBAAkB,QAAQ,EAAE,QAAQ,gBAAgB,CAAC;AAC1D,aAAK,YAAY;AACjB;AAAA,MACF;AAEA,WAAK;AACL,YAAM,QAAQ,KAAK,iBAAiB,SAAS,KAAK,SAAS;AAC3D,UAAI,MAAM,iBAAiB;AAAE,aAAK,YAAY;AAAM;AAAA,MAAO;AAC3D,WAAK,iBAAiB,MAAM,MAAM;AAClC,UAAI;AAAE,cAAM,MAAM;AAAA,MAAS,UAC3B;AAAU,aAAK,iBAAiB,CAAC;AAAA,MAAG;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,WAAW,gBAA+B;AAChD,QAAI,KAAK,gBAAgB,OAAW;AACpC,UAAM,OAAO,KAAK;AAGlB,QAAI,gBAAgB;AAClB,WAAK,cAAc,cAAc;AAAA,IACnC,OAAO;AACL,WAAK,cAAc,aAAa;AAAA,IAClC;AAGA,UAAM,eAAe,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC;AAC/C,eAAW,QAAQ,cAAc;AAC/B,UAAI,CAAC,KAAK,cAAc,KAAK,OAAO,aAAa;AAE/C,aAAK,aAAa;AAClB,aAAK,SAAS,OAAO,KAAK,MAAM;AAAA,MAClC,OAAO;AAEL,aAAK,KAAK,KAAK,iBAAiB;AAAA,MAClC;AAAA,IACF;AAGA,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEmB,qBAAqB,WAAkC;AACxE,SAAK,mBAAmB,QAAQ,SAAS;AAAA,EAC3C;AAAA,EAES,QAAc;AACrB,SAAK,YAAY;AACjB,UAAM,MAAM;AAAA,EACd;AACF;AAGO,IAAM,gBAAN,cAA4B,QAAQ;AAAA,EACzC,YAAY,KAAa,KAAa;AACpC,UAAM,KAAK,GAAG;AAAA,EAChB;AAAA;AAAA,EAGA,OAAO,MAA2B;AAChC,SAAK,gBAAgB,IAAI;AAAA,EAC3B;AAAA,EAEmB,qBAAqB,YAAmC;AAEzE,QAAI,KAAK,gBAAgB,QAAW;AAClC,WAAK,KAAK,iBAAiB,UAAU,KAAK,aAAa,KAAK,IAAI,KAAK,KAAK,OAAO,CAAC;AAAA,IACpF;AAAA,EACF;AACF;AAMA,SAAS,gBAAgB,MAAsB;AAC7C,QAAM,MAAM,KAAK,QAAQ,MAAM,EAAE;AACjC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAG/B,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACvC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACvC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACvC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AAEvC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,EAAE,GAAG,EAAE;AACzC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,GAAG,EAAE,GAAG,EAAE;AAExC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,EAAE,GAAG,EAAE;AACzC,QAAM,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,EAAE,GAAG,EAAE;AAEzC,WAAS,IAAI,GAAG,IAAI,IAAI;AACtB,UAAM,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAErD,QAAM,SAAS,OAAO,aAAa,GAAG,KAAK;AAC3C,SAAO,KAAK,MAAM,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC/E;;;AEniBO,IAAM,iBAAN,MAAqB;AAAA,EAClB,WAAW,oBAAI,IAAwE;AAAA,EAE/F,SAAS,KAAoB,MAA4B;AACvD,eAAW,aAAa,IAAI,QAAQ,OAAO,GAAG;AAC5C,YAAM,KAAK,KAAK,UAAU,IAAI;AAC9B,UAAI,CAAC,GAAI;AACT,WAAK,SAAS,IAAI,eAAe,SAAS,GAAG,EAAE,KAAK,WAAW,GAAG,CAAC;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,YAAoB,MAAiB,SAAgD;AAClG,UAAM,QAAQ,KAAK,SAAS,IAAI,UAAU;AAC1C,QAAI,UAAU,OAAW,OAAM,IAAI,MAAM,qBAAqB,UAAU,EAAE;AAI1E,QAAI,YAAY,UAAa,MAAM,IAAI,eAAe,GAAG;AACvD,aAAO,MAAM,MAAM,GAAG,GAAG,MAAM,OAAO;AAAA,IACxC;AACA,WAAO,MAAM,MAAM,GAAG,GAAG,IAAI;AAAA,EAC/B;AAAA,EAEA,aAAa,YAA8C;AACzD,WAAO,KAAK,SAAS,IAAI,UAAU,GAAG;AAAA,EACxC;AACF;;;AC3CA,IAAM,eAAe,uBAAO,IAAI,mBAAmB;AACnD,IAAM,eAAe,uBAAO,IAAI,mBAAmB;AAa5C,SAAS,eAAe,KAAmF;AAChH,SAAQ,IAAY,OAAO,QAAQ,IAAI,YAAY;AACrD;AAGO,SAAS,eAAe,KAAoE;AACjG,SAAQ,IAAY,OAAO,QAAQ,IAAI,YAAY;AACrD;AAGO,SAAS,WAAW,aAAqB;AAC9C,SAAO,SACL,SACA,SACM;AACN,UAAM,OAAsB,QAAQ,SAAiB,YAAY,MAAM,CAAC;AACxE,SAAK,OAAO;AAAA,EACd;AACF;AAGO,SAAS,UAAU,SAAgC;AACxD,SAAO,SACL,QACA,SACuC;AACvC,UAAM,aAAa,OAAO,QAAQ,IAAI;AACtC,UAAM,UAAwC,QAAQ,SAAiB,YAAY,MAAM,CAAC;AAC1F,YAAQ,UAAU,IAAI;AAAA,MACpB,GAAG,QAAQ,UAAU;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB,SAAS,SAAS;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AACF;;;AChCO,IAAM,SAAN,MAAa;AAAA,EACT;AAAA,EACA,QAAQ,oBAAI,IAAqB;AAAA,EACjC;AAAA,EACA,mBAAmB,IAAI,oBAAoB;AAAA,EACpD,oBAA0C,IAAI,qBAAqB;AAAA,EAEnE,YAAY,OAAgB;AAC1B,SAAK,QAAQ,SAAS,OAAO,WAAW;AACxC,SAAK,cAAc,IAAI,eAAe;AAAA,EACxC;AAAA,EAEA,QAAQ,MAAqB;AAC3B,SAAK,MAAM,IAAI,KAAK,KAAK,IAAI;AAAA,EAC/B;AAAA,EAEA,WAAW,KAAmB;AAC5B,UAAM,OAAO,KAAK,MAAM,IAAI,GAAG;AAC/B,QAAI,SAAS,QAAW;AACtB,WAAK,MAAM;AACX,WAAK,MAAM,OAAO,GAAG;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGA,QAAQ,KAAsB;AAC5B,QAAI,OAAO,KAAK,MAAM,IAAI,GAAG;AAC7B,QAAI,KAAM,QAAO;AACjB,WAAO,IAAI,WAAW,WAAW,IAC7B,IAAI,cAAc,MAAM,GAAG,IAC3B,IAAI,cAAc,MAAM,GAAG;AAC/B,SAAK,QAAQ,IAAI;AACjB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,cAAc,KAA4B;AACxC,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA,EAGA,cAAc,KAA4B;AACxC,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA,EAGA,WAAW,eAAuE,MAA4B;AAC5G,UAAM,MAAM,KAAK,mBAAmB,aAAa;AACjD,UAAM,cAA8B,CAAC;AACrC,eAAW,aAAa,IAAI,QAAQ,OAAO,GAAG;AAC5C,YAAM,KAAK,KAAK,UAAU,IAAI;AAC9B,UAAI,CAAC,GAAI;AACT,kBAAY,UAAU,IAAI,IAAI,UAAU,eAAe,IACnD,KAAK,kBAAkB,WAAW,IAAI,IAAc,IACpD;AAAA,IACN;AACA,SAAK,YAAY,SAAS,KAAK,WAAW;AAAA,EAC5C;AAAA;AAAA,EAGA,UAA4B,MAAe,eAA0E;AACnH,UAAM,MAAM,KAAK,mBAAmB,aAAa;AAGjD,UAAM,YAAY,oBAAI,IAAmC;AACzD,eAAW,aAAa,IAAI,QAAQ,OAAO,GAAG;AAC5C,UAAI,aAAa,UAAU,IAAI,UAAU,IAAI;AAC7C,UAAI,CAAC,YAAY;AAAE,qBAAa,oBAAI,IAAI;AAAG,kBAAU,IAAI,UAAU,MAAM,UAAU;AAAA,MAAG;AACtF,iBAAW,IAAI,UAAU,UAAU,KAAK,oBAAoB,MAAM,SAAS,CAAC;AAAA,IAC9E;AAGA,UAAM,UAAU,oBAAI,IAAsB;AAC1C,eAAW,CAAC,MAAM,UAAU,KAAK,WAAW;AAC1C,UAAI,WAAW,SAAS,GAAG;AACzB,gBAAQ,IAAI,MAAM,WAAW,OAAO,EAAE,KAAK,EAAE,KAAM;AAAA,MACrD,OAAO;AACL,gBAAQ,IAAI,MAAM,IAAI,SAAoB;AACxC,gBAAM,KAAK,WAAW,IAAI,KAAK,MAAM;AACrC,cAAI,CAAC,GAAI,OAAM,IAAI,MAAM,kBAAkB,IAAI,YAAY,KAAK,MAAM,YAAY;AAClF,iBAAO,GAAG,GAAG,IAAI;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,IAAI,MAAM,CAAC,GAAQ;AAAA,MACxB,KAAK,CAAC,SAAS,SAAS,OAAO,SAAS,WAAW,QAAQ,IAAI,IAAI,IAAI;AAAA,IACzE,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,eAAW,QAAQ,KAAK,MAAM,OAAO,EAAG,MAAK,MAAM;AACnD,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA;AAAA,EAGU,iBAAiB,KAA0D;AACnF,UAAM,UAAU,eAAe,GAAG;AAClC,QAAI,YAAY,OAAW,OAAM,IAAI,MAAM,6CAA6C;AAExF,UAAM,cAAc,eAAe,GAAG,KAAK,CAAC;AAC5C,UAAM,UAAU,oBAAI,IAA0B;AAE9C,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,YAAM,eAAe,KAAK,WAAW;AACrC,YAAM,SAAS,GAAG,IAAI,IAAI,YAAY;AACtC,cAAQ,IAAI,QAAQ;AAAA,QAClB;AAAA,QACA,aAAa,QAAQ;AAAA,QACrB,UAAU,KAAK;AAAA,QACf;AAAA,QACA,YAAY;AAAA,QACZ,QAAQ,KAAK,YAAY,QAAQ;AAAA,QACjC,QAAQ,KAAK,YAAY,QAAQ;AAAA,MACnC,CAAC;AAAA,IACH;AAEA,WAAO,EAAE,MAAM,QAAQ,MAAM,QAAQ;AAAA,EACvC;AAAA;AAAA,EAGU,mBAAmB,eAAsF;AACjH,QAAI,OAAO,kBAAkB;AAC3B,aAAO,KAAK,iBAAiB,aAAa;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,kBACR,YACA,IAAqC,OACJ;AACjC,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,oBACR,MAAe,WACkB;AACjC,UAAM,WAAW,eAAe,SAAS;AAGzC,QAAI,UAAU,QAAQ;AACpB,aAAO,IAAI,SAAoB;AAC7B,cAAM,WAAW,KAAK,MAAM,GAAG,UAAU,QAAQ;AACjD,aAAK,WAAW,UAAU,QAAQ;AAAA,MACpC;AAAA,IACF;AAGA,QAAI,UAAU,QAAQ;AACpB,aAAO,UAAU,SAAoB;AACnC,cAAM,WAAW,KAAK,MAAM,GAAG,UAAU,QAAQ;AACjD,cAAM,eAAe,KAAK,KAAK,UAAU,QAAQ;AACjD,cAAM,SAAS,MAAM,aAAa,OAAO;AACzC,cAAM,MAAM,eAAe,MAAM;AACjC,YAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mCAAmC,KAAK,UAAU,MAAM,CAAC,EAAE;AACrF,cAAM,SAAS,IAAI,UAAU,KAAK,IAAI;AACtC,aAAK,cAAc,SAAS,MAAM;AAClC,eAAO;AAAA,MACT;AAAA,IACF;AAGA,UAAM,cAA0C,UAAU,eAAe,IACrE,EAAE,YAAY,UAAU,WAAW,IACnC;AAEJ,WAAO,UAAU,SAAoB;AACnC,YAAM,WAAW,KAAK,MAAM,GAAG,UAAU,QAAQ;AACjD,YAAM,eAAe,KAAK,KAAK,UAAU,UAAU,WAAW;AAC9D,YAAM,SAAS,MAAM,aAAa,OAAO;AACzC,aAAO,kBAAkB,QAAQ,IAAI;AAAA,IACvC;AAAA,EACF;AACF;;;ACpMO,SAAS,gBACd,MACA,YACG;AAEH,QAAM,YAAY,oBAAI,IAAuC;AAC7D,aAAW,aAAa,WAAW,QAAQ,OAAO,GAAG;AACnD,QAAI,aAAa,UAAU,IAAI,UAAU,IAAI;AAC7C,QAAI,CAAC,YAAY;AAAE,mBAAa,oBAAI,IAAI;AAAG,gBAAU,IAAI,UAAU,MAAM,UAAU;AAAA,IAAG;AACtF,eAAW,IAAI,UAAU,UAAU,SAAS;AAAA,EAC9C;AAGA,QAAM,UAAU,oBAAI,IAAsB;AAC1C,aAAW,CAAC,MAAM,UAAU,KAAK,WAAW;AAC1C,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,YAAY,WAAW,OAAO,EAAE,KAAK,EAAE;AAC7C,cAAQ,IAAI,MAAM,aAAa,MAAM,SAAS,CAAC;AAAA,IACjD,OAAO;AACL,YAAM,MAAM,oBAAI,IAAsB;AACtC,iBAAW,CAAC,UAAU,SAAS,KAAK,YAAY;AAC9C,YAAI,IAAI,UAAU,aAAa,MAAM,SAAS,CAAC;AAAA,MACjD;AACA,cAAQ,IAAI,MAAM,IAAI,SAAoB;AACxC,cAAM,KAAK,IAAI,IAAI,KAAK,MAAM;AAC9B,YAAI,CAAC,GAAI,OAAM,IAAI,MAAM,kBAAkB,IAAI,YAAY,KAAK,MAAM,YAAY;AAClF,eAAO,GAAG,GAAG,IAAI;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,CAAC,GAAQ;AAAA,IACxB,IAAI,SAAS,MAAM;AACjB,UAAI,OAAO,SAAS,SAAU,QAAO;AACrC,aAAO,QAAQ,IAAI,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,MAAe,WAAmC;AACtE,SAAO,UAAU,SAAoB;AACnC,UAAM,WAAW,KAAK,MAAM,GAAG,UAAU,QAAQ;AACjD,UAAM,WAAW,eAAe,SAAS;AACzC,UAAM,UAAU,UAAU,eAAe,IAAI,EAAE,YAAY,UAAU,WAAW,IAAI;AACpF,UAAM,eAAe,KAAK,KAAK,UAAU,UAAU,OAAO;AAC1D,WAAO,aAAa,OAAO;AAAA,EAC7B;AACF;;;ACpEO,IAAW,mBAAX,kBAAWC,sBAAX;AACL,EAAAA,oCAAA,eAAY,KAAZ;AACA,EAAAA,oCAAA,sBAAmB,KAAnB;AACA,EAAAA,oCAAA,kBAAe,KAAf;AACA,EAAAA,oCAAA,mBAAgB,KAAhB;AAJgB,SAAAA;AAAA,GAAA;AAaX,SAAS,YAAY,OAA8B;AACxD,SAAO,MAAM,SAAS,qBAA8B,MAAM,SAAS;AACrE;AAEO,SAAS,gBAAgB,OAA8B;AAC5D,SAAO,MAAM,SAAS;AACxB;AAEO,SAAS,oBAAoB,OAAqB,eAAe,OAAe;AACrF,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,UAAI,MAAM,eAAe;AACvB;AACF,aAAO;AAAA,EACX;AACA,MAAI,MAAM,aAAa,QAAQ,CAAC;AAC9B,WAAO;AAET,MAAI,UAAU,MAAM,UAAU,QAAQ,KAAK;AAC3C,MAAI,EAAE,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,GAAG;AAC1E,eAAW;AACb,SAAO,mBAAmB;AAC5B;;;ACxCA,IAAAC,eAAgC;AAIhC,IAAM,wBAAwB;AAC9B,IAAM,2BAA2B;AACjC,IAAM,oBAAoB;AAEnB,IAAM,sBAAN,MAA0B;AAAA,EACtB;AAAA,EACA,eAAe,IAAI,6BAA8B;AAAA,EAElD;AAAA,EACA,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA,eAAe,MAAY;AACjC,SAAK,gBAAgB;AACrB,SAAK,eAAe,KAAK,IAAI;AAC7B,SAAK,aAAa;AAClB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,kBAAkB,CAAC,OAA+C;AACxE,UAAM,eAAe,KAAK;AAC1B,SAAK,gBAAgB;AACrB,SAAK,aAAa,IAAI,MAAM,sBAAsB,GAAG,MAAM,UAAU,GAAG,IAAI,GAAG;AAC/E,QAAI,cAAc;AAEhB,WAAK,kBAAkB,KAAK,IAAI;AAAA,IAClC;AAGA,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,yBAAyB,MAAY;AAC3C,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,yBAAyB,MAAY;AAC3C,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,YAAY,MAAqB;AAC/B,SAAK,OAAO;AACZ,SAAK,gBAAgB,KAAK;AAE1B,QAAI,KAAK,eAAe;AACtB,WAAK,eAAe,KAAK,IAAI;AAC7B,WAAK,SAAS,EAAE,6BAAsC,cAAc,EAAE;AAAA,IACxE,OAAO;AACL,WAAK,kBAAkB,KAAK,IAAI;AAChC,WAAK,SAAS,EAAE,gCAAyC,cAAc,EAAE;AAAA,IAC3E;AAEA,SAAK,UAAU,IAAI,KAAK,YAAY;AACpC,SAAK,aAAa,IAAI,KAAK,eAAe;AAC1C,SAAK,oBAAoB,IAAI,KAAK,sBAAsB;AACxD,SAAK,iBAAiB,oBAAoB,IAAI,KAAK,sBAAsB;AAGzE,SAAK,mBAAmB,KAAK,gBAAgB,wBAAwB,wBAAwB;AAAA,EAC/F;AAAA,EAEA,IAAI,QAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,WAAW,QAAW;AAC7B,mBAAa,KAAK,MAAM;AACxB,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,KAAK,UAAU,OAAO,KAAK,YAAY;AAC5C,SAAK,KAAK,aAAa,OAAO,KAAK,eAAe;AAClD,SAAK,KAAK,oBAAoB,OAAO,KAAK,sBAAsB;AAChE,SAAK,KAAK,iBAAiB,oBAAoB,OAAO,KAAK,sBAAsB;AAAA,EACnF;AAAA,EAEQ,aAAmB;AACzB,QAAI,KAAK,WAAW,QAAW;AAC7B,mBAAa,KAAK,MAAM;AACxB,WAAK,SAAS;AAAA,IAChB;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI;AAEJ,QAAI,KAAK,eAAe;AACtB,YAAM,eAAe,MAAM,KAAK;AAChC,UAAI,eAAe,uBAAuB;AACxC,mBAAW,EAAE,6BAAsC,cAAc,EAAE;AACnE,aAAK,mBAAmB,wBAAwB,YAAY;AAAA,MAC9D,OAAO;AACL,mBAAW,EAAE,yBAAkC,cAAc,EAAE;AAAA,MACjE;AAAA,IACF,OAAO;AACL,YAAM,kBAAkB,MAAM,KAAK;AACnC,UAAI,kBAAkB,0BAA0B;AAC9C,mBAAW,EAAE,gCAAyC,WAAW,KAAK,YAAY,cAAc,EAAE;AAClG,aAAK,mBAAmB,2BAA2B,eAAe;AAAA,MACpE,OAAO;AACL,cAAM,eAAe,KAAK,KAAK;AAC/B,cAAM,eAAe,eAAe,IAAI,KAAK,IAAI,GAAG,eAAe,GAAG,IAAI;AAC1E,YAAI,eAAe,mBAAmB;AACpC,qBAAW,EAAE,4BAAqC,WAAW,KAAK,YAAY,cAAc,EAAE;AAAA,QAChG,OAAO;AACL,qBAAW,EAAE,4BAAqC,WAAW,KAAK,YAAY,aAAa;AAE3F,eAAK,mBAAmB,KAAK,IAAI,KAAM,eAAe,iBAAiB,CAAC;AAAA,QAC1E;AAAA,MACF;AAAA,IACF;AAEA,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA,EAEQ,UAAU,UAA8B;AAC9C,UAAM,MAAM,KAAK;AACjB,QAAI,IAAI,SAAS,SAAS,QACrB,IAAI,cAAc,SAAS,aAC3B,IAAI,iBAAiB,SAAS;AACjC;AAEF,SAAK,SAAS;AACd,SAAK,aAAa,QAAQ,QAAQ;AAAA,EACpC;AAAA,EAEQ,mBAAmB,SAAuB;AAChD,QAAI,KAAK,WAAW,OAAW,cAAa,KAAK,MAAM;AACvD,SAAK,SAAS,WAAW,MAAM;AAC7B,WAAK,SAAS;AACd,WAAK,WAAW;AAAA,IAClB,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,EACzB;AACF;","names":["RpcCallTypeId","import_core","import_core","import_core","import_core","import_core","import_core","RpcPeerConnectionKind","RpcPeerStateKind","import_core"]}
|