Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return error objects #16

Merged
merged 1 commit into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hungry-llamas-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@farcaster/connect": patch
---

Return error responses
18 changes: 16 additions & 2 deletions packages/connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const channel = await appClient.connect({
channelToken: string;
connectURI: string;
}
isError: boolean;
error: Error;
}
```

Expand Down Expand Up @@ -124,6 +126,8 @@ const status = await appClient.status({
displayName?: string
pfpUrl?: string
}
isError: boolean
error: Error
}
```

Expand Down Expand Up @@ -171,6 +175,8 @@ const status = await appClient.watchStatus({
displayName?: string
pfpUrl?: string
}
isError: boolean
error: Error
}
```

Expand All @@ -186,7 +192,7 @@ Example: `"210f1718-427e-46a4-99e3-2207f21f83ec"`

###### timeout

Polling timeout, in milliseconds. If the connect request is not completed before the timeout, `watchStatus` throws.
Polling timeout, in milliseconds. If the connect request is not completed before the timeout, `watchStatus` returns an error.

Type: `number`

Expand Down Expand Up @@ -235,6 +241,8 @@ const { data, success, fid } = await appClient.verifySignInMessage({
data: SiweMessage,
success: boolean,
fid: number
isError: boolean
error: Error
}
```

Expand Down Expand Up @@ -279,7 +287,7 @@ Parse the Sign In With Farcaster URI provided by a connected app user.

Returns the parsed parameters. Your app should use these to construct a Sign In With Farcaster message.

Throws if URI is invalid.
Returns an error if URI is invalid.

```ts
const params = authClient.parseSignInURI({
Expand All @@ -300,6 +308,8 @@ const params = authClient.parseSignInURI({
expirationTime?: string
requestId?: string
}
isError: boolean
error: Error
}
```

Expand Down Expand Up @@ -337,6 +347,8 @@ const { siweMessage, message } = authClient.buildSignInMessage({
{
siweMessage: SiweMessage;
message: string;
isError: boolean;
error: Error;
}
```

Expand Down Expand Up @@ -438,6 +450,8 @@ const params = authClient.authenticate({
displayName?: string
pfpUrl?: string
}
isError: boolean
error: Error
}
```

Expand Down
15 changes: 15 additions & 0 deletions packages/connect/src/actions/app/connect.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createAppClient } from "../../clients/createAppClient";
import { jest } from "@jest/globals";
import { viem } from "../../clients/ethereum/viem";
import { ConnectError } from "../../errors";

describe("connect", () => {
const client = createAppClient({
Expand Down Expand Up @@ -45,4 +46,18 @@ describe("connect", () => {
},
});
});

test("handles errors", async () => {
const spy = jest.spyOn(global, "fetch").mockRejectedValue(new Error("some error"));

const res = await client.connect({
siweUri,
domain,
nonce,
});

expect(spy).toHaveBeenCalledTimes(1);
expect(res.isError).toBe(true);
expect(res.error).toEqual(new ConnectError("unknown", "some error"));
});
});
8 changes: 5 additions & 3 deletions packages/connect/src/actions/app/connect.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { AsyncUnwrapped, unwrap } from "../../errors";
import { Client } from "../../clients/createClient";
import { AsyncHttpResponse, post } from "../../clients/transports/http";
import { HttpResponse, post } from "../../clients/transports/http";

export type ConnectArgs = ConnectRequest;
export type ConnectResponse = AsyncHttpResponse<ConnectAPIResponse>;
export type ConnectResponse = AsyncUnwrapped<HttpResponse<ConnectAPIResponse>>;

interface ConnectRequest {
siweUri: string;
Expand All @@ -21,5 +22,6 @@ interface ConnectAPIResponse {
const path = "connect";

export const connect = async (client: Client, { ...request }: ConnectArgs): ConnectResponse => {
return post<ConnectRequest, ConnectAPIResponse>(client, path, request);
const response = await post<ConnectRequest, ConnectAPIResponse>(client, path, request);
return unwrap(response);
};
10 changes: 7 additions & 3 deletions packages/connect/src/actions/app/status.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { AsyncUnwrapped, unwrap } from "../../errors";
import { Client } from "../../clients/createClient";
import { get, AsyncHttpResponse } from "../../clients/transports/http";
import { get, HttpResponse } from "../../clients/transports/http";

export interface StatusArgs {
channelToken: string;
}

export type StatusResponse = AsyncHttpResponse<StatusAPIResponse>;
export type StatusResponse = AsyncUnwrapped<HttpResponse<StatusAPIResponse>>;

interface StatusAPIResponse {
state: "pending" | "completed";
Expand All @@ -22,5 +23,8 @@ interface StatusAPIResponse {
const path = "connect/status";

export const status = async (client: Client, { channelToken }: StatusArgs): StatusResponse => {
return get<StatusAPIResponse>(client, path, { authToken: channelToken });
const response = await get<StatusAPIResponse>(client, path, {
authToken: channelToken,
});
return unwrap(response);
};
30 changes: 14 additions & 16 deletions packages/connect/src/actions/app/verifySignInMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ describe("verifySignInMessage", () => {
});

const errMsg = `Invalid resource: signer ${account.address} does not own fid 1234.`;
const error = new ConnectError("unauthorized", errMsg);

await expect(
client.verifySignInMessage({
message,
signature,
}),
).rejects.toStrictEqual(error);
const err = new ConnectError("unauthorized", errMsg);
const { isError, error } = await client.verifySignInMessage({
message,
signature,
});
expect(isError).toBe(true);
expect(error).toStrictEqual(err);
});

test("verifies 1271 sign in message", async () => {
Expand All @@ -59,13 +58,12 @@ describe("verifySignInMessage", () => {
});

const errMsg = `Invalid resource: signer ${LGTM} does not own fid 1234.`;
const error = new ConnectError("unauthorized", errMsg);

await expect(
client.verifySignInMessage({
message,
signature,
}),
).rejects.toStrictEqual(error);
const err = new ConnectError("unauthorized", errMsg);
const { isError, error } = await client.verifySignInMessage({
message,
signature,
});
expect(isError).toBe(true);
expect(error).toStrictEqual(err);
});
});
11 changes: 4 additions & 7 deletions packages/connect/src/actions/app/verifySignInMessage.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { SiweMessage } from "siwe";
import { Client } from "../../clients/createClient";
import { SignInResponse, verify } from "../../messages/verify";
import { VerifyResponse, verify } from "../../messages/verify";
import { Unwrapped, unwrap } from "../../errors";

export interface VerifySignInMessageArgs {
message: string | Partial<SiweMessage>;
signature: `0x${string}`;
}

export type VerifySignInMessageResponse = Promise<SignInResponse>;
export type VerifySignInMessageResponse = Promise<Unwrapped<VerifyResponse>>;

export const verifySignInMessage = async (
client: Client,
Expand All @@ -17,9 +18,5 @@ export const verifySignInMessage = async (
getFid: client.ethereum.getFid,
provider: client.ethereum.provider,
});
if (result.isErr()) {
throw result.error;
} else {
return result.value;
}
return unwrap(result);
};
8 changes: 5 additions & 3 deletions packages/connect/src/actions/app/watchStatus.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AsyncUnwrapped, unwrap } from "../../errors";
import { Client } from "../../clients/createClient";
import { AsyncHttpResponse, poll, HttpResponse } from "../../clients/transports/http";
import { poll, HttpResponse } from "../../clients/transports/http";

export interface WatchStatusArgs {
channelToken: string;
Expand All @@ -8,7 +9,7 @@ export interface WatchStatusArgs {
onResponse?: (response: HttpResponse<StatusAPIResponse>) => void;
}

export type WatchStatusResponse = AsyncHttpResponse<StatusAPIResponse>;
export type WatchStatusResponse = AsyncUnwrapped<HttpResponse<StatusAPIResponse>>;

interface StatusAPIResponse {
state: "pending" | "completed";
Expand All @@ -28,7 +29,7 @@ const path = "connect/status";
const voidCallback = () => {};

export const watchStatus = async (client: Client, args: WatchStatusArgs): WatchStatusResponse => {
return poll<StatusAPIResponse>(
const result = await poll<StatusAPIResponse>(
client,
path,
{
Expand All @@ -38,4 +39,5 @@ export const watchStatus = async (client: Client, args: WatchStatusArgs): WatchS
},
{ authToken: args.channelToken },
);
return unwrap(result);
};
10 changes: 7 additions & 3 deletions packages/connect/src/actions/auth/authenticate.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { StatusResponse } from "../app/status";
import { post, AsyncHttpResponse } from "../../clients/transports/http";
import { post, HttpResponse } from "../../clients/transports/http";
import { Client } from "../../clients/createClient";
import { AsyncUnwrapped, unwrap } from "../../errors";

export interface AuthenticateArgs extends AuthenticateRequest {
channelToken: string;
}

export type AuthenticateResponse = AsyncHttpResponse<AuthenticateAPIResponse>;
export type AuthenticateResponse = AsyncUnwrapped<HttpResponse<AuthenticateAPIResponse>>;

interface AuthenticateRequest {
message: string;
Expand All @@ -26,5 +27,8 @@ export const authenticate = async (
client: Client,
{ channelToken, ...request }: AuthenticateArgs,
): AuthenticateResponse => {
return post<AuthenticateRequest, AuthenticateAPIResponse>(client, path, request, { authToken: channelToken });
const result = await post<AuthenticateRequest, AuthenticateAPIResponse>(client, path, request, {
authToken: channelToken,
});
return unwrap(result);
};
17 changes: 4 additions & 13 deletions packages/connect/src/actions/auth/buildSignInMessage.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import { Client } from "clients/createClient";
import { build, SignInMessageParams } from "../../messages/build";
import { SiweMessage } from "siwe";
import { build, BuildResponse, SignInMessageParams } from "../../messages/build";
import { Unwrapped, unwrap } from "../../errors";

export type BuildSignInMessageArgs = SignInMessageParams;
export interface BuildSignInMessageResponse {
siweMessage: SiweMessage;
message: string;
}
export type BuildSignInMessageResponse = Unwrapped<BuildResponse>;

export const buildSignInMessage = (_client: Client, args: BuildSignInMessageArgs): BuildSignInMessageResponse => {
const result = build(args);
if (result.isErr()) {
throw result.error;
} else {
const siweMessage = result.value;
return { siweMessage, message: siweMessage.toMessage() };
}
return unwrap(build(args));
};
10 changes: 3 additions & 7 deletions packages/connect/src/actions/auth/parseSignInURI.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { Unwrapped, unwrap } from "../../errors";
import { Client } from "../../clients/createClient";
import { parseSignInURI as parse, ParsedSignInURI } from "../../messages/parseSignInURI";

export interface ParseSignInURIArgs {
uri: string;
}
export type ParseSignInURIResponse = ParsedSignInURI;
export type ParseSignInURIResponse = Unwrapped<ParsedSignInURI>;

export const parseSignInURI = (_client: Client, { uri }: ParseSignInURIArgs): ParseSignInURIResponse => {
const result = parse(uri);
if (result.isErr()) {
throw result.error;
} else {
return result.value;
}
return unwrap(parse(uri));
};
Loading