@northsea4/proxy-service 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +107 -180
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,26 +1,16 @@
1
1
  # @northsea4/proxy-service
2
2
 
3
- 基于 [@webext-core/proxy-service](https://github.com/aklinker1/webext-core/tree/main/packages/proxy-service) 的二次开发版本,添加了 `sender` 参数支持。
4
3
 
5
- 类型安全的浏览器扩展消息传递 API 封装,允许你在任何地方调用函数,但在后台脚本中执行。支持所有浏览器(Chrome、Firefox、Safari 等)。
4
+ 基于 [@webext-core/proxy-service](https://github.com/aklinker1/webext-core/tree/main/packages/proxy-service) 的二次开发版本,支持 ServiceCallContext 上下文对象,提供更丰富的调用上下文信息。
6
5
 
7
- ## 主要修改
6
+ 类型安全的浏览器扩展消息传递 API 封装,允许你在任何地方调用函数,但在后台脚本中执行。支持所有主流浏览器(Chrome、Firefox、Safari 等)。
8
7
 
9
- 相比原版 `@webext-core/proxy-service`,本包的主要修改:
10
8
 
11
- **`registerService` 函数现在会将 `sender` 参数传递给服务方法:**
9
+ ## 主要特性
12
10
 
13
- 当代理服务的方法被调用时,消息的 `sender` 对象(包含 taburl 等信息)会作为最后一个参数传递给实际的服务方法。这使得服务可以获取调用者的上下文信息。
14
-
15
- ```ts
16
- // 原版行为
17
- registerService(key, service);
18
- // 调用 service.method(arg1, arg2)
19
-
20
- // 修改后的行为
21
- registerService(key, service);
22
- // 调用 service.method(arg1, arg2, sender)
23
- ```
11
+ - 支持 ServiceCallContext:服务方法可获取完整的调用上下文(sender、requestId、timestamp、metadata 等)
12
+ - 类型安全的服务注册与调用
13
+ - 自动注入 context,无需手动传递
24
14
 
25
15
  ## 安装
26
16
 
@@ -30,270 +20,206 @@ pnpm add @northsea4/proxy-service
30
20
 
31
21
  ## 快速开始
32
22
 
23
+
33
24
  ### 1. 定义服务
34
25
 
35
26
  ```ts
36
27
  // services/tab-service.ts
37
- import type { Runtime } from 'webextension-polyfill';
28
+ import { defineProxyService, type ServiceCallContext } from '@northsea4/proxy-service'
38
29
 
39
- export class TabService {
40
- // sender 参数会自动注入为最后一个参数
41
- async getCurrentTabInfo(sender: Runtime.MessageSender) {
30
+ class TabService {
31
+ async getCurrentTabInfo(context?: ServiceCallContext) {
42
32
  return {
43
- tabId: sender.tab?.id,
44
- url: sender.tab?.url,
45
- title: sender.tab?.title,
46
- };
33
+ tabId: context?.sender?.tab?.id,
34
+ url: context?.sender?.tab?.url,
35
+ title: context?.sender?.tab?.title,
36
+ requestId: context?.requestId,
37
+ timestamp: context?.timestamp,
38
+ }
47
39
  }
48
40
 
49
- async closeTab(tabId: number, sender: Runtime.MessageSender) {
50
- console.log('closeTab called from:', sender.tab?.url);
51
- await browser.tabs.remove(tabId);
41
+ async closeTab(tabId: number, context?: ServiceCallContext) {
42
+ console.log('closeTab called from:', context?.sender?.tab?.url)
43
+ await browser.tabs.remove(tabId)
52
44
  }
53
45
  }
54
- ```
55
-
56
- ### 2. 创建类型安全的服务键
57
46
 
58
- ```ts
59
- // services/keys.ts
60
- import type { ProxyServiceKey } from '@northsea4/proxy-service';
61
- import type { TabService } from './tab-service';
62
-
63
- export const TAB_SERVICE_KEY = 'tab-service' as ProxyServiceKey<TabService>;
47
+ export const [registerTabService, getTabService] = defineProxyService(
48
+ 'tab-service',
49
+ () => new TabService()
50
+ )
64
51
  ```
65
52
 
66
- ### 3. 在后台脚本中注册服务
53
+
54
+ ### 2. 注册服务(Background)
67
55
 
68
56
  ```ts
69
- // background.ts
70
- import { registerService } from '@northsea4/proxy-service';
71
- import { TabService } from './services/tab-service';
72
- import { TAB_SERVICE_KEY } from './services/keys';
73
-
74
- // 注册服务
75
- const tabService = new TabService();
76
- registerService(TAB_SERVICE_KEY, tabService);
57
+ // background/index.ts
58
+ import { registerTabService } from '@/services/tab-service'
59
+
60
+ registerTabService()
77
61
  ```
78
62
 
79
- ### 4. 在其他上下文中使用服务
63
+ ### 3. 使用服务(任何地方)
80
64
 
81
65
  ```ts
82
66
  // content-script.ts 或 popup.ts
83
- import { createProxyService } from '@northsea4/proxy-service';
84
- import { TAB_SERVICE_KEY } from './services/keys';
67
+ import { getTabService } from '@/services/tab-service'
85
68
 
86
- const tabService = createProxyService(TAB_SERVICE_KEY);
69
+ const tabService = getTabService()
87
70
 
88
- // 调用服务方法(不需要手动传递 sender,它会自动注入)
89
- const tabInfo = await tabService.getCurrentTabInfo();
90
- console.log('Current tab:', tabInfo);
71
+ // context 会自动注入
72
+ const tabInfo = await tabService.getCurrentTabInfo()
73
+ console.log('Current tab:', tabInfo)
91
74
 
92
- // 关闭当前标签页
93
- await tabService.closeTab(tabInfo.tabId);
75
+ await tabService.closeTab(tabInfo.tabId)
94
76
  ```
95
77
 
96
- ## 服务定义方式
97
78
 
98
- 支持多种服务定义方式:
99
-
100
- ### 类
79
+ ## ServiceCallContext 类型
101
80
 
102
81
  ```ts
103
- export class MathService {
104
- add(x: number, y: number, sender: Runtime.MessageSender): number {
105
- console.log('add called from:', sender.tab?.url);
106
- return x + y;
107
- }
108
- }
109
- ```
110
-
111
- ### 对象
82
+ import type { ServiceCallContext } from '@northsea4/proxy-service'
112
83
 
113
- ```ts
114
- export function createMathService() {
115
- return {
116
- add(x: number, y: number, sender: Runtime.MessageSender): number {
117
- console.log('add called from:', sender.tab?.url);
118
- return x + y;
119
- },
120
- };
84
+ interface ServiceCallContext {
85
+ sender: Browser.Runtime.MessageSender
86
+ timestamp?: number
87
+ requestId?: string
88
+ metadata?: Record<string, any>
121
89
  }
122
90
  ```
123
91
 
124
- ### 函数
92
+ 服务方法的最后一个参数应为 `context?: ServiceCallContext`,并通过 `context?.sender` 获取调用方信息。
125
93
 
126
- ```ts
127
- export type SayHello = (name: string, sender: Runtime.MessageSender) => Promise<string>;
128
94
 
129
- export const sayHello: SayHello = async (name, sender) => {
130
- console.log('sayHello called from:', sender.tab?.url);
131
- return `Hello, ${name}!`;
132
- };
133
- ```
95
+ ## 常见场景示例
134
96
 
135
- ### 嵌套对象
136
-
137
- ```ts
138
- export function createApi() {
139
- return {
140
- math: {
141
- add(x: number, y: number, sender: Runtime.MessageSender): number {
142
- return x + y;
143
- },
144
- },
145
- string: {
146
- uppercase(str: string, sender: Runtime.MessageSender): string {
147
- return str.toUpperCase();
148
- },
149
- },
150
- };
151
- }
152
-
153
- // 使用
154
- registerService('api', createApi());
155
- const api = createProxyService('api');
156
- await api.math.add(1, 2);
157
- await api.string.uppercase('hello');
158
- ```
159
-
160
- ## Sender 参数详解
161
-
162
- `sender` 参数是一个 `Runtime.MessageSender` 对象,包含以下信息:
163
-
164
- ```ts
165
- interface MessageSender {
166
- tab?: Tab; // 发送消息的标签页信息
167
- frameId?: number; // 发送消息的框架 ID
168
- id?: string; // 发送消息的扩展 ID
169
- url?: string; // 发送消息的页面 URL
170
- tlsChannelId?: string;
171
- }
172
- ```
173
-
174
- ### 使用场景示例
175
-
176
- #### 1. 获取调用者的标签页信息
97
+ ### 1. 获取调用者信息
177
98
 
178
99
  ```ts
179
100
  class ContextService {
180
- async getCallerInfo(sender: Runtime.MessageSender) {
101
+ async getCallerInfo(context?: ServiceCallContext) {
181
102
  return {
182
- fromTab: sender.tab?.id,
183
- fromUrl: sender.tab?.url,
184
- fromFrame: sender.frameId,
185
- };
103
+ fromTab: context?.sender?.tab?.id,
104
+ fromUrl: context?.sender?.tab?.url,
105
+ fromFrame: context?.sender?.frameId,
106
+ requestId: context?.requestId,
107
+ timestamp: context?.timestamp,
108
+ }
186
109
  }
187
110
  }
188
111
  ```
189
112
 
190
- #### 2. 根据调用者执行不同逻辑
113
+ ### 2. 权限检查
191
114
 
192
115
  ```ts
193
116
  class PermissionService {
194
- async checkPermission(action: string, sender: Runtime.MessageSender) {
195
- const url = sender.tab?.url;
117
+ async checkPermission(action: string, context?: ServiceCallContext) {
118
+ const url = context?.sender?.tab?.url
196
119
  if (url?.startsWith('https://trusted-domain.com')) {
197
- return true;
120
+ return true
198
121
  }
199
- return false;
122
+ return false
200
123
  }
201
124
  }
202
125
  ```
203
126
 
204
- #### 3. 操作调用者的标签页
127
+ ### 3. 日志追踪
205
128
 
206
129
  ```ts
207
- class TabControlService {
208
- async closeCallerTab(sender: Runtime.MessageSender) {
209
- if (sender.tab?.id) {
210
- await browser.tabs.remove(sender.tab.id);
211
- }
212
- }
213
-
214
- async reloadCallerTab(sender: Runtime.MessageSender) {
215
- if (sender.tab?.id) {
216
- await browser.tabs.reload(sender.tab.id);
217
- }
130
+ class LoggingService {
131
+ async logAction(action: string, data: any, context?: ServiceCallContext) {
132
+ console.log({
133
+ requestId: context?.requestId,
134
+ timestamp: context?.timestamp,
135
+ caller: context?.sender?.tab?.title,
136
+ action,
137
+ data
138
+ })
218
139
  }
219
140
  }
220
141
  ```
221
142
 
143
+
222
144
  ## TypeScript 类型支持
223
145
 
224
- 在定义服务类型时,记得在每个方法的最后添加 `sender` 参数:
146
+ 服务方法签名应为:
225
147
 
226
148
  ```ts
227
- import type { Runtime } from 'webextension-polyfill';
149
+ import type { ServiceCallContext } from '@northsea4/proxy-service'
228
150
 
229
- // 正确的类型定义
230
151
  interface MyService {
231
- method1(arg1: string, sender: Runtime.MessageSender): Promise<void>;
232
- method2(arg1: number, arg2: string, sender: Runtime.MessageSender): Promise<number>;
233
- }
234
-
235
- // 错误的类型定义(缺少 sender 参数)
236
- interface MyServiceWrong {
237
- method1(arg1: string): Promise<void>; // ❌ 运行时会出错
152
+ method1(arg1: string, context?: ServiceCallContext): Promise<void>
153
+ method2(arg1: number, arg2: string, context?: ServiceCallContext): Promise<number>
238
154
  }
239
155
  ```
240
156
 
241
157
  ## 高级功能
242
158
 
159
+
243
160
  ### flattenPromise
244
161
 
245
162
  简化异步依赖的处理:
246
163
 
247
164
  ```ts
248
- import { flattenPromise } from '@northsea4/proxy-service';
165
+ import { flattenPromise } from '@northsea4/proxy-service'
249
166
 
250
167
  function createService(dbPromise: Promise<IDBDatabase>) {
251
- const db = flattenPromise(dbPromise);
168
+ const db = flattenPromise(dbPromise)
252
169
 
253
170
  return {
254
- async getData(sender: Runtime.MessageSender) {
255
- // 不需要 await dbPromise,可以直接使用
256
- return await db.get('store', 'key');
257
- // 而不是: await (await dbPromise).get('store', 'key');
171
+ async getData(context?: ServiceCallContext) {
172
+ return await db.get('store', 'key')
258
173
  },
259
- };
174
+ }
260
175
  }
261
176
  ```
262
177
 
178
+
263
179
  ### defineProxyService
264
180
 
265
- 简化服务定义和使用的工具函数:
181
+ 简化服务定义和类型推导:
266
182
 
267
183
  ```ts
268
- // service.ts
269
- export const [registerMathService, getMathService] = defineProxyService(
270
- 'math-service',
271
- () => new MathService(),
272
- );
184
+ // services/my-service.ts
185
+ import { defineProxyService, type ServiceCallContext } from '@northsea4/proxy-service'
186
+
187
+ class MyService {
188
+ async add(x: number, y: number, context?: ServiceCallContext) {
189
+ return x + y
190
+ }
191
+ }
273
192
 
274
- // background.ts
275
- registerMathService();
193
+ export const [registerMyService, getMyService] = defineProxyService(
194
+ 'my-service',
195
+ () => new MyService()
196
+ )
197
+
198
+ // background/index.ts
199
+ registerMyService()
276
200
 
277
201
  // content-script.ts
278
- const mathService = getMathService();
279
- await mathService.add(1, 2);
202
+ const myService = getMyService()
203
+ await myService.add(1, 2)
280
204
  ```
281
205
 
206
+
282
207
  ## 配置选项
283
208
 
284
- 支持 `@webext-core/messaging` 的所有配置选项:
209
+ 支持 `@webext-core/messaging` 的所有配置项:
285
210
 
286
211
  ```ts
287
- import { registerService, createProxyService } from '@northsea4/proxy-service';
212
+ import { registerService, createProxyService } from '@northsea4/proxy-service'
288
213
 
289
214
  const config = {
290
- logger: console, // 自定义日志记录器
291
- };
215
+ logger: console, // 自定义日志记录器
216
+ }
292
217
 
293
- registerService('my-service', myService, config);
294
- const proxy = createProxyService('my-service', config);
218
+ registerService('my-service', myService, config)
219
+ const proxy = createProxyService('my-service', config)
295
220
  ```
296
221
 
222
+
297
223
  ## 开发
298
224
 
299
225
  ```bash
@@ -308,6 +234,7 @@ pnpm dev
308
234
 
309
235
  MIT
310
236
 
237
+
311
238
  ## 致谢
312
239
 
313
240
  本项目基于 [@webext-core/proxy-service](https://github.com/aklinker1/webext-core/tree/main/packages/proxy-service) 开发,感谢原作者的优秀工作。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@northsea4/proxy-service",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "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.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",