1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-02 09:43:29 +00:00

feat: replace everything with new proxies

This commit is contained in:
Andreas Coroiu
2025-11-03 11:11:38 +01:00
parent 41805e84b6
commit 77fbdb53d1
18 changed files with 207 additions and 530 deletions

View File

@@ -35,12 +35,12 @@ export class BrowserRemoteSdkService implements RemoteSdkService {
);
},
sendCommand: async (command) => {
this.logService.debug("[RemoteSdkService]: Sending command", command);
const response = (await BrowserApi.sendMessageWithResponse("sdk.request", {
const jsonResponse = (await BrowserApi.sendMessageWithResponse("sdk.request", {
type: "RemoteSdkRequest",
command,
} satisfies RemoteSdkRequest)) as Response;
this.logService.debug("[RemoteSdkService]: Received response", response);
command: JSON.stringify(command),
} satisfies RemoteSdkRequest)) as string;
this.logService.debug("[RemoteSdkService]: Request:", command, "Response:", jsonResponse);
const response = JSON.parse(jsonResponse) as Response;
return response;
},
});

View File

@@ -1,13 +1,13 @@
import { Command, Response, Result } from "@bitwarden/common/platform/services/sdk/rpc/protocol";
import { Result } from "@bitwarden/common/platform/services/sdk/rpc/protocol";
export type RemoteSdkRequest = {
type: "RemoteSdkRequest";
command: Command;
command: string;
};
export type RemoteSdkResponse = {
type: "RemoteSdkResponse";
response: Response;
response: string;
};
export type RemoteSdkResendRootRequest = {
@@ -21,37 +21,22 @@ export type RemoteSdkRoot = {
export function isRemoteSdkRequest(message: unknown): message is RemoteSdkRequest {
return (
typeof message === "object" &&
message !== null &&
(message as any).type === "RemoteSdkRequest" &&
typeof (message as any).command === "object"
typeof message === "object" && message !== null && (message as any).type === "RemoteSdkRequest"
);
}
export function isRemoteSdkResponse(message: unknown): message is RemoteSdkResponse {
return (
typeof message === "object" &&
message !== null &&
(message as any).type === "RemoteSdkResponse" &&
typeof (message as any).response === "object"
typeof message === "object" && message !== null && (message as any).type === "RemoteSdkResponse"
);
}
export function isRemoteSdkResendRootRequest(
message: unknown,
): message is RemoteSdkResendRootRequest {
return (
typeof message === "object" &&
message !== null &&
(message as any).type === "RemoteSdkResendRootRequest"
);
return message !== null && (message as any).type === "RemoteSdkResendRootRequest";
}
export function isRemoteSdkRoot(message: unknown): message is RemoteSdkRoot {
return (
typeof message === "object" &&
message !== null &&
(message as any).type === "RemoteSdkRoot" &&
typeof (message as any).result === "object"
);
return message !== null && (message as any).type === "RemoteSdkRoot";
}

View File

@@ -62,8 +62,8 @@ export class RemoteSdkServerService {
"sdk.request",
(message: any, sender: chrome.runtime.MessageSender, sendResponse: any) => {
if (isRemoteSdkRequest(message)) {
void this.server.handle(message.command).then((response) => {
sendResponse(response);
void this.server.handle(JSON.parse(message.command)).then((response) => {
sendResponse(JSON.stringify(response));
});
return true; // Indicate that we will send a response asynchronously
}

View File

@@ -97,9 +97,9 @@ export const DefaultFeatureFlagValue = {
/* Vault */
[FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.PM19941MigrateCipherDomainToSdk]: true,
[FeatureFlag.PM22134SdkCipherListView]: true,
[FeatureFlag.PM22136_SdkCipherEncryption]: true,
[FeatureFlag.PM19941MigrateCipherDomainToSdk]: FALSE,
[FeatureFlag.PM22134SdkCipherListView]: FALSE,
[FeatureFlag.PM22136_SdkCipherEncryption]: FALSE,
/* Auth */
[FeatureFlag.PM22110_DisableAlternateLoginMethods]: FALSE,

View File

@@ -4,7 +4,7 @@ import { BitwardenClient } from "@bitwarden/sdk-internal";
import { Rc } from "../../misc/reference-counting/rc";
import { Remote } from "./remote";
import { Remote } from "./rpc/remote";
export abstract class RemoteSdkService {
abstract remoteClient$: Observable<Remote<Rc<BitwardenClient> | null>>;

View File

@@ -1,65 +0,0 @@
import { ChainablePromise } from "./chainable-promise";
export type Remote<T> = {
[K in keyof T as K extends typeof Symbol.dispose ? never : K]: RemoteProperty<T[K]>;
} & (typeof Symbol.dispose extends keyof T ? { [Symbol.asyncDispose](): Promise<void> } : object);
type Resolved<T> = T extends Promise<infer U> ? U : T;
// type HasFree<T> = T extends { free(): void } ? true : false;
/**
* Maps remote object fields to RPC-exposed types.
*
* Property access (non-function):
* - If the value is serializable (see IsSerializable), returns Promise<Resolved<T>>.
* - If not serializable (e.g., class instance, Wasm object), returns Remote<Resolved<T>> (a live reference).
* Note: properties do NOT expose `.await`; they are direct remote references.
*
* Function call:
* - If the return value is serializable, returns Promise<Resolved<R>>.
* - If not serializable, returns ChainablePromise<Remote<Resolved<R>>> so callers can use `.await`
* for ergonomic chaining, e.g. remote.vault().await.totp().await.generate(...).
*/
export type RemoteProperty<T> = T extends (...args: any[]) => any
? RemoteFunction<T>
: RemoteReference<Resolved<T>>;
// : HasFree<Resolved<T>> extends true
// ? RemoteReference<Resolved<T>>
// : Promise<Resolved<T>>;
export type Transfer<T> = {
/**
* Force a by-value snapshot transfer of this remote reference. Resolves to a serializable value.
* If the object is not serializable at runtime, this will throw.
*/
transfer: Promise<T>;
};
export type RemoteReference<T> = Remote<T> &
Transfer<T> & {
/**
* Force a by-value snapshot transfer of this remote reference. Resolves to a serializable value.
* If the object is not serializable at runtime, this will throw.
*
* OLD: Remove
*/
by_value(): Promise<T>;
};
/**
* RemoteFunction arguments must be Serializable at compile time. For non-serializable
* return types, we expose ChainablePromise<Remote<...>> to enable Rust-like `.await` chaining.
*/
export type RemoteFunction<T extends (...args: any[]) => any> = (
...args: Parameters<T>
) => ChainablePromise<RemoteReference<Resolved<ReturnType<T>>>> & Transfer<Resolved<ReturnType<T>>>;
// ) => Resolved<ReturnType<T>> extends object
// ? ChainablePromise<RemoteReference<Resolved<ReturnType<T>>>>
// : Promise<Resolved<ReturnType<T>>>;
// Serializable type rules to mirror `isSerializable` from rpc/server.ts
// - Primitives: string | number | boolean | null
// - Arrays: elements must be Serializable
// - Plain objects: all non-function properties must be Serializable
// - Everything else (functions, class instances, Date, Map, Set, etc.) is NOT serializable
// Serializability checks removed: transport and server decide value vs reference.

View File

@@ -1,168 +0,0 @@
import { RpcRequestChannel } from "./client";
import { ReferenceId, PropertySymbol, BatchCommand, serializeSymbol } from "./protocol";
export type BatchingProxy<T> = {
[ProxyInfo]: T & {
proxyType: "RpcObjectReference" | "RpcPendingObjectReference";
};
};
export const ProxyInfo = Symbol("ProxyInfo");
export function isProxy(obj: any): obj is BatchingProxy<any> {
return obj && typeof obj === "function" && obj[ProxyInfo] !== undefined;
}
export function isReferenceProxy(obj: any): obj is BatchingProxy<RpcObjectReference> {
return isProxy(obj) && obj[ProxyInfo].proxyType === "RpcObjectReference";
}
export function isPendingReferenceProxy(obj: any): obj is BatchingProxy<RpcPendingObjectReference> {
return isProxy(obj) && obj[ProxyInfo].proxyType === "RpcPendingObjectReference";
}
/**
* A reference to a remote object.
*/
export type RpcObjectReference = {
referenceId: ReferenceId;
objectType?: string;
};
export function RpcObjectReference(channel: RpcRequestChannel, reference: RpcObjectReference) {
const target = () => {};
Object.defineProperty(target, "name", { value: `RpcObjectReference`, configurable: true });
(target as any)[ProxyInfo] = { ...reference, proxyType: "RpcObjectReference" };
return new Proxy(
target,
proxyHandler(target, channel, reference, []),
) as any as BatchingProxy<RpcObjectReference>;
}
/**
* A pending reference to a remote object.
*/
export type RpcPendingObjectReference = {
reference: RpcObjectReference;
commands: BatchCommand[];
};
export function RpcPendingObjectReference(
channel: RpcRequestChannel,
reference: RpcPendingObjectReference,
) {
const target = () => {};
Object.defineProperty(target, "name", {
value: `RpcPendingObjectReference(${reference.reference.objectType}.${commandsToString(reference.commands)})`,
configurable: true,
});
(target as any)[ProxyInfo] = { ...reference, proxyType: "RpcPendingObjectReference" };
return new Proxy(
target,
proxyHandler(target, channel, reference.reference, reference.commands),
) as any as BatchingProxy<RpcPendingObjectReference>;
}
function proxyHandler(
target: any,
channel: RpcRequestChannel,
reference: RpcObjectReference,
commands: BatchCommand[],
): any {
return {
get(target: any, property: string | PropertySymbol) {
if ((property as any) === ProxyInfo) {
return (target as any)[ProxyInfo];
}
if (property === "then" && commands.length === 0) {
// This means we awaited a RpcObjectReference which resolves to itself
// We don't support transfering references to Promises themselves, we'll
// automatically await them before returning
return undefined;
}
if (property === "then" && commands.length > 0) {
return BatchCommandExecutor(channel, reference.referenceId, commands);
}
if (property === "await") {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "await" }],
});
}
if (property === "transfer") {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "transfer" }],
});
}
return RpcPendingObjectReference(channel, {
reference,
commands:
typeof property === "string"
? [...commands, { method: "get", propertyName: property }]
: [...commands, { method: "get", propertySymbol: serializeSymbol(property) }],
});
},
apply(_target: any, _thisArg: any, argArray?: any): any {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "apply", args: argArray }],
});
},
} satisfies ProxyHandler<any>;
}
function BatchCommandExecutor(
channel: RpcRequestChannel,
referenceId: ReferenceId,
commands: BatchCommand[],
): (onFulfilled: (value: any) => void, onRejected: (reason: any) => void) => void {
const command = {
method: "batch",
referenceId,
commands: commands.filter((cmd) => cmd.method !== "await"),
} as const;
return (onFulfilled, onRejected) => {
(async () => {
const result = await channel.sendCommand(command);
if (result.status === "error") {
throw result.error;
}
if (result.result.type === "value") {
return result.result.value;
}
return RpcObjectReference(channel, {
referenceId: result.result.referenceId,
objectType: result.result.objectType,
});
})().then(onFulfilled, onRejected);
};
}
function commandsToString(commands: BatchCommand[]): string {
return commands
.map((cmd) => {
if (cmd.method === "get") {
const prop = (cmd as any).propertyName ?? (cmd as any).propertySymbol;
return `${String(prop)}`;
} else if (cmd.method === "apply") {
const prop = (cmd as any).propertyName ?? (cmd as any).propertySymbol;
return `${String(prop)}()`;
}
return "???";
})
.join(".");
}

View File

@@ -1,9 +1,8 @@
import { map, Observable } from "rxjs";
import { Remote } from "../remote";
import { RpcObjectReference } from "./batch-proxies";
import { Command, Response } from "./protocol";
import { RpcObjectReference } from "./proxies";
import { Remote } from "./remote";
export interface RpcRequestChannel {
sendCommand(command: Command): Promise<Response>;

View File

@@ -1,4 +1,4 @@
import { executeBatchCommands } from "./batch-executor";
import { executeBatchCommands } from "./executor";
import { RpcError } from "./error";
import { BatchCommand, serializeSymbol } from "./protocol";
import { ReferenceStore } from "./reference-store";

View File

@@ -1,6 +1,6 @@
import { Observable } from "rxjs";
import { ProxyInfo, RpcObjectReference } from "./batch-proxies";
import { ProxyInfo, RpcObjectReference } from "./proxies";
import { RpcRequestChannel } from "./client";
import { Command, Response } from "./protocol";

View File

@@ -1,215 +1,173 @@
import { chain } from "../chainable-promise";
import { RpcRequestChannel } from "./client";
import { Command, PropertySymbol, ReferenceId, Response, serializeSymbol } from "./protocol";
import { RpcError } from "./error";
import { ReferenceId, PropertySymbol, BatchCommand, serializeSymbol } from "./protocol";
export type BatchingProxy<T> = {
[ProxyInfo]: T & {
proxyType: "RpcObjectReference" | "RpcPendingObjectReference";
};
};
export const ProxyInfo = Symbol("ProxyInfo");
export function isProxy(obj: any): obj is BatchingProxy<any> {
return obj && typeof obj === "function" && obj[ProxyInfo] !== undefined;
}
export function isReferenceProxy(obj: any): obj is BatchingProxy<RpcObjectReference> {
return isProxy(obj) && obj[ProxyInfo].proxyType === "RpcObjectReference";
}
export function isPendingReferenceProxy(obj: any): obj is BatchingProxy<RpcPendingObjectReference> {
return isProxy(obj) && obj[ProxyInfo].proxyType === "RpcPendingObjectReference";
}
/**
* A reference to a remote object.
*/
export class RpcObjectReference {
static create(
channel: RpcRequestChannel,
referenceId: ReferenceId,
objectType?: string,
): RpcObjectReference & { by_value(): Promise<any> } {
return ProxiedReference(channel, new RpcObjectReference(referenceId, objectType)) as any;
}
export type RpcObjectReference = {
referenceId: ReferenceId;
objectType?: string;
};
private constructor(
public referenceId: ReferenceId,
public objectType?: string,
) {}
export function RpcObjectReference(channel: RpcRequestChannel, reference: RpcObjectReference) {
const target = () => {};
Object.defineProperty(target, "name", { value: `RpcObjectReference`, configurable: true });
(target as any)[ProxyInfo] = { ...reference, proxyType: "RpcObjectReference" };
return new Proxy(
target,
proxyHandler(target, channel, reference, []),
) as any as BatchingProxy<RpcObjectReference>;
}
function ProxiedReference(
/**
* A pending reference to a remote object.
*/
export type RpcPendingObjectReference = {
reference: RpcObjectReference;
commands: BatchCommand[];
};
export function RpcPendingObjectReference(
channel: RpcRequestChannel,
reference: RpcPendingObjectReference,
) {
const target = () => {};
Object.defineProperty(target, "name", {
value: `RpcPendingObjectReference(${reference.reference.objectType}.${commandsToString(reference.commands)})`,
configurable: true,
});
(target as any)[ProxyInfo] = { ...reference, proxyType: "RpcPendingObjectReference" };
return new Proxy(
target,
proxyHandler(target, channel, reference.reference, reference.commands),
) as any as BatchingProxy<RpcPendingObjectReference>;
}
function proxyHandler(
target: any,
channel: RpcRequestChannel,
reference: RpcObjectReference,
): RpcObjectReference & { by_value(): Promise<any> } {
return new Proxy(reference as any, {
get(target, property: string | PropertySymbol) {
if (property === "then") {
// Allow awaiting the proxy itself
commands: BatchCommand[],
): any {
return {
get(target: any, property: string | PropertySymbol) {
if ((property as any) === ProxyInfo) {
return (target as any)[ProxyInfo];
}
if (property === "then" && commands.length === 0) {
// This means we awaited a RpcObjectReference which resolves to itself
// We don't support transfering references to Promises themselves, we'll
// automatically await them before returning
return undefined;
}
if (property === "by_value") {
return async () => {
const result = await sendAndUnwrap(channel, {
method: "by_value",
referenceId: reference.referenceId,
} as Command);
if (result.type !== "value") {
throw new Error(
`[RPC] by_value() expected a value but got a reference for ${reference.objectType}`,
);
}
return result.value;
};
if (property === "then" && commands.length > 0) {
return BatchCommandExecutor(channel, reference.referenceId, commands);
}
// console.log(`Accessing ${reference.objectType}.${String(propertyName)}`);
return RpcPropertyReference(channel, { objectReference: target as any, property });
if (property === "await") {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "await" }],
});
}
if (property === "transfer") {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "transfer" }],
});
}
return RpcPendingObjectReference(channel, {
reference,
commands:
typeof property === "string"
? [...commands, { method: "get", propertyName: property }]
: [...commands, { method: "get", propertySymbol: serializeSymbol(property) }],
});
},
}) as any;
apply(_target: any, _thisArg: any, argArray?: any): any {
return RpcPendingObjectReference(channel, {
reference,
commands: [...commands, { method: "apply", args: argArray }],
});
},
} satisfies ProxyHandler<any>;
}
/**
* A reference to a specific property on a remote object.
*/
type RpcPropertyReference = {
objectReference: RpcObjectReference;
property: string | PropertySymbol;
};
function BatchCommandExecutor(
channel: RpcRequestChannel,
referenceId: ReferenceId,
commands: BatchCommand[],
): (onFulfilled: (value: any) => void, onRejected: (reason: any) => void) => void {
const command = {
method: "batch",
referenceId,
commands: commands.filter((cmd) => cmd.method !== "await"),
} as const;
/**
* 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),
// );
// }
return (onFulfilled, onRejected) => {
(async () => {
const result = await channel.sendCommand(command);
// private constructor(
// public objectReference: RpcObjectReference,
// public propertyName: string,
// ) {}
// }
/**
* A sub-proxy for a specific property of a proxied reference
* This is because we need to handle property accesses differently than method calls
* but we don't know which type it is until it gets consumed.
*
* If this references a method then the `apply` trap will be called on this proxy.
* If this references a property then they'll try to await the value, triggering the `get` trap
* when they access the `then` property.
*/
function RpcPropertyReference(channel: RpcRequestChannel, reference: RpcPropertyReference) {
const target = () => {};
Object.defineProperty(target, "name", { value: `RpcPropertyReference`, configurable: true });
(target as any).objectReference = reference.objectReference;
(target as any).property = reference.property;
return new Proxy(target, {
get(_target, propertyName: string) {
// console.log(
// `Accessing ${reference.objectReference.objectType}.${reference.propertyName}.${propertyName}`,
// );
// Allow Function.prototype.call/apply/bind to be used by TS helpers and wrappers (e.g., disposables, chainable await)
if (propertyName === "call") {
return Function.prototype.call;
}
if (propertyName === "apply") {
return Function.prototype.apply;
}
if (propertyName === "bind") {
return Function.prototype.bind;
if (result === null || result === undefined) {
throw new RpcError("RPC returned null or undefined response");
}
if (propertyName !== "then") {
// Support chained call like: (await obj.prop).method() AND obj.prop.method()
// by lazily resolving obj.prop first, then invoking method/property on the resolved reference.
return (...argArray: unknown[]) => {
const p = (async () => {
// First resolve the original referenced property value via GET
const getResult = await sendAndUnwrap(channel, buildGetCommand(reference));
if (getResult.type !== "reference") {
throw new Error(
`Cannot access property '${propertyName}' on non-reference value returned by remote property`,
);
}
// Now perform the requested operation on the resolved reference
const callResult = await sendAndUnwrap(
channel,
buildCallCommand(getResult.referenceId, propertyName, argArray),
);
if (callResult.type === "value") {
return callResult.value;
}
return RpcObjectReference.create(
channel,
callResult.referenceId,
callResult.objectType,
);
})();
return chain(p as Promise<any>);
};
if (result.status === "error") {
throw result.error;
}
return (onFulfilled: (value: any) => void, onRejected: (error: any) => void) => {
(async () => {
const result = await sendAndUnwrap(channel, buildGetCommand(reference));
return unwrapResult(channel, result);
})().then(onFulfilled, onRejected);
};
},
apply(_target, _thisArg, argArray: unknown[]) {
// console.log(`Calling ${reference.objectReference.objectType}.${reference.propertyName}`);
if (result.result.type === "value") {
return result.result.value;
}
const command = buildCallCommand(
reference.objectReference.referenceId,
reference.property,
argArray,
);
const p = (async () => {
const result = await sendAndUnwrap(channel, command);
if (result.type === "value") {
return result.value;
}
return RpcObjectReference.create(channel, result.referenceId, result.objectType);
})();
return chain(p as Promise<any>);
},
});
}
// Helpers
function buildGetCommand(reference: RpcPropertyReference): Command {
if (typeof reference.property === "string") {
return {
method: "get",
referenceId: reference.objectReference.referenceId,
propertyName: reference.property,
};
}
return {
method: "get",
referenceId: reference.objectReference.referenceId,
propertySymbol: serializeSymbol(reference.property),
return RpcObjectReference(channel, {
referenceId: result.result.referenceId,
objectType: result.result.objectType,
});
})().then(onFulfilled, onRejected);
};
}
function buildCallCommand(
referenceId: ReferenceId,
property: string | PropertySymbol,
args: unknown[],
): Command {
if (typeof property === "string") {
return { method: "call", referenceId, propertyName: property, args };
}
return { method: "call", referenceId, propertySymbol: serializeSymbol(property), args };
}
function commandsToString(commands: BatchCommand[]): string {
return commands
.map((cmd) => {
if (cmd.method === "get") {
const prop = (cmd as any).propertyName ?? (cmd as any).propertySymbol;
return `${String(prop)}`;
} else if (cmd.method === "apply") {
const prop = (cmd as any).propertyName ?? (cmd as any).propertySymbol;
return `${String(prop)}()`;
}
async function sendAndUnwrap(channel: RpcRequestChannel, command: Command) {
const response: Response = await channel.sendCommand(command);
if (response.status === "error") {
throw new Error(`RPC Error: ${response.error}`);
}
return response.result;
}
function unwrapResult(channel: RpcRequestChannel, result: any) {
if (result.type === "value") {
return result.value;
}
return RpcObjectReference.create(channel, result.referenceId, result.objectType);
return "???";
})
.join(".");
}

View File

@@ -0,0 +1,32 @@
import { ChainablePromise } from "./chainable-promise";
export type Remote<T> = {
[K in keyof T as K extends typeof Symbol.dispose ? never : K]: RemoteProperty<T[K]>;
} & (typeof Symbol.dispose extends keyof T ? { [Symbol.asyncDispose](): Promise<void> } : object);
type Resolved<T> = T extends Promise<infer U> ? U : T;
/**
* Maps remote object fields to RPC-exposed types.
*/
export type RemoteProperty<T> = T extends (...args: any[]) => any
? RemoteFunction<T>
: RemoteReference<Resolved<T>>;
export type Transfer<T> = {
/**
* Force a by-value snapshot transfer of this remote reference. Resolves to a serializable value.
* If the object is not serializable at runtime, this will throw.
*/
transfer: Promise<T>;
};
export type RemoteReference<T> = Remote<T> & Transfer<T>;
/**
* RemoteFunction arguments must be Serializable at compile time. For non-serializable
* return types, we expose ChainablePromise<Remote<...>> to enable Rust-like `.await` chaining.
*/
export type RemoteFunction<T extends (...args: any[]) => any> = (
...args: Parameters<T>
) => ChainablePromise<RemoteReference<Resolved<ReturnType<T>>>> & Transfer<Resolved<ReturnType<T>>>;

View File

@@ -2,9 +2,9 @@ import { firstValueFrom, map, Observable } from "rxjs";
import { Rc } from "../../../misc/reference-counting/rc";
import { isReferenceProxy, RpcObjectReference } from "./batch-proxies";
import { RpcClient, RpcRequestChannel } from "./client";
import { Command, Response } from "./protocol";
import { isReferenceProxy, RpcObjectReference } from "./proxies";
import { RpcServer } from "./server";
describe("RpcServer", () => {
@@ -66,7 +66,7 @@ describe("RpcServer", () => {
expect(result).toEqual({ message: "Hello, World!", array: [1, "2", null] });
});
it("handles RC objects correctly", async () => {
it.only("handles RC objects correctly", async () => {
const wasmObj = new WasmLikeObject("RC");
const rcValue = new Rc(wasmObj);
const server = new RpcServer<Rc<WasmLikeObject>>();

View File

@@ -1,6 +1,6 @@
import { map, Observable, ReplaySubject } from "rxjs";
import { executeBatchCommands } from "./batch-executor";
import { executeBatchCommands } from "./executor";
import { Command, Response, Result } from "./protocol";
import { ReferenceStore } from "./reference-store";
@@ -30,70 +30,6 @@ export class RpcServer<T> {
}
}
// if (command.method === "get") {
// const target = this.references.get<any>(command.referenceId);
// if (!target) {
// return { status: "error", error: `[RPC] Reference ID ${command.referenceId} not found` };
// }
// try {
// const propertyKey =
// (command as any).propertyName ?? deserializeSymbol((command as any).propertySymbol);
// const propertyValue = target[propertyKey];
// if (typeof propertyValue === "function") {
// return { status: "error", error: `[RPC] Property ${String(propertyKey)} is a function` };
// } else {
// return { status: "success", result: this.convertToReturnable(propertyValue) };
// }
// } catch (error) {
// return { status: "error", error };
// }
// }
// if (command.method === "by_value") {
// const target = this.references.get<any>(command.referenceId);
// if (!target) {
// return { status: "error", error: `[RPC] Reference ID ${command.referenceId} not found` };
// }
// // try {
// // Not a dependable check
// // if (!isSerializable(target)) {
// // return {
// // status: "error",
// // error: `[RPC] by_value() not supported for non-serializable object of type ${target?.constructor?.name}`,
// // };
// // }
// return { status: "success", result: { type: "value", value: target } };
// // } catch (error) {
// // return { status: "error", error };
// // }
// }
// 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 propertyKey =
// (command as any).propertyName ?? deserializeSymbol((command as any).propertySymbol);
// const method = target[propertyKey];
// if (typeof method !== "function") {
// return {
// status: "error",
// error: `[RPC] Property ${String(propertyKey)} is not a function of ${target.constructor.name}`,
// };
// }
// const result = await method.apply(target, command.args);
// return { status: "success", result: this.convertToReturnable(result) };
// } catch (error) {
// return { status: "error", error };
// }
// }
return { status: "error", error: `Unknown command method: ${command.method}` };
}

View File

@@ -233,12 +233,12 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
throw new Error("SDK is undefined");
}
using ref = await sdk.take();
await using ref = await sdk.take();
const ciphersClient = await ref.value.vault().await.ciphers();
const result: DecryptCipherListResult = await ciphersClient
.decrypt_list_with_failures(ciphers.map((cipher) => cipher.toSdkCipher()))
.await.by_value();
const result: DecryptCipherListResult = await ciphersClient.decrypt_list_with_failures(
ciphers.map((cipher) => cipher.toSdkCipher()),
).transfer;
const decryptedCiphers = result.successes;
const failedCiphers: Cipher[] = result.failures

View File

@@ -39,10 +39,10 @@ export class TotpService implements TotpServiceAbstraction {
// Using remote SDK service to generate TOTP
return this.remoteSdkService.remoteClient$.pipe(
switchMap(async (sdk) => {
using ref = await sdk!.take();
await using ref = await sdk!.take();
const totp = await ref.value.vault().await.totp();
// Force by-value transfer for the TOTP response
return totp.generate_totp(key).await.by_value();
// Transfer the TOTP response
return totp.generate_totp(key).transfer;
}),
shareReplay({ bufferSize: 1, refCount: true }),
);