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 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