crx-rpc 2.0.0 → 2.1.0
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.
- package/README.md +81 -586
- package/README.zh-CN.md +79 -794
- package/dist/adapter/content.d.ts +4 -14
- package/dist/adapter/content.js +4 -141
- package/dist/adapter/index.d.ts +1 -2
- package/dist/adapter/index.js +1 -2
- package/dist/adapter/runtime.d.ts +12 -0
- package/dist/adapter/runtime.js +31 -0
- package/dist/adapter/tab.d.ts +3 -10
- package/dist/adapter/tab.js +5 -2
- package/dist/adapter/web.d.ts +3 -2
- package/dist/adapter/web.js +5 -2
- package/dist/background.d.ts +1 -7
- package/dist/background.js +21 -41
- package/dist/client.d.ts +5 -4
- package/dist/client.js +8 -4
- package/dist/content.d.ts +1 -10
- package/dist/content.js +10 -32
- package/dist/id.d.ts +3 -1
- package/dist/id.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/services.d.ts +9 -0
- package/dist/services.js +6 -0
- package/dist/types.d.ts +5 -0
- package/package.json +33 -33
- package/src/adapter/index.ts +1 -2
- package/src/adapter/runtime.ts +42 -0
- package/src/adapter/tab.ts +5 -2
- package/src/adapter/web.ts +5 -2
- package/src/{adapter/background.ts → background.ts} +10 -9
- package/src/client.ts +8 -5
- package/src/{adapter/content.ts → content.ts} +14 -43
- package/src/id.ts +5 -1
- package/src/index.ts +3 -1
- package/src/services.ts +11 -0
- package/src/types.ts +6 -0
- package/dist/tab.d.ts +0 -15
- package/dist/tab.js +0 -42
- package/dist/web.d.ts +0 -10
- package/dist/web.js +0 -25
package/README.md
CHANGED
|
@@ -1,657 +1,152 @@
|
|
|
1
|
-
#
|
|
1
|
+
# crx-rpc
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A type-safe RPC implementation for Chrome Extensions, supporting communication between Content Scripts, Background, Popup/Sidepanel, and Web Pages.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- 📦 **Zero Configuration**: No manual method binding required
|
|
11
|
-
- 🎯 **Observable Support**: Built-in support for reactive data streams with RemoteSubject
|
|
12
|
-
- 🛡️ **Error Handling**: Preserves stack traces and error types across boundaries
|
|
13
|
-
- 🧹 **Resource Management**: Built-in disposable pattern for clean resource cleanup
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install crx-rpc
|
|
19
|
-
# or
|
|
20
|
-
pnpm add crx-rpc
|
|
21
|
-
# or
|
|
22
|
-
yarn add crx-rpc
|
|
23
|
-
```
|
|
7
|
+
- **Type-safe**: Built with TypeScript.
|
|
8
|
+
- **Flexible**: Supports various communication paths within a Chrome Extension.
|
|
9
|
+
- **Observable**: Supports RxJS-like observables for real-time updates.
|
|
24
10
|
|
|
25
|
-
##
|
|
11
|
+
## Communication Architecture
|
|
26
12
|
|
|
27
|
-
|
|
13
|
+
The library facilitates communication between different parts of a Chrome Extension.
|
|
28
14
|
|
|
29
|
-
|
|
30
|
-
// services/math.ts
|
|
31
|
-
import { createIdentifier } from 'crx-rpc';
|
|
15
|
+
### Service Providers
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
add(a: number, b: number): Promise<number>;
|
|
35
|
-
subtract(a: number, b: number): Promise<number>;
|
|
36
|
-
multiply(a: number, b: number): Promise<number>;
|
|
37
|
-
divide(a: number, b: number): Promise<number>;
|
|
38
|
-
}
|
|
17
|
+
Services can be hosted in two locations:
|
|
39
18
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
19
|
+
1. **Background**: Hosted using `BackgroundRPCHost`. Handles requests from Content Scripts and Web Pages.
|
|
20
|
+
2. **Content Script**: Hosted using `ContentRPCHost`. Handles requests from Background and Popup/Sidepanel.
|
|
43
21
|
|
|
44
|
-
###
|
|
22
|
+
### Callers
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
// background.ts
|
|
48
|
-
import { BackgroundRPC } from 'crx-rpc';
|
|
49
|
-
import { IMathService } from './services/math';
|
|
24
|
+
Callers can be:
|
|
50
25
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return a + b;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async subtract(a: number, b: number): Promise<number> {
|
|
57
|
-
return a - b;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async multiply(a: number, b: number): Promise<number> {
|
|
61
|
-
return a * b;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async divide(a: number, b: number): Promise<number> {
|
|
65
|
-
if (b === 0) throw new Error('Division by zero');
|
|
66
|
-
return a / b;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Register service with optional logging
|
|
71
|
-
const rpc = new BackgroundRPC(true); // Enable logging
|
|
72
|
-
// const rpc = new BackgroundRPC(); // Disable logging (default)
|
|
73
|
-
rpc.register(IMathService, new MathService());
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 3. Initialize Content Script
|
|
26
|
+
1. **Runtime**: Content Scripts, Popup, Sidepanel.
|
|
27
|
+
2. **Web**: Injected scripts in the web page.
|
|
77
28
|
|
|
78
|
-
|
|
29
|
+
### Supported Flows
|
|
79
30
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Initialize RPC bridge for web page ↔ background communication
|
|
87
|
-
const contentRpc = new ContentRPC();
|
|
88
|
-
|
|
89
|
-
// Remember to dispose when cleanup is needed
|
|
90
|
-
// contentRpc.dispose();
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
#### Option B: As a Direct Client
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
// content.ts
|
|
97
|
-
import { ContentRPCClient } from 'crx-rpc';
|
|
98
|
-
import { IMathService } from './services/math';
|
|
99
|
-
|
|
100
|
-
// Use content script as a direct RPC client
|
|
101
|
-
const client = new ContentRPCClient();
|
|
102
|
-
const mathService = client.createWebRPCService(IMathService);
|
|
103
|
-
|
|
104
|
-
// Direct calls to background services
|
|
105
|
-
const result = await mathService.add(5, 3);
|
|
106
|
-
console.log('Result from content script:', result);
|
|
107
|
-
|
|
108
|
-
// Remember to dispose when cleanup is needed
|
|
109
|
-
// client.dispose();
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
#### Option C: Both Bridge and Client
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// content.ts
|
|
116
|
-
import { ContentRPC, ContentRPCClient } from 'crx-rpc';
|
|
117
|
-
import { IMathService } from './services/math';
|
|
31
|
+
| Caller | Target | Client | Host | Note |
|
|
32
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
33
|
+
| **Content Script** | **Background** | `RuntimeRPCClient` | `BackgroundRPCHost` | Standard Runtime -> Background communication. |
|
|
34
|
+
| **Web Page** | **Background** | `WebRPCClient` | `BackgroundRPCHost` | Relayed via Content Script (`Web2BackgroundProxy`). |
|
|
35
|
+
| **Background** | **Content Script** | `TabRPCClient` | `ContentRPCHost` | Targets a specific tab. |
|
|
36
|
+
| **Popup/Sidepanel** | **Content Script** | `TabRPCClient` | `ContentRPCHost` | Targets a specific tab. |
|
|
118
37
|
|
|
119
|
-
|
|
120
|
-
const bridge = new ContentRPC();
|
|
38
|
+
> **Note**: Direct communication from Popup/Sidepanel to Background using `RuntimeRPCClient` is currently not supported by `BackgroundRPCHost` as it requires a sender tab ID.
|
|
121
39
|
|
|
122
|
-
|
|
123
|
-
const client = new ContentRPCClient();
|
|
124
|
-
const mathService = client.createWebRPCService(IMathService);
|
|
40
|
+
## Usage
|
|
125
41
|
|
|
126
|
-
|
|
127
|
-
const result = await mathService.multiply(2, 3);
|
|
128
|
-
console.log('Content script calculation:', result);
|
|
129
|
-
```
|
|
42
|
+
### 1. Define API
|
|
130
43
|
|
|
131
|
-
|
|
44
|
+
Define your service interface and create an identifier.
|
|
132
45
|
|
|
133
46
|
```typescript
|
|
134
|
-
|
|
135
|
-
import { WebRPCClient } from 'crx-rpc';
|
|
136
|
-
import { IMathService } from './services/math';
|
|
137
|
-
|
|
138
|
-
async function calculate() {
|
|
139
|
-
// Create RPC client
|
|
140
|
-
const client = new WebRPCClient();
|
|
141
|
-
|
|
142
|
-
// Create type-safe service proxy
|
|
143
|
-
const mathService = client.createWebRPCService(IMathService);
|
|
144
|
-
|
|
145
|
-
// Type-safe method calls
|
|
146
|
-
const sum = await mathService.add(1, 2); // TypeScript knows this returns Promise<number>
|
|
147
|
-
const difference = await mathService.subtract(10, 5);
|
|
148
|
-
const product = await mathService.multiply(3, 4);
|
|
149
|
-
const quotient = await mathService.divide(15, 3);
|
|
150
|
-
|
|
151
|
-
console.log('Results:', { sum, difference, product, quotient });
|
|
47
|
+
import { createIdentifier } from 'crx-rpc';
|
|
152
48
|
|
|
153
|
-
|
|
154
|
-
|
|
49
|
+
export interface IMathService {
|
|
50
|
+
add(a: number, b: number): Promise<number>;
|
|
155
51
|
}
|
|
156
|
-
```
|
|
157
52
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### Complete Communication Topology
|
|
161
|
-
|
|
162
|
-
```mermaid
|
|
163
|
-
graph TB
|
|
164
|
-
subgraph WebPage["Web Page Context"]
|
|
165
|
-
WC[WebRPCClient]
|
|
166
|
-
WO[WebObservable]
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
subgraph ContentScript["Content Script Context"]
|
|
170
|
-
CR[ContentRPC<br/>Bridge Mode]
|
|
171
|
-
CC[ContentRPCClient<br/>Direct Mode]
|
|
172
|
-
CO[ContentObservable]
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
subgraph Background["Background Script Context"]
|
|
176
|
-
BR[BackgroundRPC]
|
|
177
|
-
MS[MathService]
|
|
178
|
-
US[UserService]
|
|
179
|
-
RS[RemoteSubject]
|
|
180
|
-
RSM[RemoteSubjectManager]
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
subgraph ExtPage["Extension Page Context<br/>(Popup/Options/Sidepanel)"]
|
|
184
|
-
EC[ExtPageRPCClient]
|
|
185
|
-
EO[ExtPageObservable]
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
subgraph TabContext["Tab-specific Access"]
|
|
189
|
-
TC[TabRPCClient]
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
%% RPC Calls
|
|
193
|
-
WC -->|"CustomEvent<br/>.add(1,2)"| CR
|
|
194
|
-
CR -->|"chrome.runtime<br/>Forward"| BR
|
|
195
|
-
CC -->|"chrome.runtime<br/>.multiply(2,3)"| BR
|
|
196
|
-
EC -->|"chrome.runtime<br/>.divide(10,2)"| BR
|
|
197
|
-
TC -->|"chrome.tabs<br/>Access Content Service"| CC
|
|
198
|
-
|
|
199
|
-
BR -->|Response| CR
|
|
200
|
-
CR -->|CustomEvent| WC
|
|
201
|
-
BR -->|Response| CC
|
|
202
|
-
BR -->|Response| EC
|
|
203
|
-
|
|
204
|
-
BR -.->|Manages| MS
|
|
205
|
-
BR -.->|Manages| US
|
|
206
|
-
|
|
207
|
-
%% Observable Streams
|
|
208
|
-
WO -.->|Subscribe| CR
|
|
209
|
-
CR -.->|Forward| RSM
|
|
210
|
-
CO -.->|Subscribe| RSM
|
|
211
|
-
EO -.->|Subscribe| RSM
|
|
212
|
-
RSM -.->|Broadcast| RS
|
|
213
|
-
RS -.->|Updates| CR
|
|
214
|
-
CR -.->|Updates| WO
|
|
215
|
-
RS -.->|Updates| CO
|
|
216
|
-
RS -.->|Updates| EO
|
|
217
|
-
|
|
218
|
-
style WC fill:#e1f5ff
|
|
219
|
-
style CC fill:#e1f5ff
|
|
220
|
-
style EC fill:#e1f5ff
|
|
221
|
-
style TC fill:#e1f5ff
|
|
222
|
-
style BR fill:#fff4e6
|
|
223
|
-
style MS fill:#f0f0f0
|
|
224
|
-
style US fill:#f0f0f0
|
|
225
|
-
style RS fill:#ffe6f0
|
|
226
|
-
style RSM fill:#ffe6f0
|
|
227
|
-
style CR fill:#e8f5e9
|
|
53
|
+
export const IMathService = createIdentifier<IMathService>('math-service', 'background');
|
|
228
54
|
```
|
|
229
55
|
|
|
230
|
-
###
|
|
231
|
-
|
|
232
|
-
| Path | Method | Description |
|
|
233
|
-
|------|--------|-------------|
|
|
234
|
-
| **Web Page → Background** | CustomEvent + chrome.runtime | Through ContentRPC bridge |
|
|
235
|
-
| **Content Script → Background** | chrome.runtime | Direct communication |
|
|
236
|
-
| **Extension Page → Background** | chrome.runtime | Direct communication |
|
|
237
|
-
| **Extension Page → Content Script** | chrome.tabs + TabRPCClient | Tab-specific access |
|
|
238
|
-
| **Background → All Contexts** | RemoteSubject broadcast | Real-time data streaming |
|
|
239
|
-
|
|
240
|
-
### Key Components
|
|
241
|
-
|
|
242
|
-
- **WebRPCClient**: Client for web pages using window events
|
|
243
|
-
- **ContentRPC**: Bridge that forwards messages between web and background
|
|
244
|
-
- **ContentRPCClient**: Direct RPC client for content scripts (bypasses bridge)
|
|
245
|
-
- **BackgroundRPC**: Service registry and handler in the background script
|
|
246
|
-
- **RPCClient**: Base client with service proxy generation
|
|
56
|
+
### 2. Implement & Host Service
|
|
247
57
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
The framework includes built-in logging support for debugging and monitoring RPC calls.
|
|
251
|
-
|
|
252
|
-
### Enable Logging
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Enable logging in BackgroundRPC
|
|
256
|
-
const rpc = new BackgroundRPC(true); // Enable logging
|
|
257
|
-
// const rpc = new BackgroundRPC(); // Disable logging (default)
|
|
258
|
-
|
|
259
|
-
// Example output:
|
|
260
|
-
// [RPC] Call: MathService.add { id: "123", args: [5, 3], senderId: 456, timestamp: "2025-09-01T10:00:00.000Z" }
|
|
261
|
-
// [RPC] Success: MathService.add { id: "123", result: 8, timestamp: "2025-09-01T10:00:00.001Z" }
|
|
262
|
-
|
|
263
|
-
// For errors:
|
|
264
|
-
// [RPC] Error: MathService.divide { id: "124", error: "Division by zero", timestamp: "2025-09-01T10:00:01.000Z" }
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### Log Output
|
|
268
|
-
|
|
269
|
-
When logging is enabled, the following information is logged:
|
|
270
|
-
|
|
271
|
-
- **Function Calls**: Service name, method name, arguments, sender ID, and timestamp
|
|
272
|
-
- **Success Responses**: Service name, method name, result, and timestamp
|
|
273
|
-
- **Error Responses**: Service name, method name, error message, and timestamp
|
|
274
|
-
- **Unknown Services/Methods**: Warnings for invalid service or method calls
|
|
275
|
-
|
|
276
|
-
### Use Cases
|
|
277
|
-
|
|
278
|
-
- **Development**: Debug RPC communication during development
|
|
279
|
-
- **Production Monitoring**: Track RPC usage patterns and performance
|
|
280
|
-
- **Troubleshooting**: Identify failed calls and error patterns
|
|
281
|
-
- **Security Auditing**: Monitor RPC access patterns
|
|
282
|
-
|
|
283
|
-
## Observable Support
|
|
284
|
-
|
|
285
|
-
The framework includes built-in support for reactive data streams using `RemoteSubject` and `Observable` patterns with a centralized message management system.
|
|
286
|
-
|
|
287
|
-
### Remote Subject Manager & Remote Subject (Background Script)
|
|
288
|
-
|
|
289
|
-
The `RemoteSubjectManager` acts as a centralized message hub that handles all subscription management and message routing, while `RemoteSubject` focuses purely on state management.
|
|
58
|
+
#### In Background
|
|
290
59
|
|
|
291
60
|
```typescript
|
|
292
61
|
// background.ts
|
|
293
|
-
import {
|
|
294
|
-
|
|
295
|
-
interface ICounterObservable {
|
|
296
|
-
value: number;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
300
|
-
|
|
301
|
-
const rpc = new BackgroundRPC();
|
|
302
|
-
|
|
303
|
-
// Create a centralized subject manager
|
|
304
|
-
const subjectManager = new RemoteSubjectManager();
|
|
305
|
-
|
|
306
|
-
// Create a remote subject through the manager
|
|
307
|
-
const counterSubject = subjectManager.createSubject(
|
|
308
|
-
ICounterObservable,
|
|
309
|
-
'main',
|
|
310
|
-
{ value: 0 }
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
// Update value and broadcast to all subscribers
|
|
314
|
-
setInterval(() => {
|
|
315
|
-
const newValue = { value: Math.floor(Math.random() * 100) };
|
|
316
|
-
counterSubject.next(newValue);
|
|
317
|
-
}, 1000);
|
|
318
|
-
|
|
319
|
-
// The manager handles:
|
|
320
|
-
// - Message routing and subscription management
|
|
321
|
-
// - Queuing subscriptions that arrive before subjects are created
|
|
322
|
-
// - Automatic cleanup when tabs are closed
|
|
323
|
-
// - Broadcasting to multiple subscribers
|
|
324
|
-
|
|
325
|
-
// Cleanup
|
|
326
|
-
// subjectManager.dispose(); // This will dispose all subjects
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Key Features of RemoteSubjectManager
|
|
330
|
-
|
|
331
|
-
- **Centralized Message Hub**: All observable-related messages are handled by the manager
|
|
332
|
-
- **Queue Management**: Subscriptions received before subject creation are queued and processed later
|
|
333
|
-
- **Resource Management**: Automatic cleanup of subscriptions when tabs are closed
|
|
334
|
-
- **Type Safety**: Full TypeScript support with proper typing throughout
|
|
335
|
-
|
|
336
|
-
### Architecture
|
|
337
|
-
|
|
338
|
-
```
|
|
339
|
-
┌─────────────────┐ ┌──────────────────────────────────────┐ ┌─────────────────┐
|
|
340
|
-
│ Web Page │ │ Background Script │ │ Content Script │
|
|
341
|
-
├─────────────────┤ ├──────────────────────────────────────┤ ├─────────────────┤
|
|
342
|
-
│ WebObservable │ │ RemoteSubjectManager │ │ContentObservable│
|
|
343
|
-
│ │ │ ┌─────────────────────────────────┐ │ │ │
|
|
344
|
-
│ subscribe() ────┼───▶│ │ Message Routing & Queue Mgmt │ │◄───┤ subscribe() │
|
|
345
|
-
│ │◄───│ │ │ │ │ │
|
|
346
|
-
└─────────────────┘ │ └─────────────────────────────────┘ │ └─────────────────┘
|
|
347
|
-
│ │ │
|
|
348
|
-
│ ┌─────────────▼─────────────────┐ │
|
|
349
|
-
│ │ RemoteSubject │ │
|
|
350
|
-
│ │ (Pure State Management) │ │
|
|
351
|
-
│ │ │ │
|
|
352
|
-
│ │ next() ─────────────────────▶ │ │
|
|
353
|
-
│ │ complete() ─────────────────▶ │ │
|
|
354
|
-
│ └───────────────────────────────┘ │
|
|
355
|
-
└──────────────────────────────────────┘
|
|
356
|
-
```
|
|
62
|
+
import { BackgroundRPCHost } from 'crx-rpc';
|
|
63
|
+
import { IMathService } from './api';
|
|
357
64
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
import { WebObservable, createIdentifier } from 'crx-rpc';
|
|
363
|
-
|
|
364
|
-
interface ICounterObservable {
|
|
365
|
-
value: number;
|
|
65
|
+
class MathService implements IMathService {
|
|
66
|
+
async add(a: number, b: number) {
|
|
67
|
+
return a + b;
|
|
68
|
+
}
|
|
366
69
|
}
|
|
367
70
|
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
// Subscribe to remote observable
|
|
371
|
-
const observable = new WebObservable(
|
|
372
|
-
ICounterObservable,
|
|
373
|
-
'main',
|
|
374
|
-
(value) => {
|
|
375
|
-
console.log('Counter updated:', value.value);
|
|
376
|
-
}
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
// Cleanup when done
|
|
380
|
-
// observable.dispose();
|
|
71
|
+
const host = new BackgroundRPCHost();
|
|
72
|
+
host.register(IMathService, new MathService());
|
|
381
73
|
```
|
|
382
74
|
|
|
383
|
-
|
|
75
|
+
#### In Content Script
|
|
384
76
|
|
|
385
77
|
```typescript
|
|
386
78
|
// content.ts
|
|
387
|
-
import {
|
|
388
|
-
|
|
389
|
-
interface ICounterObservable {
|
|
390
|
-
value: number;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
394
|
-
|
|
395
|
-
// Content script can directly subscribe to observables
|
|
396
|
-
const observable = new ContentObservable(
|
|
397
|
-
ICounterObservable,
|
|
398
|
-
'main',
|
|
399
|
-
(value) => {
|
|
400
|
-
console.log('Counter from content script:', value.value);
|
|
401
|
-
// Content script can react to real-time updates
|
|
402
|
-
updateUI(value.value);
|
|
403
|
-
}
|
|
404
|
-
);
|
|
405
|
-
|
|
406
|
-
// Cleanup when done
|
|
407
|
-
// observable.dispose();
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
### Subscribing from Extension Page
|
|
79
|
+
import { ContentRPCHost, createIdentifier } from 'crx-rpc';
|
|
411
80
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
import { ExtPageObservable, createIdentifier } from 'crx-rpc';
|
|
415
|
-
|
|
416
|
-
interface ICounterObservable {
|
|
417
|
-
value: number;
|
|
81
|
+
export interface IPageService {
|
|
82
|
+
doSomething(): void;
|
|
418
83
|
}
|
|
84
|
+
export const IPageService = createIdentifier<IPageService>('page-service', 'content');
|
|
419
85
|
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
// Extension page can subscribe to background observables
|
|
423
|
-
const observable = new ExtPageObservable(
|
|
424
|
-
ICounterObservable,
|
|
425
|
-
'main',
|
|
426
|
-
(value) => {
|
|
427
|
-
console.log('Counter from extension page:', value.value);
|
|
428
|
-
document.getElementById('counter').textContent = value.value.toString();
|
|
429
|
-
}
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
// Cleanup when done
|
|
433
|
-
window.addEventListener('unload', () => {
|
|
434
|
-
observable.dispose();
|
|
435
|
-
});
|
|
86
|
+
const host = new ContentRPCHost();
|
|
87
|
+
host.register(IPageService, new PageService());
|
|
436
88
|
```
|
|
437
89
|
|
|
438
|
-
###
|
|
90
|
+
### 3. Call Service
|
|
439
91
|
|
|
440
|
-
|
|
92
|
+
#### From Content Script (to Background)
|
|
441
93
|
|
|
442
94
|
```typescript
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// Background: RemoteSubject.next() → Manager routes to subscribers
|
|
446
|
-
// Web Page: WebObservable.subscribe()
|
|
447
|
-
|
|
448
|
-
// Pattern 2: Background → Content Script (direct)
|
|
449
|
-
// Background: RemoteSubject.next() → Manager routes directly
|
|
450
|
-
// Content Script: ContentObservable.subscribe()
|
|
451
|
-
|
|
452
|
-
// Pattern 3: Background → Both Web Page and Content Script
|
|
453
|
-
// Background: RemoteSubject.next() → Manager broadcasts to all subscribers
|
|
454
|
-
// Web Page: WebObservable.subscribe()
|
|
455
|
-
// Content Script: ContentObservable.subscribe()
|
|
456
|
-
|
|
457
|
-
// Pattern 4: Subscription before Subject Creation (Queue Management)
|
|
458
|
-
// Subscriber: WebObservable.subscribe() → Manager queues subscription
|
|
459
|
-
// Background: Later creates RemoteSubject → Manager processes queued subscriptions
|
|
460
|
-
// Result: No missed initial values, proper subscription ordering
|
|
461
|
-
```
|
|
95
|
+
import { RuntimeRPCClient } from 'crx-rpc';
|
|
96
|
+
import { IMathService } from './api';
|
|
462
97
|
|
|
463
|
-
|
|
98
|
+
const client = new RuntimeRPCClient();
|
|
99
|
+
const mathService = client.createRPCService(IMathService);
|
|
464
100
|
|
|
465
|
-
|
|
101
|
+
await mathService.add(1, 2);
|
|
102
|
+
```
|
|
466
103
|
|
|
467
|
-
|
|
104
|
+
#### From Web Page (to Background)
|
|
468
105
|
|
|
469
106
|
```typescript
|
|
470
|
-
import { WebRPCClient
|
|
107
|
+
import { WebRPCClient } from 'crx-rpc';
|
|
108
|
+
import { IMathService } from './api';
|
|
471
109
|
|
|
472
110
|
const client = new WebRPCClient();
|
|
473
|
-
const
|
|
474
|
-
const backgroundRpc = new BackgroundRPC();
|
|
475
|
-
|
|
476
|
-
// Proper cleanup
|
|
477
|
-
function cleanup() {
|
|
478
|
-
client.dispose();
|
|
479
|
-
contentRpc.dispose();
|
|
480
|
-
backgroundRpc.dispose();
|
|
481
|
-
}
|
|
111
|
+
const mathService = client.createRPCService(IMathService);
|
|
482
112
|
|
|
483
|
-
|
|
484
|
-
if (!client.isDisposed()) {
|
|
485
|
-
const service = client.createWebRPCService(IMathService);
|
|
486
|
-
// Use service...
|
|
487
|
-
}
|
|
113
|
+
await mathService.add(1, 2);
|
|
488
114
|
```
|
|
489
115
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
Extension pages can access content script services using `TabRPCClient` by specifying the target tab ID:
|
|
116
|
+
*Note: Requires `Web2BackgroundProxy` to be active in the content script.*
|
|
493
117
|
|
|
494
118
|
```typescript
|
|
495
|
-
//
|
|
496
|
-
import {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
// Get current active tab
|
|
500
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
501
|
-
|
|
502
|
-
if (tab.id) {
|
|
503
|
-
// Create RPC client for specific tab
|
|
504
|
-
const tabClient = new TabRPCClient(tab.id);
|
|
505
|
-
|
|
506
|
-
// Access content script services in that tab
|
|
507
|
-
const contentService = tabClient.createWebRPCService(IContentService);
|
|
508
|
-
|
|
509
|
-
// Call content script methods
|
|
510
|
-
const result = await contentService.getDOMInfo();
|
|
511
|
-
console.log('DOM info from content script:', result);
|
|
512
|
-
|
|
513
|
-
// Cleanup when done
|
|
514
|
-
window.addEventListener('unload', () => {
|
|
515
|
-
tabClient.dispose();
|
|
516
|
-
});
|
|
517
|
-
}
|
|
119
|
+
// content.ts
|
|
120
|
+
import { Web2BackgroundProxy } from 'crx-rpc';
|
|
121
|
+
const proxy = new Web2BackgroundProxy();
|
|
518
122
|
```
|
|
519
123
|
|
|
520
|
-
####
|
|
521
|
-
|
|
522
|
-
1. **DOM Inspection**: Popup queries content script for page information
|
|
523
|
-
2. **User Actions**: Options page triggers content script actions on specific tabs
|
|
524
|
-
3. **Multi-tab Management**: Sidepanel coordinates actions across multiple tabs
|
|
525
|
-
4. **Live Preview**: Extension page gets real-time updates from content script
|
|
526
|
-
|
|
527
|
-
#### Complete Example: Popup with Tab-specific Services
|
|
124
|
+
#### From Background/Popup (to Content)
|
|
528
125
|
|
|
529
126
|
```typescript
|
|
530
|
-
|
|
531
|
-
import {
|
|
532
|
-
import { IPageService } from './services';
|
|
533
|
-
|
|
534
|
-
class PageService implements IPageService {
|
|
535
|
-
async getTitle(): Promise<string> {
|
|
536
|
-
return document.title;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
async getSelection(): Promise<string> {
|
|
540
|
-
return window.getSelection()?.toString() || '';
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
async highlightText(text: string): Promise<void> {
|
|
544
|
-
// Highlight logic...
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const contentHost = new ContentRPCHost();
|
|
549
|
-
contentHost.register(IPageService, new PageService());
|
|
550
|
-
|
|
551
|
-
// popup.ts - Access content script from popup
|
|
552
|
-
import { TabRPCClient, ExtPageRPCClient } from 'crx-rpc';
|
|
553
|
-
import { IPageService, IMathService } from './services';
|
|
554
|
-
|
|
555
|
-
// Access background services
|
|
556
|
-
const bgClient = new ExtPageRPCClient();
|
|
557
|
-
const mathService = bgClient.createWebRPCService(IMathService);
|
|
558
|
-
|
|
559
|
-
// Access content script services in active tab
|
|
560
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
561
|
-
if (tab.id) {
|
|
562
|
-
const tabClient = new TabRPCClient(tab.id);
|
|
563
|
-
const pageService = tabClient.createWebRPCService(IPageService);
|
|
564
|
-
|
|
565
|
-
// Get page info from content script
|
|
566
|
-
const title = await pageService.getTitle();
|
|
567
|
-
const selection = await pageService.getSelection();
|
|
568
|
-
|
|
569
|
-
// Process with background service
|
|
570
|
-
const result = await mathService.calculate(selection.length);
|
|
571
|
-
|
|
572
|
-
// Update popup UI
|
|
573
|
-
document.getElementById('title').textContent = title;
|
|
574
|
-
document.getElementById('result').textContent = result.toString();
|
|
575
|
-
}
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
## Usage Scenarios
|
|
579
|
-
|
|
580
|
-
### Scenario 1: Web Page Only
|
|
581
|
-
- Web pages need to communicate with background services
|
|
582
|
-
- Use: `WebRPCClient` + `ContentRPC` bridge
|
|
583
|
-
|
|
584
|
-
### Scenario 2: Content Script Only
|
|
585
|
-
- Content scripts need direct access to background services
|
|
586
|
-
- Use: `ContentRPCClient` directly (no bridge needed)
|
|
127
|
+
import { TabRPCClient } from 'crx-rpc';
|
|
128
|
+
import { IPageService } from './api';
|
|
587
129
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
130
|
+
const tabId = 123; // Target Tab ID
|
|
131
|
+
const client = new TabRPCClient(tabId);
|
|
132
|
+
const pageService = client.createRPCService(IPageService);
|
|
591
133
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
- Use: `RemoteSubject` + `WebObservable`/`ContentObservable`
|
|
134
|
+
await pageService.doSomething();
|
|
135
|
+
```
|
|
595
136
|
|
|
596
137
|
## API Reference
|
|
597
138
|
|
|
598
|
-
###
|
|
599
|
-
|
|
600
|
-
- **`BackgroundRPC`**: Service registry and message handler for background scripts
|
|
601
|
-
- **`ContentRPC`**: Message bridge between web pages and background scripts
|
|
602
|
-
- **`WebRPCClient`**: RPC client for web pages
|
|
603
|
-
- **`ContentRPCClient`**: Direct RPC client for content scripts
|
|
604
|
-
- **`RemoteSubjectManager`**: Centralized observable message management system
|
|
605
|
-
|
|
606
|
-
### Observable Classes
|
|
607
|
-
|
|
608
|
-
- **`RemoteSubjectManager`**: Centralized message hub that manages subscriptions and message routing for all observables
|
|
609
|
-
- **`RemoteSubject<T>`**: Pure state management observable that works with the manager to broadcast updates
|
|
610
|
-
- **`WebObservable<T>`**: Observable subscriber for web pages
|
|
611
|
-
- **`ContentObservable<T>`**: Observable subscriber for content scripts
|
|
612
|
-
|
|
613
|
-
### Utility Functions
|
|
614
|
-
|
|
615
|
-
- **`createIdentifier<T>(key: string)`**: Creates a type-safe service identifier
|
|
616
|
-
|
|
617
|
-
### Interfaces
|
|
618
|
-
|
|
619
|
-
- **`Identifier<T>`**: Type-safe service identifier interface
|
|
620
|
-
- **`RpcRequest`**: RPC request message structure
|
|
621
|
-
- **`RpcResponse`**: RPC response message structure
|
|
622
|
-
- **`IMessageAdapter`**: Message transport abstraction interface
|
|
623
|
-
- **`IDisposable`**: Resource management interface
|
|
624
|
-
|
|
625
|
-
## Best Practices
|
|
626
|
-
|
|
627
|
-
1. **Service Interface Design**
|
|
628
|
-
- Use clear method names and proper TypeScript types
|
|
629
|
-
- Return Promise types for async operation support
|
|
630
|
-
- Define detailed parameter and return value types
|
|
631
|
-
- Keep interfaces focused and cohesive
|
|
632
|
-
|
|
633
|
-
2. **Resource Management**
|
|
634
|
-
- Always call `dispose()` on RPC instances when cleanup is needed
|
|
635
|
-
- Check `isDisposed()` before using disposed instances
|
|
636
|
-
- Use proper cleanup in component unmount/destroy lifecycle
|
|
139
|
+
### Hosts
|
|
637
140
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
- Throw meaningful errors with descriptive messages
|
|
641
|
-
- Handle RPC errors appropriately on the client side
|
|
141
|
+
- `BackgroundRPCHost`: Handles RPC requests in the background script.
|
|
142
|
+
- `ContentRPCHost`: Handles RPC requests in the content script.
|
|
642
143
|
|
|
643
|
-
|
|
644
|
-
- Avoid frequent small data transfers
|
|
645
|
-
- Consider batching operations when possible
|
|
646
|
-
- Use Observable pattern for real-time data updates with `RemoteSubjectManager` for efficient message routing
|
|
647
|
-
- Implement caching strategies where appropriate
|
|
648
|
-
- The manager automatically handles subscription queuing to prevent race conditions
|
|
144
|
+
### Clients
|
|
649
145
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
- Consider rate limiting for resource-intensive operations
|
|
146
|
+
- `RuntimeRPCClient`: Used in Content Scripts to call Background services.
|
|
147
|
+
- `WebRPCClient`: Used in Web Pages to call Background services (via relay).
|
|
148
|
+
- `TabRPCClient`: Used in Background/Popup to call Content Script services for a specific tab.
|
|
654
149
|
|
|
655
|
-
|
|
150
|
+
### Proxies
|
|
656
151
|
|
|
657
|
-
|
|
152
|
+
- `Web2BackgroundProxy`: Relays messages from Web Page to Background. Must be instantiated in the Content Script.
|