From c415e2ae4883d9ec0294b8dcad997a211cd7393f Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 27 Oct 2025 09:38:33 +0100 Subject: [PATCH] feat: implement function calling --- .../src/platform/services/sdk/rpc/proxies.ts | 79 ++++++++++++++----- .../src/platform/services/sdk/rpc/rpc.spec.ts | 2 +- .../src/platform/services/sdk/rpc/server.ts | 22 ++++++ 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/libs/common/src/platform/services/sdk/rpc/proxies.ts b/libs/common/src/platform/services/sdk/rpc/proxies.ts index 88b99e58f37..f47fbf0c6a8 100644 --- a/libs/common/src/platform/services/sdk/rpc/proxies.ts +++ b/libs/common/src/platform/services/sdk/rpc/proxies.ts @@ -31,7 +31,7 @@ function ProxiedReference( } // console.log(`Accessing ${reference.objectType}.${propertyName}`); - return RpcPropertyReference.create(channel, target, propertyName); + return RpcPropertyReference(channel, { objectReference: target, propertyName }); }, }); } @@ -39,23 +39,31 @@ function ProxiedReference( /** * A reference to a specific property on a remote object. */ -export class RpcPropertyReference { - static create( - channel: RpcRequestChannel, - objectReference: RpcObjectReference, - propertyName: string, - ): RpcPropertyReference { - return ProxiedReferenceProperty( - channel, - new RpcPropertyReference(objectReference, propertyName), - ); - } +type RpcPropertyReference = { + objectReference: RpcObjectReference; + propertyName: string; +}; - private constructor( - public objectReference: RpcObjectReference, - public propertyName: string, - ) {} -} +/** + * A reference to a specific property on a remote object. + */ +// export class RpcPropertyReference { +// static create( +// channel: RpcRequestChannel, +// objectReference: RpcObjectReference, +// propertyName: string, +// ): RpcPropertyReference { +// return ProxiedReferenceProperty( +// channel, +// new RpcPropertyReference(objectReference, propertyName), +// ); +// } + +// private constructor( +// public objectReference: RpcObjectReference, +// public propertyName: string, +// ) {} +// } /** * A sub-proxy for a specific property of a proxied reference @@ -66,13 +74,22 @@ export class RpcPropertyReference { * If this references a property then they'll try to await the value, triggering the `get` trap * when they access the `then` property. */ -function ProxiedReferenceProperty(channel: RpcRequestChannel, reference: RpcPropertyReference) { - return new Proxy(reference, { +function RpcPropertyReference(channel: RpcRequestChannel, reference: RpcPropertyReference) { + const target = () => {}; + Object.defineProperty(target, "name", { value: `RpcPropertyReference`, configurable: true }); + target.objectReference = reference.objectReference; + target.propertyName = reference.propertyName; + + return new Proxy(target, { get(_target, propertyName: string) { // console.log( // `Accessing ${reference.objectReference.objectType}.${reference.propertyName}.${propertyName}`, // ); + if (propertyName === "call") { + return undefined; + } + if (propertyName !== "then") { throw new Error(`Cannot access property '${propertyName}' on remote proxy synchronously`); } @@ -100,6 +117,28 @@ function ProxiedReferenceProperty(channel: RpcRequestChannel, reference: RpcProp })().then(onFulfilled, onRejected); }; }, - apply(_target, _thisArg, argArray: unknown[]) {}, + apply(_target, _thisArg, argArray: unknown[]) { + // console.log(`Calling ${reference.objectReference.objectType}.${reference.propertyName}`); + + // Handle method call + const command: Command = { + method: "call", + referenceId: reference.objectReference.referenceId, + propertyName: reference.propertyName, + args: argArray, + }; + + return channel.sendCommand(command).then((result) => { + if (result.status === "error") { + throw new Error(`RPC Error: ${result.error}`); + } + + if (result.result.type === "value") { + return result.result.value; + } else if (result.result.type === "reference") { + return RpcObjectReference.create(channel, result.result.referenceId); + } + }); + }, }); } diff --git a/libs/common/src/platform/services/sdk/rpc/rpc.spec.ts b/libs/common/src/platform/services/sdk/rpc/rpc.spec.ts index 8f96b29728f..7a3a0b74f98 100644 --- a/libs/common/src/platform/services/sdk/rpc/rpc.spec.ts +++ b/libs/common/src/platform/services/sdk/rpc/rpc.spec.ts @@ -23,7 +23,7 @@ describe("RpcServer", () => { expect(value).toBe(42); }); - it.skip("calls sync function and returns value", async () => { + it("calls sync function and returns value", async () => { const remoteInstance = await firstValueFrom(client.getRoot()); const result = await remoteInstance.greet("World"); diff --git a/libs/common/src/platform/services/sdk/rpc/server.ts b/libs/common/src/platform/services/sdk/rpc/server.ts index f206d106989..98919732731 100644 --- a/libs/common/src/platform/services/sdk/rpc/server.ts +++ b/libs/common/src/platform/services/sdk/rpc/server.ts @@ -34,6 +34,28 @@ export class RpcServer { } } + if (command.method === "call") { + const target = this.references.get(command.referenceId); + if (!target) { + return { status: "error", error: `[RPC] Reference ID ${command.referenceId} not found` }; + } + + try { + const method = target[command.propertyName]; + if (typeof method !== "function") { + return { + status: "error", + error: `[RPC] Property ${command.propertyName} is not a function`, + }; + } + + const result = method.apply(target, command.args); + return { status: "success", result: { type: "value", value: result } }; + } catch (error) { + return { status: "error", error }; + } + } + return { status: "error", error: `Unknown command method: ${command.method}` }; }