1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-31 00:33:33 +00:00

feat: implement function calling

This commit is contained in:
Andreas Coroiu
2025-10-27 09:38:33 +01:00
parent c119fd0f4e
commit c415e2ae48
3 changed files with 82 additions and 21 deletions

View File

@@ -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);
}
});
},
});
}

View File

@@ -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");

View File

@@ -34,6 +34,28 @@ export class RpcServer<T> {
}
}
if (command.method === "call") {
const target = this.references.get<any>(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}` };
}