Skip to content

Commit

Permalink
feat: add RpcResponse.parseError and Provider.parseError
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Jan 13, 2025
1 parent efcce91 commit b431077
Show file tree
Hide file tree
Showing 5 changed files with 633 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changeset/olive-games-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": patch
---

Added `RpcResponse.parseError` and `Provider.parseError`.
108 changes: 99 additions & 9 deletions src/core/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Errors from './Errors.js'
import * as RpcResponse from './RpcResponse.js'
import type * as RpcSchema from './RpcSchema.js'
import type * as RpcSchema_internal from './internal/rpcSchema.js'
import type { Compute } from './internal/types.js'
import type { Compute, IsNarrowable, IsNever } from './internal/types.js'

/** Options for a {@link ox#Provider.Provider}. */
export type Options = {
Expand Down Expand Up @@ -92,6 +92,7 @@ export type EventMap = {
/** The user rejected the request. */
export class UserRejectedRequestError extends ProviderRpcError {
static readonly code = 4001
override readonly code = 4001
override readonly name = 'Provider.UserRejectedRequestError'

constructor({
Expand All @@ -104,6 +105,7 @@ export class UserRejectedRequestError extends ProviderRpcError {
/** The requested method and/or account has not been authorized by the user. */
export class UnauthorizedError extends ProviderRpcError {
static readonly code = 4100
override readonly code = 4100
override readonly name = 'Provider.UnauthorizedError'

constructor({
Expand All @@ -116,6 +118,7 @@ export class UnauthorizedError extends ProviderRpcError {
/** The provider does not support the requested method. */
export class UnsupportedMethodError extends ProviderRpcError {
static readonly code = 4200
override readonly code = 4200
override readonly name = 'Provider.UnsupportedMethodError'

constructor({
Expand All @@ -128,6 +131,7 @@ export class UnsupportedMethodError extends ProviderRpcError {
/** The provider is disconnected from all chains. */
export class DisconnectedError extends ProviderRpcError {
static readonly code = 4900
override readonly code = 4900
override readonly name = 'Provider.DisconnectedError'

constructor({
Expand All @@ -140,6 +144,7 @@ export class DisconnectedError extends ProviderRpcError {
/** The provider is not connected to the requested chain. */
export class ChainDisconnectedError extends ProviderRpcError {
static readonly code = 4901
override readonly code = 4901
override readonly name = 'Provider.ChainDisconnectedError'

constructor({
Expand Down Expand Up @@ -385,14 +390,18 @@ export function from(provider: any, options: Options = {}): Provider<Options> {
}
: {}),
async request(args) {
const result = await provider.request(args)
if (
result &&
typeof result === 'object' &&
'jsonrpc' in (result as { jsonrpc?: unknown })
)
return RpcResponse.parse(result) as never
return result
try {
const result = await provider.request(args)
if (
result &&
typeof result === 'object' &&
'jsonrpc' in (result as { jsonrpc?: unknown })
)
return RpcResponse.parse(result) as never
return result
} catch (error) {
throw parseError(error)
}
},
}
}
Expand All @@ -401,6 +410,87 @@ export declare namespace from {
type ErrorType = IsUndefinedError | Errors.GlobalErrorType
}

/**
* Parses an error object into an error instance.
*
* @example
* ```ts twoslash
* import { Provider } from 'ox'
*
* const error = Provider.parseError({ code: 4200, message: 'foo' })
*
* error
* // ^?
*
* ```
*
* @param errorObject - The error object to parse.
* @returns An error instance.
*/
export function parseError<
const errorObject extends RpcResponse.ErrorObject | unknown,
>(
errorObject: errorObject | RpcResponse.ErrorObject,
): parseError.ReturnType<errorObject> {
const errorObject_ = errorObject as RpcResponse.ErrorObject
const error = RpcResponse.parseError(errorObject_)
if (error instanceof RpcResponse.BaseError) {
if (error.code === DisconnectedError.code)
return new DisconnectedError(errorObject_) as never
if (error.code === ChainDisconnectedError.code)
return new ChainDisconnectedError(errorObject_) as never
if (error.code === UserRejectedRequestError.code)
return new UserRejectedRequestError(errorObject_) as never
if (error.code === UnauthorizedError.code)
return new UnauthorizedError(errorObject_) as never
if (error.code === UnsupportedMethodError.code)
return new UnsupportedMethodError(errorObject_) as never
}
return error as never
}

export declare namespace parseError {
type ReturnType<
errorObject extends RpcResponse.ErrorObject | unknown,
//
error = errorObject extends RpcResponse.ErrorObject
?
| (errorObject['code'] extends DisconnectedError['code']
? DisconnectedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? DisconnectedError
: never)
| (errorObject['code'] extends ChainDisconnectedError['code']
? ChainDisconnectedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? ChainDisconnectedError
: never)
| (errorObject['code'] extends UserRejectedRequestError['code']
? UserRejectedRequestError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? UserRejectedRequestError
: never)
| (errorObject['code'] extends UnauthorizedError['code']
? UnauthorizedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? UnauthorizedError
: never)
| (errorObject['code'] extends UnsupportedMethodError['code']
? UnsupportedMethodError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? UnsupportedMethodError
: never)
: RpcResponse.parseError.ReturnType<RpcResponse.ErrorObject>,
> = IsNever<error> extends true
? RpcResponse.parseError.ReturnType<errorObject>
: error
}

/** Thrown when the provider is undefined. */
export class IsUndefinedError extends Errors.BaseError {
override readonly name = 'Provider.IsUndefinedError'
Expand Down
157 changes: 135 additions & 22 deletions src/core/RpcResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Errors, RpcRequest } from '../index.js'
import type {
Compute,
IsNarrowable,
IsNever,
OneOf,
UnionPartialBy,
} from './internal/types.js'
Expand Down Expand Up @@ -215,28 +216,7 @@ export function parse<
const { raw = false } = options
const response_ = response as RpcResponse
if (raw) return response as never
if (response_.error) {
const { code } = response_.error
const JsonRpcError = (() => {
if (code === InternalError.code) return InternalError
if (code === InvalidInputError.code) return InvalidInputError
if (code === InvalidParamsError.code) return InvalidParamsError
if (code === InvalidRequestError.code) return InvalidRequestError
if (code === LimitExceededError.code) return LimitExceededError
if (code === MethodNotFoundError.code) return MethodNotFoundError
if (code === MethodNotSupportedError.code) return MethodNotSupportedError
if (code === ParseError.code) return ParseError
if (code === ResourceNotFoundError.code) return ResourceNotFoundError
if (code === ResourceUnavailableError.code)
return ResourceUnavailableError
if (code === TransactionRejectedError.code)
return TransactionRejectedError
if (code === VersionNotSupportedError.code)
return VersionNotSupportedError
return BaseError
})()
throw new JsonRpcError(response_.error)
}
if (response_.error) throw parseError(response_.error)
return response_.result as never
}

Expand Down Expand Up @@ -283,6 +263,139 @@ export declare namespace parse {
| Errors.GlobalErrorType
}

/**
* Parses a JSON-RPC error object into an error instance.
*
* @example
* ```ts twoslash
* import { RpcResponse } from 'ox'
*
* const error = RpcResponse.parseError({ code: -32000, message: 'unsupported method' })
*
* error
* // ^?
*
* ```
*
* @param errorObject - JSON-RPC error object.
* @returns Error instance.
*/
export function parseError<const errorObject extends ErrorObject | unknown>(
errorObject: errorObject | ErrorObject,
): parseError.ReturnType<errorObject> {
const errorObject_ = errorObject as ErrorObject
const { code } = errorObject_
if (code === InternalError.code)
return new InternalError(errorObject_) as never
if (code === InvalidInputError.code)
return new InvalidInputError(errorObject_) as never
if (code === InvalidParamsError.code)
return new InvalidParamsError(errorObject_) as never
if (code === InvalidRequestError.code)
return new InvalidRequestError(errorObject_) as never
if (code === LimitExceededError.code)
return new LimitExceededError(errorObject_) as never
if (code === MethodNotFoundError.code)
return new MethodNotFoundError(errorObject_) as never
if (code === MethodNotSupportedError.code)
return new MethodNotSupportedError(errorObject_) as never
if (code === ParseError.code) return new ParseError(errorObject_) as never
if (code === ResourceNotFoundError.code)
return new ResourceNotFoundError(errorObject_) as never
if (code === ResourceUnavailableError.code)
return new ResourceUnavailableError(errorObject_) as never
if (code === TransactionRejectedError.code)
return new TransactionRejectedError(errorObject_) as never
if (code === VersionNotSupportedError.code)
return new VersionNotSupportedError(errorObject_) as never
return new BaseError(errorObject_) as never
}

export declare namespace parseError {
type ReturnType<
errorObject extends ErrorObject | unknown,
//
error = errorObject extends ErrorObject
?
| (errorObject['code'] extends InternalError['code']
? InternalError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? InternalError
: never)
| (errorObject['code'] extends InvalidInputError['code']
? InvalidInputError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? InvalidInputError
: never)
| (errorObject['code'] extends ResourceNotFoundError['code']
? ResourceNotFoundError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? ResourceNotFoundError
: never)
| (errorObject['code'] extends ResourceUnavailableError['code']
? ResourceUnavailableError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? ResourceUnavailableError
: never)
| (errorObject['code'] extends TransactionRejectedError['code']
? TransactionRejectedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? TransactionRejectedError
: never)
| (errorObject['code'] extends ParseError['code']
? ParseError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? ParseError
: never)
| (errorObject['code'] extends MethodNotSupportedError['code']
? MethodNotSupportedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? MethodNotSupportedError
: never)
| (errorObject['code'] extends LimitExceededError['code']
? LimitExceededError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? LimitExceededError
: never)
| (errorObject['code'] extends VersionNotSupportedError['code']
? VersionNotSupportedError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? VersionNotSupportedError
: never)
| (errorObject['code'] extends InvalidRequestError['code']
? InvalidRequestError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? InvalidRequestError
: never)
| (errorObject['code'] extends MethodNotFoundError['code']
? MethodNotFoundError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? MethodNotFoundError
: never)
| (errorObject['code'] extends InvalidParamsError['code']
? InvalidParamsError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? InvalidParamsError
: never)
| (IsNarrowable<errorObject['code'], number> extends false
? BaseError
: never)
: parseError.ReturnType<ErrorObject>,
> = IsNever<error> extends true ? BaseError : error
}

export type BaseErrorType = BaseError & { name: 'BaseError' }

/** Thrown when a JSON-RPC error has occurred. */
Expand Down
Loading

0 comments on commit b431077

Please sign in to comment.