crx-rpc 1.0.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 +417 -0
- package/README.zh-CN.md +533 -0
- package/dist/background.d.ts +21 -0
- package/dist/background.js +146 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +88 -0
- package/dist/const.d.ts +5 -0
- package/dist/const.js +5 -0
- package/dist/content.d.ts +14 -0
- package/dist/content.js +55 -0
- package/dist/disposable.d.ts +7 -0
- package/dist/disposable.js +16 -0
- package/dist/id.d.ts +12 -0
- package/dist/id.js +8 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +1 -0
- package/dist/web.d.ts +10 -0
- package/dist/web.js +25 -0
- package/package.json +27 -0
- package/src/background.ts +162 -0
- package/src/client.ts +110 -0
- package/src/const.ts +6 -0
- package/src/content.ts +71 -0
- package/src/disposable.ts +18 -0
- package/src/id.ts +17 -0
- package/src/index.ts +6 -0
- package/src/types.ts +49 -0
- package/src/web.ts +34 -0
- package/tsconfig.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Chrome Extension RPC (@weird94/crx-rpc)
|
|
2
|
+
|
|
3
|
+
A lightweight, type-safe RPC framework for Chrome Extensions supporting communication between web pages, content scripts, and background scripts. Built with TypeScript for maximum type safety and developer experience.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ๐ **Type Safety**: Full TypeScript type support with automatic proxy type generation
|
|
8
|
+
- ๐ **Easy to Use**: Auto-generated client proxies based on interfaces
|
|
9
|
+
- ๐ **Bidirectional Communication**: Supports web page โ content script โ background script
|
|
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 @weird94/crx-rpc
|
|
19
|
+
# or
|
|
20
|
+
pnpm add @weird94/crx-rpc
|
|
21
|
+
# or
|
|
22
|
+
yarn add @weird94/crx-rpc
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Define Service Interface
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// services/math.ts
|
|
31
|
+
import { createIdentifier } from '@weird94/crx-rpc';
|
|
32
|
+
|
|
33
|
+
interface IMathService {
|
|
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
|
+
}
|
|
39
|
+
|
|
40
|
+
// Create service identifier
|
|
41
|
+
export const IMathService = createIdentifier<IMathService>('MathService');
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Implement Service (Background Script)
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// background.ts
|
|
48
|
+
import { BackgroundRPC } from '@weird94/crx-rpc';
|
|
49
|
+
import { IMathService } from './services/math';
|
|
50
|
+
|
|
51
|
+
class MathService implements IMathService {
|
|
52
|
+
async add(a: number, b: number): Promise<number> {
|
|
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
|
|
71
|
+
const rpc = new BackgroundRPC();
|
|
72
|
+
rpc.register(IMathService, new MathService());
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Initialize Content Script
|
|
76
|
+
|
|
77
|
+
Content scripts can work in two modes:
|
|
78
|
+
|
|
79
|
+
#### Option A: As a Bridge (for web page communication)
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// content.ts
|
|
83
|
+
import { ContentRPC } from '@weird94/crx-rpc';
|
|
84
|
+
|
|
85
|
+
// Initialize RPC bridge for web page โ background communication
|
|
86
|
+
const contentRpc = new ContentRPC();
|
|
87
|
+
|
|
88
|
+
// Remember to dispose when cleanup is needed
|
|
89
|
+
// contentRpc.dispose();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Option B: As a Direct Client
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// content.ts
|
|
96
|
+
import { ContentRPCClient } from '@weird94/crx-rpc';
|
|
97
|
+
import { IMathService } from './services/math';
|
|
98
|
+
|
|
99
|
+
// Use content script as a direct RPC client
|
|
100
|
+
const client = new ContentRPCClient();
|
|
101
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
102
|
+
|
|
103
|
+
// Direct calls to background services
|
|
104
|
+
const result = await mathService.add(5, 3);
|
|
105
|
+
console.log('Result from content script:', result);
|
|
106
|
+
|
|
107
|
+
// Remember to dispose when cleanup is needed
|
|
108
|
+
// client.dispose();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Option C: Both Bridge and Client
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// content.ts
|
|
115
|
+
import { ContentRPC, ContentRPCClient } from '@weird94/crx-rpc';
|
|
116
|
+
import { IMathService } from './services/math';
|
|
117
|
+
|
|
118
|
+
// Initialize bridge for web pages
|
|
119
|
+
const bridge = new ContentRPC();
|
|
120
|
+
|
|
121
|
+
// Also use as direct client
|
|
122
|
+
const client = new ContentRPCClient();
|
|
123
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
124
|
+
|
|
125
|
+
// Content script can make its own RPC calls
|
|
126
|
+
const result = await mathService.multiply(2, 3);
|
|
127
|
+
console.log('Content script calculation:', result);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4. Use Client (Web Page)
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// web-page.ts
|
|
134
|
+
import { WebRPCClient } from '@weird94/crx-rpc';
|
|
135
|
+
import { IMathService } from './services/math';
|
|
136
|
+
|
|
137
|
+
async function calculate() {
|
|
138
|
+
// Create RPC client
|
|
139
|
+
const client = new WebRPCClient();
|
|
140
|
+
|
|
141
|
+
// Create type-safe service proxy
|
|
142
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
143
|
+
|
|
144
|
+
// Type-safe method calls
|
|
145
|
+
const sum = await mathService.add(1, 2); // TypeScript knows this returns Promise<number>
|
|
146
|
+
const difference = await mathService.subtract(10, 5);
|
|
147
|
+
const product = await mathService.multiply(3, 4);
|
|
148
|
+
const quotient = await mathService.divide(15, 3);
|
|
149
|
+
|
|
150
|
+
console.log('Results:', { sum, difference, product, quotient });
|
|
151
|
+
|
|
152
|
+
// Remember to dispose when cleanup is needed
|
|
153
|
+
// client.dispose();
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Architecture
|
|
158
|
+
|
|
159
|
+
### Hybrid Mode (Both Bridge + Direct)
|
|
160
|
+
```
|
|
161
|
+
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
|
|
162
|
+
โ Web Page โ โ Content Script โ โ Background โ
|
|
163
|
+
โ โ โ (Bridge+Client) โ โ Script โ
|
|
164
|
+
โโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโค
|
|
165
|
+
โ WebRPCClient โ โ ContentRPC โ โ BackgroundRPC โ
|
|
166
|
+
โ โ โ + โ โ โ
|
|
167
|
+
โ mathService โโโโโผโโโโถโContentRPCClient โโโโโถโ MathService โ
|
|
168
|
+
โ .add(1,2) โโโโโโ โ โ UserService โ
|
|
169
|
+
โ โ โ userService โ โ โ
|
|
170
|
+
โ โ โ .getUser() โโโโโโผโโโโถโ โ
|
|
171
|
+
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Key Components
|
|
175
|
+
|
|
176
|
+
- **WebRPCClient**: Client for web pages using window events
|
|
177
|
+
- **ContentRPC**: Bridge that forwards messages between web and background
|
|
178
|
+
- **ContentRPCClient**: Direct RPC client for content scripts (bypasses bridge)
|
|
179
|
+
- **BackgroundRPC**: Service registry and handler in the background script
|
|
180
|
+
- **RPCClient**: Base client with service proxy generation
|
|
181
|
+
|
|
182
|
+
## Error Handling
|
|
183
|
+
|
|
184
|
+
The framework preserves error details including stack traces and error types:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const client = new WebRPCClient();
|
|
188
|
+
const mathService = client.createWebRPCService(IMathService);
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const result = await mathService.divide(10, 0);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('RPC Error:', error.message);
|
|
194
|
+
console.error('Stack trace:', error.stack);
|
|
195
|
+
console.error('Error name:', error.name);
|
|
196
|
+
// Error preserves original stack trace and error type from the background script
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Error Structure
|
|
201
|
+
|
|
202
|
+
Errors are transmitted with full details:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
interface RpcErrorDetails {
|
|
206
|
+
message: string;
|
|
207
|
+
stack?: string;
|
|
208
|
+
name?: string;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Observable Support
|
|
213
|
+
|
|
214
|
+
The framework includes built-in support for reactive data streams using `RemoteSubject` and `Observable` patterns.
|
|
215
|
+
|
|
216
|
+
### Remote Subject (Background Script)
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// background.ts
|
|
220
|
+
import { BackgroundRPC, RemoteSubject, createIdentifier } from '@weird94/crx-rpc';
|
|
221
|
+
|
|
222
|
+
interface ICounterObservable {
|
|
223
|
+
value: number;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
227
|
+
|
|
228
|
+
const rpc = new BackgroundRPC();
|
|
229
|
+
|
|
230
|
+
// Create a remote subject that can broadcast to multiple subscribers
|
|
231
|
+
const counterSubject = new RemoteSubject(ICounterObservable, 'main', { value: 0 });
|
|
232
|
+
|
|
233
|
+
// Update value and broadcast to all subscribers
|
|
234
|
+
setInterval(() => {
|
|
235
|
+
const newValue = { value: Math.floor(Math.random() * 100) };
|
|
236
|
+
counterSubject.next(newValue);
|
|
237
|
+
}, 1000);
|
|
238
|
+
|
|
239
|
+
// Cleanup
|
|
240
|
+
// counterSubject.dispose();
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Subscribing from Web Page
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// web-page.ts
|
|
247
|
+
import { WebObservable, createIdentifier } from '@weird94/crx-rpc';
|
|
248
|
+
|
|
249
|
+
interface ICounterObservable {
|
|
250
|
+
value: number;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
254
|
+
|
|
255
|
+
// Subscribe to remote observable
|
|
256
|
+
const observable = new WebObservable(
|
|
257
|
+
ICounterObservable,
|
|
258
|
+
'main',
|
|
259
|
+
(value) => {
|
|
260
|
+
console.log('Counter updated:', value.value);
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// Cleanup when done
|
|
265
|
+
// observable.dispose();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Subscribing from Content Script
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// content.ts
|
|
272
|
+
import { ContentObservable, createIdentifier } from '@weird94/crx-rpc';
|
|
273
|
+
|
|
274
|
+
interface ICounterObservable {
|
|
275
|
+
value: number;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const ICounterObservable = createIdentifier<ICounterObservable>('Counter');
|
|
279
|
+
|
|
280
|
+
// Content script can directly subscribe to observables
|
|
281
|
+
const observable = new ContentObservable(
|
|
282
|
+
ICounterObservable,
|
|
283
|
+
'main',
|
|
284
|
+
(value) => {
|
|
285
|
+
console.log('Counter from content script:', value.value);
|
|
286
|
+
// Content script can react to real-time updates
|
|
287
|
+
updateUI(value.value);
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Cleanup when done
|
|
292
|
+
// observable.dispose();
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Observable Communication Patterns
|
|
296
|
+
|
|
297
|
+
The Observable system supports multiple communication patterns:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// Pattern 1: Background โ Web Page (via Content Script bridge)
|
|
301
|
+
// Background: RemoteSubject.next()
|
|
302
|
+
// Web Page: WebObservable.subscribe()
|
|
303
|
+
|
|
304
|
+
// Pattern 2: Background โ Content Script (direct)
|
|
305
|
+
// Background: RemoteSubject.next()
|
|
306
|
+
// Content Script: ContentObservable.subscribe()
|
|
307
|
+
|
|
308
|
+
// Pattern 3: Background โ Both Web Page and Content Script
|
|
309
|
+
// Background: RemoteSubject.next() (broadcasts to all subscribers)
|
|
310
|
+
// Web Page: WebObservable.subscribe()
|
|
311
|
+
// Content Script: ContentObservable.subscribe()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Advanced Usage
|
|
315
|
+
|
|
316
|
+
### Resource Management with Disposables
|
|
317
|
+
|
|
318
|
+
All RPC components extend the `Disposable` class for proper cleanup:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { WebRPCClient, ContentRPC, BackgroundRPC } from '@weird94/crx-rpc';
|
|
322
|
+
|
|
323
|
+
const client = new WebRPCClient();
|
|
324
|
+
const contentRpc = new ContentRPC();
|
|
325
|
+
const backgroundRpc = new BackgroundRPC();
|
|
326
|
+
|
|
327
|
+
// Proper cleanup
|
|
328
|
+
function cleanup() {
|
|
329
|
+
client.dispose();
|
|
330
|
+
contentRpc.dispose();
|
|
331
|
+
backgroundRpc.dispose();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Check if already disposed
|
|
335
|
+
if (!client.isDisposed()) {
|
|
336
|
+
const service = client.createWebRPCService(IMathService);
|
|
337
|
+
// Use service...
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Usage Scenarios
|
|
342
|
+
|
|
343
|
+
### Scenario 1: Web Page Only
|
|
344
|
+
- Web pages need to communicate with background services
|
|
345
|
+
- Use: `WebRPCClient` + `ContentRPC` bridge
|
|
346
|
+
|
|
347
|
+
### Scenario 2: Content Script Only
|
|
348
|
+
- Content scripts need direct access to background services
|
|
349
|
+
- Use: `ContentRPCClient` directly (no bridge needed)
|
|
350
|
+
|
|
351
|
+
### Scenario 3: Both Web Page and Content Script
|
|
352
|
+
- Both contexts need RPC access
|
|
353
|
+
- Use: `ContentRPC` bridge + `ContentRPCClient` for direct access
|
|
354
|
+
|
|
355
|
+
### Scenario 4: Real-time Data Streaming
|
|
356
|
+
- Background needs to push updates to multiple contexts
|
|
357
|
+
- Use: `RemoteSubject` + `WebObservable`/`ContentObservable`
|
|
358
|
+
|
|
359
|
+
## API Reference
|
|
360
|
+
|
|
361
|
+
### Core Classes
|
|
362
|
+
|
|
363
|
+
- **`BackgroundRPC`**: Service registry and message handler for background scripts
|
|
364
|
+
- **`ContentRPC`**: Message bridge between web pages and background scripts
|
|
365
|
+
- **`WebRPCClient`**: RPC client for web pages
|
|
366
|
+
- **`ContentRPCClient`**: Direct RPC client for content scripts
|
|
367
|
+
|
|
368
|
+
### Observable Classes
|
|
369
|
+
|
|
370
|
+
- **`RemoteSubject<T>`**: Observable subject that can broadcast to multiple subscribers
|
|
371
|
+
- **`WebObservable<T>`**: Observable subscriber for web pages
|
|
372
|
+
- **`ContentObservable<T>`**: Observable subscriber for content scripts
|
|
373
|
+
|
|
374
|
+
### Utility Functions
|
|
375
|
+
|
|
376
|
+
- **`createIdentifier<T>(key: string)`**: Creates a type-safe service identifier
|
|
377
|
+
|
|
378
|
+
### Interfaces
|
|
379
|
+
|
|
380
|
+
- **`Identifier<T>`**: Type-safe service identifier interface
|
|
381
|
+
- **`RpcRequest`**: RPC request message structure
|
|
382
|
+
- **`RpcResponse`**: RPC response message structure
|
|
383
|
+
- **`IMessageAdapter`**: Message transport abstraction interface
|
|
384
|
+
- **`IDisposable`**: Resource management interface
|
|
385
|
+
|
|
386
|
+
## Best Practices
|
|
387
|
+
|
|
388
|
+
1. **Service Interface Design**
|
|
389
|
+
- Use clear method names and proper TypeScript types
|
|
390
|
+
- Return Promise types for async operation support
|
|
391
|
+
- Define detailed parameter and return value types
|
|
392
|
+
- Keep interfaces focused and cohesive
|
|
393
|
+
|
|
394
|
+
2. **Resource Management**
|
|
395
|
+
- Always call `dispose()` on RPC instances when cleanup is needed
|
|
396
|
+
- Check `isDisposed()` before using disposed instances
|
|
397
|
+
- Use proper cleanup in component unmount/destroy lifecycle
|
|
398
|
+
|
|
399
|
+
3. **Error Handling**
|
|
400
|
+
- Implement proper error handling in service methods
|
|
401
|
+
- Throw meaningful errors with descriptive messages
|
|
402
|
+
- Handle RPC errors appropriately on the client side
|
|
403
|
+
|
|
404
|
+
4. **Performance Optimization**
|
|
405
|
+
- Avoid frequent small data transfers
|
|
406
|
+
- Consider batching operations when possible
|
|
407
|
+
- Use Observable pattern for real-time data updates
|
|
408
|
+
- Implement caching strategies where appropriate
|
|
409
|
+
|
|
410
|
+
5. **Security Considerations**
|
|
411
|
+
- Validate input parameters in service implementations
|
|
412
|
+
- Don't expose sensitive operations through RPC
|
|
413
|
+
- Consider rate limiting for resource-intensive operations
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
MIT
|