Skip to content

Commit

Permalink
✨ feat: background error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
axuj committed Jul 17, 2024
1 parent 57b9384 commit 53f4d97
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 29 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"webextension-polyfill": "^0.12.0"
},
"devDependencies": {
"@swc/core": "^1.6.13",
"@types/webextension-polyfill": "^0.10.7",
"@vitest/coverage-v8": "^2.0.3",
"tsup": "^8.1.1",
Expand Down
139 changes: 137 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions src/createBackgroundHandler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Browser from 'webextension-polyfill'
import { C2BMessage, RouterRecord } from './types'
import { toError } from './utils/util'
import { handleCall } from './utils/handleCall'
import { DEFAULT_PORT_NAME } from './port_name'
import { toErrorMessage } from './utils'

export function createBackgroundHandler(
router: RouterRecord,
Expand All @@ -15,8 +15,9 @@ export function createBackgroundHandler(
port.onMessage.addListener(async (message: C2BMessage) => {
const { calls, args } = message

function throwError(error: Error) {
port.postMessage({ error: error.message })
function throwError(error: unknown) {
const error_message = toErrorMessage(error)
port.postMessage({ error: error_message })
throw error
}
try {
Expand All @@ -26,7 +27,7 @@ export function createBackgroundHandler(
port.postMessage(message)
})
} catch (error) {
throwError(toError(error))
throwError(error)
} finally {
port.disconnect()
}
Expand Down
18 changes: 9 additions & 9 deletions src/createWebextRpcCaller.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import Browser from 'webextension-polyfill'
import { ExposedPromise } from './utils'
import type {
B2CMessage,
C2BMessage,
PromisifiedRouter,
RouterRecord,
} from './types'
import type { B2CMessage, RpcRouter, RouterRecord } from './types'
import { MessageStream } from './utils'
import { createRpcCaller } from './utils/createRpcCaller'
import { DEFAULT_PORT_NAME } from './port_name'

export const createWebextRpcCaller = <T extends RouterRecord>(
port_name: string = DEFAULT_PORT_NAME
): PromisifiedRouter<T> => {
): RpcRouter<T> => {
return createRpcCaller((calls, args) => {
const port = Browser.runtime.connect({
name: port_name,
Expand All @@ -21,8 +16,13 @@ export const createWebextRpcCaller = <T extends RouterRecord>(
const exposedPromise = new ExposedPromise()
let messageStream: MessageStream<any> | null = null
port.onMessage.addListener((message: B2CMessage) => {
if (message.error != undefined) {
throw new Error(message.error)
if (message.error) {
const error = new Error(message.error.message)
error.stack =
'[webext-rpc background function error]' + message.error.stack
error.name = message.error.name
exposedPromise.reject(error)
return
}

// stream
Expand Down
18 changes: 12 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ interface NonStreamMessage extends BaseMessage {
isStream: false
}

interface ErrorMessage {
error: string
export interface ErrorSerializable {
message: string
stack?: string
name: string
}

export interface ErrorMessage {
error: ErrorSerializable
}

export type B2CMessage = ErrorMessage | StreamMessage | NonStreamMessage
Expand All @@ -27,7 +33,7 @@ export interface C2BMessage {
args: any[]
}

export type Promisify<T> = T extends (...args: infer A) => infer R
export type RpcFunction<T> = T extends (...args: infer A) => infer R
? (
...args: A
) => R extends Promise<any>
Expand All @@ -37,8 +43,8 @@ export type Promisify<T> = T extends (...args: infer A) => infer R
: Promise<R>
: Promise<T>

export type PromisifiedRouter<T> = {
export type RpcRouter<T> = {
[K in keyof T]: T[K] extends (...args: any) => any
? Promisify<T[K]>
: PromisifiedRouter<T[K]>
? RpcFunction<T[K]>
: RpcRouter<T[K]>
}
6 changes: 3 additions & 3 deletions src/utils/createRpcCaller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PromisifiedRouter, RouterRecord } from '../types'
import { RpcRouter, RouterRecord } from '../types'

export function createRpcCaller<T extends RouterRecord>(
callbacks: (calls: string[], args: any[]) => Promise<any>
): PromisifiedRouter<T> {
): RpcRouter<T> {
const createProxyObject = (calls: string[] = []): any => {
const handler: ProxyHandler<any> = {
get(_thisArg, prop, receiver) {
Expand All @@ -15,5 +15,5 @@ export function createRpcCaller<T extends RouterRecord>(
return new Proxy(() => {}, handler)
}

return createProxyObject() as PromisifiedRouter<T>
return createProxyObject() as RpcRouter<T>
}
9 changes: 7 additions & 2 deletions src/utils/handleCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@ export async function handleCall(
sendMessage({ isStream: false, value })
return
}
sendMessage({ error: 'Unsupported generator type' })
throw new Error('Unsupported generator type')
const error = {
message: 'Unsupported type of generator',
stack: new Error().stack,
name: 'UnsupportedType',
}
sendMessage({ error })
throw error
}

//Other
Expand Down
16 changes: 13 additions & 3 deletions src/utils/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
export function toError(error: unknown) {
import { ErrorSerializable } from '../types'

export function toErrorMessage(error: unknown): ErrorSerializable {
if (error instanceof Error) {
return error
return {
message: error.message,
stack: error.stack,
name: error.name,
}
}
return {
message: String(error),
stack: '',
name: 'UnknownError',
}
return new Error(String(error))
}

0 comments on commit 53f4d97

Please sign in to comment.