From c62efc37ccb772f10a85ea54c019a2568ef06db2 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Sun, 15 Sep 2024 19:31:46 +0200 Subject: [PATCH] Remove websocket functionality --- examples/test-app/src/components/Navbar.tsx | 27 +-- .../test-app/src/hooks/useParcnetClient.tsx | 58 +----- examples/test-app/src/utils.ts | 8 +- .../app-connector/src/adapters/websocket.ts | 57 ------ packages/app-connector/src/index.ts | 1 - packages/app-connector/src/rpc_client.ts | 7 +- .../test/parcnet-connector.spec.ts | 10 +- packages/client-helpers/package.json | 10 -- .../src/connection/websocket.ts | 167 ------------------ 9 files changed, 17 insertions(+), 328 deletions(-) delete mode 100644 packages/app-connector/src/adapters/websocket.ts delete mode 100644 packages/client-helpers/src/connection/websocket.ts diff --git a/examples/test-app/src/components/Navbar.tsx b/examples/test-app/src/components/Navbar.tsx index 7809585..7822b8c 100644 --- a/examples/test-app/src/components/Navbar.tsx +++ b/examples/test-app/src/components/Navbar.tsx @@ -6,19 +6,18 @@ export function Navbar({ connecting }: { connecting: boolean }): ReactNode { const connectionInfo = useMemo(() => getConnectionInfo(), []); const [inputClientUrl, setInputClientUrl] = useState(connectionInfo.url); - const [inputClientType, setInputClientType] = useState(connectionInfo.type); const onSave = useCallback( (e: React.MouseEvent) => { e.preventDefault(); - const newConnectionInfo = { url: inputClientUrl, type: inputClientType }; + const newConnectionInfo = { url: inputClientUrl, type: "iframe" }; localStorage.setItem( "clientConnectionInfo", JSON.stringify(newConnectionInfo) ); window.location.reload(); }, - [inputClientUrl, inputClientType] + [inputClientUrl] ); return ( @@ -72,28 +71,6 @@ export function Navbar({ connecting }: { connecting: boolean }): ReactNode { value={inputClientUrl} onChange={(e) => setInputClientUrl(e.target.value)} /> -
- - -
diff --git a/examples/test-app/src/hooks/useParcnetClient.tsx b/examples/test-app/src/hooks/useParcnetClient.tsx index c009e0c..39e1d8b 100644 --- a/examples/test-app/src/hooks/useParcnetClient.tsx +++ b/examples/test-app/src/hooks/useParcnetClient.tsx @@ -1,9 +1,4 @@ -import { - connect, - connectWebsocket, - ParcnetAPI, - Zapp -} from "@parcnet-js/app-connector"; +import { connect, ParcnetAPI, Zapp } from "@parcnet-js/app-connector"; import { createContext, ReactNode, @@ -15,7 +10,7 @@ import { export type ClientConnectionInfo = { url: string; - type: "iframe" | "websocket"; + type: "iframe"; }; export enum ClientConnectionState { @@ -31,7 +26,7 @@ export const ParcnetClientContext = createContext({ type ClientIframeState = | { state: ClientConnectionState.CONNECTING; - ref: React.RefObject; + ref: React.RefObject | null; } | { state: ClientConnectionState.CONNECTED; @@ -39,16 +34,7 @@ type ClientIframeState = ref: React.RefObject; }; -type ClientWebsocketState = - | { - state: ClientConnectionState.CONNECTING; - } - | { - state: ClientConnectionState.CONNECTED; - z: ParcnetAPI; - }; - -type ClientState = ClientIframeState | ClientWebsocketState; +type ClientState = ClientIframeState; export function ParcnetClientProvider({ zapp, @@ -65,12 +51,6 @@ export function ParcnetClientProvider({ {children} ); - } else { - return ( - - {children} - - ); } } @@ -111,36 +91,6 @@ export function ParcnetIframeProvider({ ); } -function ParcnetWebsocketProvider({ - zapp, - url, - children -}: { - zapp: Zapp; - url: string; - children: React.ReactNode; -}): ReactNode { - const [value, setValue] = useState({ - state: ClientConnectionState.CONNECTING - }); - - useEffect(() => { - void connectWebsocket(zapp, url).then((api) => { - setValue({ - state: ClientConnectionState.CONNECTED, - z: api - }); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - {children} - - ); -} - type UseParcnetClient = | { connected: true; diff --git a/examples/test-app/src/utils.ts b/examples/test-app/src/utils.ts index 5a1ef1b..3ad455e 100644 --- a/examples/test-app/src/utils.ts +++ b/examples/test-app/src/utils.ts @@ -6,9 +6,7 @@ export function cn(...classes: string[]): string { export const DEFAULT_CONNECTION_INFO: ClientConnectionInfo = { url: process.env.CLIENT_URL ?? "https://staging-rob.zupass.org", - type: (["iframe", "websocket"].includes(`${process.env.CLIENT_TYPE}`) - ? process.env.CLIENT_TYPE - : "iframe") as "iframe" | "websocket" + type: "iframe" }; export function getConnectionInfo(): ClientConnectionInfo { @@ -20,8 +18,8 @@ export function getConnectionInfo(): ClientConnectionInfo { storedConnectionInfo ) as ClientConnectionInfo; if ( - ["iframe", "websocket"].includes(connectionInfo.type) && - typeof connectionInfo.url === "string" + parsedConnectionInfo.type === "iframe" && + typeof parsedConnectionInfo.url === "string" ) { connectionInfo = parsedConnectionInfo; } diff --git a/packages/app-connector/src/adapters/websocket.ts b/packages/app-connector/src/adapters/websocket.ts deleted file mode 100644 index 8332903..0000000 --- a/packages/app-connector/src/adapters/websocket.ts +++ /dev/null @@ -1,57 +0,0 @@ -import toast from "@brenoroosevelt/toast"; -import { - InitializationMessage, - InitializationMessageType, - Zapp -} from "@parcnet-js/client-rpc"; -import JSONBig from "json-bigint"; -import { ParcnetAPI } from "../api_wrapper.js"; -import { ParcnetRPCConnector } from "../rpc_client.js"; -import { DialogController } from "./iframe.js"; - -class DialogControllerImpl implements DialogController { - public show(): void { - toast.info("Your PARCNET client requests interaction").catch(() => { - // Do nothing - }); - } - - public close(): void { - // Does nothing - } -} - -export function connectWebsocket(zapp: Zapp, url: string): Promise { - const ws = new WebSocket(url); - const chan = new MessageChannel(); - - ws.addEventListener("open", () => async () => { - const connectMsg = JSONBig.stringify({ - type: InitializationMessageType.PARCNET_CLIENT_CONNECT, - zapp - } satisfies InitializationMessage); - await new Promise((resolve) => window.setTimeout(resolve, 1000)); - ws.send(connectMsg); - }); - - ws.addEventListener("message", (ev) => { - chan.port1.postMessage(JSONBig.parse(ev.data as string)); - }); - - chan.port1.addEventListener("message", (message) => { - ws.send(JSONBig.stringify(message.data)); - }); - - chan.port1.start(); - - return new Promise((resolve) => { - // Create a new RPC client - const client = new ParcnetRPCConnector( - chan.port2, - new DialogControllerImpl() - ); - client.start(() => { - resolve(new ParcnetAPI(client)); - }); - }); -} diff --git a/packages/app-connector/src/index.ts b/packages/app-connector/src/index.ts index d8fc9de..fca094e 100644 --- a/packages/app-connector/src/index.ts +++ b/packages/app-connector/src/index.ts @@ -1,5 +1,4 @@ export * from "./adapters/iframe.js"; -export * from "./adapters/websocket.js"; export * from "./api_wrapper.js"; export * from "./rpc_client.js"; export * from "./utils.js"; diff --git a/packages/app-connector/src/rpc_client.ts b/packages/app-connector/src/rpc_client.ts index ffccba0..fd11137 100644 --- a/packages/app-connector/src/rpc_client.ts +++ b/packages/app-connector/src/rpc_client.ts @@ -91,14 +91,11 @@ export class ParcnetRPCConnector implements ParcnetRPC, ParcnetEvents { args: v.InferInput, functionSchema: F ): Promise> { - const result = this.#invoke(fn, v.parse(functionSchema.input, args)); + const result = await this.#invoke(fn, v.parse(functionSchema.input, args)); // Ensure that the result is of the expected type const parsedResult = v.safeParse(functionSchema.output, result); if (parsedResult.success) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const result = await parsedResult.output; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return result; + return parsedResult.output; } else { throw new Error( `Failed to parse result for ${fn}: ${parsedResult.issues.map((issue) => issue.message).join(", ")}` diff --git a/packages/app-connector/test/parcnet-connector.spec.ts b/packages/app-connector/test/parcnet-connector.spec.ts index 49a938a..8edcbd9 100644 --- a/packages/app-connector/test/parcnet-connector.spec.ts +++ b/packages/app-connector/test/parcnet-connector.spec.ts @@ -7,8 +7,8 @@ import { import * as p from "@parcnet-js/podspec"; import { POD } from "@pcd/pod"; import crypto from "crypto"; +import * as v from "valibot"; import { assert, describe, expect, it } from "vitest"; -import { ZodError } from "zod"; import { postRPCMessage } from "../src/index.js"; import { ParcnetRPCConnector } from "../src/rpc_client.js"; import { connectedClient, mockDialog } from "./utils.js"; @@ -54,7 +54,7 @@ describe("parcnet-client should work", function () { const { chan, client } = await connectedClient(); chan.port1.onmessage = (event): void => { - const message = RPCMessageSchema.parse(event.data); + const message = v.parse(RPCMessageSchema, event.data); assert(message.type === RPCMessageType.PARCNET_CLIENT_INVOKE); assert(message.fn === "identity.getSemaphoreV3Commitment"); assert(message.args.length === 0); @@ -76,7 +76,7 @@ describe("parcnet-client should work", function () { const { chan, client } = await connectedClient(); chan.port1.onmessage = (event): void => { - const message = RPCMessageSchema.parse(event.data); + const message = v.parse(RPCMessageSchema, event.data); assert(message.type === RPCMessageType.PARCNET_CLIENT_INVOKE); assert(message.fn === "identity.getSemaphoreV3Commitment"); assert(message.args.length === 0); @@ -91,7 +91,9 @@ describe("parcnet-client should work", function () { result: "INCORRECT" // Not a BigInt }); - await expect(promise).rejects.toThrow(ZodError); + await expect(promise).rejects.toThrow( + `Failed to parse result for identity.getSemaphoreV3Commitment: Invalid type: Expected bigint but received "INCORRECT"` + ); }); it("should notify when a subscription is updated", async function () { diff --git a/packages/client-helpers/package.json b/packages/client-helpers/package.json index cf1e3c3..f0b14b1 100644 --- a/packages/client-helpers/package.json +++ b/packages/client-helpers/package.json @@ -16,11 +16,6 @@ "types": "./src/connection/iframe.ts", "import": "./dist/connection/iframe.js", "require": "./dist/connection/iframe.cjs" - }, - "./connection/websocket": { - "types": "./src/connection/websocket.ts", - "import": "./dist/connection/websocket.js", - "require": "./dist/connection/websocket.cjs" } }, "scripts": { @@ -59,11 +54,6 @@ "types": "./dist/connection/iframe.d.ts", "import": "./dist/connection/iframe.js", "require": "./dist/connection/iframe.cjs" - }, - "./connection/websocket": { - "types": "./dist/connection/websocket.d.ts", - "import": "./dist/connection/websocket.js", - "require": "./dist/connection/websocket.cjs" } } } diff --git a/packages/client-helpers/src/connection/websocket.ts b/packages/client-helpers/src/connection/websocket.ts deleted file mode 100644 index 10486e0..0000000 --- a/packages/client-helpers/src/connection/websocket.ts +++ /dev/null @@ -1,167 +0,0 @@ -// pass in a websocket -// set up event handlers?? -// wait til we get a connect -// return advice channel and zapp - -import { - deepGet, - InitializationMessageSchema, - InitializationMessageType, - ParcnetRPC, - RPCMessage, - RPCMessageSchema, - RPCMessageType, - SubscriptionUpdateResult, - Zapp -} from "@parcnet-js/client-rpc"; -import JSONBig from "json-bigint"; -import type { MessageEvent, WebSocket } from "ws"; -import { ConnectorAdvice } from "./advice.js"; - -export class WebsocketAdviceChannel implements ConnectorAdvice { - private socket: WebSocket; - private onReady: (rpc: ParcnetRPC) => void; - - constructor({ - socket, - onReady - }: { - socket: WebSocket; - onReady: (rpc: ParcnetRPC) => void; - }) { - this.socket = socket; - this.onReady = onReady; - } - - private send(message: RPCMessage): void { - this.socket.send(JSONBig.stringify(message)); - } - - public showClient(): void { - this.send({ - type: RPCMessageType.PARCNET_CLIENT_SHOW - }); - } - - public hideClient(): void { - this.send({ - type: RPCMessageType.PARCNET_CLIENT_HIDE - }); - } - - public ready(rpc: ParcnetRPC): void { - this.onReady(rpc); - this.send({ - type: RPCMessageType.PARCNET_CLIENT_READY - }); - } - - public subscriptionUpdate( - { update, subscriptionId }: SubscriptionUpdateResult, - subscriptionSerial: number - ): void { - this.send({ - type: RPCMessageType.PARCNET_CLIENT_SUBSCRIPTION_UPDATE, - update, - subscriptionId, - subscriptionSerial - }); - } -} - -interface ListenResult { - advice: ConnectorAdvice; - zapp: Zapp; -} - -export async function listen(ws: WebSocket): Promise { - let rpcMessageHandler: (ev: MessageEvent) => void; - - return new Promise((resolve) => { - const initialEventHandler = (initialEvent: MessageEvent) => { - const data = InitializationMessageSchema.safeParse( - // eslint-disable-next-line @typescript-eslint/no-base-to-string - JSONBig.parse(initialEvent.data.toString()) - ); - if (!data.success) { - return; - } - - const msg = data.data; - if (msg.type === InitializationMessageType.PARCNET_CLIENT_CONNECT) { - ws.removeEventListener("message", initialEventHandler); - resolve({ - advice: new WebsocketAdviceChannel({ - socket: ws, - onReady: (rpc) => { - rpcMessageHandler = (ev: MessageEvent) => { - const message = RPCMessageSchema.safeParse( - // eslint-disable-next-line @typescript-eslint/no-base-to-string - JSONBig.parse(ev.data.toString()) - ); - if (message.success === false) { - return; - } - - void handleMessage(rpc, ws, message.data); - }; - - ws.addEventListener("message", rpcMessageHandler); - } - }), - zapp: msg.zapp - }); - } - }; - - ws.addEventListener("message", initialEventHandler); - }); -} - -async function handleMessage( - rpc: ParcnetRPC, - socket: WebSocket, - message: RPCMessage -): Promise { - if (message.type === RPCMessageType.PARCNET_CLIENT_INVOKE) { - const path = message.fn.split("."); - const functionName = path.pop(); - if (!functionName) { - throw new Error("Path does not contain a function name"); - } - const object = deepGet(rpc, path); - const functionToInvoke = (object as Record)[functionName]; - try { - if (functionToInvoke && typeof functionToInvoke === "function") { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const result = await functionToInvoke.apply(object, message.args); - socket.send( - JSONBig.stringify({ - type: RPCMessageType.PARCNET_CLIENT_INVOKE_RESULT, - result, - serial: message.serial - } satisfies RPCMessage) - ); - } catch (error) { - socket.send( - JSONBig.stringify({ - type: RPCMessageType.PARCNET_CLIENT_INVOKE_ERROR, - serial: message.serial, - error: (error as Error).message - } satisfies RPCMessage) - ); - } - } else { - throw new Error("Function not found"); - } - } catch (error) { - socket.send( - JSONBig.stringify({ - type: RPCMessageType.PARCNET_CLIENT_INVOKE_ERROR, - error: (error as Error).message - }) - ); - } - } -}