npm package: @northsea4/proxy-service
Description: A type-safe wrapper around the web extension messaging APIs that lets you call a function from anywhere, but execute it in the background. Forked from @webext-core/proxy-service with sender parameter support.
Author: northsea4
Latest version: 1.0.2

# @northsea4/proxy-service

基于 [@webext-core/proxy-service](https://github.com/aklinker1/webext-core/tree/main/packages/proxy-service) 的二次开发版本，支持 ServiceCallContext 上下文对象，提供更丰富的调用上下文信息。

类型安全的浏览器扩展消息传递 API 封装，允许你在任何地方调用函数，但在后台脚本中执行。支持所有主流浏览器（Chrome、Firefox、Safari 等）。

## 主要特性

- 支持 ServiceCallContext：服务方法可获取完整的调用上下文（sender、requestId、timestamp、metadata 等）
- 类型安全的服务注册与调用
- 自动注入 context，无需手动传递

## 安装

```bash
pnpm add @northsea4/proxy-service
```

## 快速开始

### 1. 定义服务

```ts
// services/tab-service.ts
import { defineProxyService, type ServiceCallContext } from '@northsea4/proxy-service'

class TabService {
  async getCurrentTabInfo(context?: ServiceCallContext) {
    return {
      tabId: context?.sender?.tab?.id,
      url: context?.sender?.tab?.url,
      title: context?.sender?.tab?.title,
      requestId: context?.requestId,
      timestamp: context?.timestamp
    }
  }

  async closeTab(tabId: number, context?: ServiceCallContext) {
    console.log('closeTab called from:', context?.sender?.tab?.url)
    await browser.tabs.remove(tabId)
  }
}

export const [registerTabService, getTabService] = defineProxyService(
  'tab-service',
  () => new TabService()
)
```

### 2. 注册服务（Background）

```ts
// background/index.ts
import { registerTabService } from '@/services/tab-service'

registerTabService()
```

### 3. 使用服务（任何地方）

```ts
// content-script.ts 或 popup.ts
import { getTabService } from '@/services/tab-service'

const tabService = getTabService()

// context 会自动注入
const tabInfo = await tabService.getCurrentTabInfo()
console.log('Current tab:', tabInfo)

await tabService.closeTab(tabInfo.tabId)
```

## ServiceCallContext 类型

```ts
import type { ServiceCallContext } from '@northsea4/proxy-service'

interface ServiceCallContext {
  sender: Browser.Runtime.MessageSender
  timestamp?: number
  requestId?: string
  metadata?: Record<string, any>
}
```

服务方法的最后一个参数应为 `context?: ServiceCallContext`，并通过 `context?.sender` 获取调用方信息。

## 常见场景示例

### 1. 获取调用者信息

```ts
class ContextService {
  async getCallerInfo(context?: ServiceCallContext) {
    return {
      fromTab: context?.sender?.tab?.id,
      fromUrl: context?.sender?.tab?.url,
      fromFrame: context?.sender?.frameId,
      requestId: context?.requestId,
      timestamp: context?.timestamp
    }
  }
}
```

### 2. 权限检查

```ts
class PermissionService {
  async checkPermission(action: string, context?: ServiceCallContext) {
    const url = context?.sender?.tab?.url
    if (url?.startsWith('https://trusted-domain.com')) {
      return true
    }
    return false
  }
}
```

### 3. 日志追踪

```ts
class LoggingService {
  async logAction(action: string, data: any, context?: ServiceCallContext) {
    console.log({
      requestId: context?.requestId,
      timestamp: context?.timestamp,
      caller: context?.sender?.tab?.title,
      action,
      data
    })
  }
}
```

## TypeScript 类型支持

服务方法签名应为：

```ts
import type { ServiceCallContext } from '@northsea4/proxy-service'

interface MyService {
  method1(arg1: string, context?: ServiceCallContext): Promise<void>
  method2(arg1: number, arg2: string, context?: ServiceCallContext): Promise<number>
}
```

## 高级功能

### flattenPromise

简化异步依赖的处理：

```ts
import { flattenPromise } from '@northsea4/proxy-service'

function createService(dbPromise: Promise<IDBDatabase>) {
  const db = flattenPromise(dbPromise)

  return {
    async getData(context?: ServiceCallContext) {
      return await db.get('store', 'key')
    }
  }
}
```

### defineProxyService

简化服务定义和类型推导：

```ts
// services/my-service.ts
import { defineProxyService, type ServiceCallContext } from '@northsea4/proxy-service'

class MyService {
  async add(x: number, y: number, context?: ServiceCallContext) {
    return x + y
  }
}

export const [registerMyService, getMyService] = defineProxyService(
  'my-service',
  () => new MyService()
)

// background/index.ts
registerMyService()

// content-script.ts
const myService = getMyService()
await myService.add(1, 2)
```

## 配置选项

支持 `@webext-core/messaging` 的所有配置项：

```ts
import { registerService, createProxyService } from '@northsea4/proxy-service'

const config = {
  logger: console // 自定义日志记录器
}

registerService('my-service', myService, config)
const proxy = createProxyService('my-service', config)
```

## 开发

```bash
# 构建
pnpm build

# 开发模式（监听文件变化）
pnpm dev
```

## License

MIT

## 致谢

本项目基于 [@webext-core/proxy-service](https://github.com/aklinker1/webext-core/tree/main/packages/proxy-service) 开发，感谢原作者的优秀工作。