@thalesrc/hermes 6.1.1 → 7.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.
- package/README.md +417 -57
- package/broadcast/default-channel-name.cjs +4 -0
- package/broadcast/default-channel-name.js +2 -0
- package/broadcast/default-channel-name.js.map +1 -0
- package/broadcast/index.cjs +13 -0
- package/broadcast/index.js +2 -0
- package/broadcast/index.js.map +1 -0
- package/broadcast/message-client.cjs +26 -0
- package/broadcast/message-client.js +2 -0
- package/broadcast/message-client.js.map +1 -0
- package/broadcast/message-host.cjs +27 -0
- package/broadcast/message-host.js +2 -0
- package/broadcast/message-host.js.map +1 -0
- package/broadcast/message-service.cjs +13 -0
- package/broadcast/message-service.d.ts +1 -1
- package/broadcast/message-service.js +4 -2
- package/broadcast/message-service.js.map +1 -0
- package/chrome/default-connection-name.cjs +4 -0
- package/chrome/default-connection-name.js +2 -0
- package/chrome/default-connection-name.js.map +1 -0
- package/chrome/index.cjs +11 -0
- package/chrome/index.js +2 -0
- package/chrome/index.js.map +1 -0
- package/chrome/message-client.cjs +41 -0
- package/chrome/message-client.js +2 -0
- package/chrome/message-client.js.map +1 -0
- package/chrome/message-host.cjs +42 -0
- package/chrome/message-host.d.ts +1 -5
- package/chrome/message-host.js +11 -11
- package/chrome/message-host.js.map +1 -0
- package/iframe/channel-path-splitter.cjs +4 -0
- package/iframe/channel-path-splitter.js +2 -0
- package/iframe/channel-path-splitter.js.map +1 -0
- package/iframe/default-channel-name.cjs +4 -0
- package/iframe/default-channel-name.js +2 -0
- package/iframe/default-channel-name.js.map +1 -0
- package/iframe/iframe.type.cjs +2 -0
- package/iframe/iframe.type.js +2 -0
- package/iframe/iframe.type.js.map +1 -0
- package/iframe/index.cjs +13 -0
- package/iframe/index.js +2 -0
- package/iframe/index.js.map +1 -0
- package/iframe/message-client.cjs +50 -0
- package/iframe/message-client.d.ts +1 -5
- package/iframe/message-client.js +10 -10
- package/iframe/message-client.js.map +1 -0
- package/iframe/message-host.cjs +62 -0
- package/iframe/message-host.d.ts +1 -11
- package/iframe/message-host.js +20 -23
- package/iframe/message-host.js.map +1 -0
- package/iframe/message-service.cjs +13 -0
- package/iframe/message-service.d.ts +1 -1
- package/iframe/message-service.js +4 -2
- package/iframe/message-service.js.map +1 -0
- package/iframe/source-id-splitter.cjs +4 -0
- package/iframe/source-id-splitter.js +2 -0
- package/iframe/source-id-splitter.js.map +1 -0
- package/iframe/upcoming-message.cjs +2 -0
- package/iframe/upcoming-message.js +2 -0
- package/iframe/upcoming-message.js.map +1 -0
- package/index.cjs +13 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/listen.decorator.cjs +20 -0
- package/listen.decorator.js +4 -2
- package/listen.decorator.js.map +1 -0
- package/listener-storage.type.cjs +2 -0
- package/listener-storage.type.js +2 -0
- package/listener-storage.type.js.map +1 -0
- package/message-client.cjs +10 -0
- package/message-client.js +2 -0
- package/message-client.js.map +1 -0
- package/message-host.cjs +53 -0
- package/message-host.js +2 -0
- package/message-host.js.map +1 -0
- package/message-response.type.cjs +2 -0
- package/message-response.type.js +2 -0
- package/message-response.type.js.map +1 -0
- package/message.interface.cjs +2 -0
- package/message.interface.js +2 -0
- package/message.interface.js.map +1 -0
- package/package.json +32 -34
- package/request.decorator.cjs +20 -0
- package/request.decorator.js +3 -1
- package/request.decorator.js.map +1 -0
- package/selectors.cjs +8 -0
- package/selectors.js +2 -0
- package/selectors.js.map +1 -0
- package/worker/index.cjs +9 -0
- package/worker/index.js +2 -0
- package/worker/index.js.map +1 -0
- package/worker/message-client.cjs +40 -0
- package/worker/message-client.d.ts +1 -1
- package/worker/message-client.js +2 -0
- package/worker/message-client.js.map +1 -0
- package/worker/message-host.cjs +43 -0
- package/worker/message-host.js +2 -0
- package/worker/message-host.js.map +1 -0
- package/worker/message-service.cjs +12 -0
- package/worker/message-service.d.ts +1 -1
- package/worker/message-service.js +4 -2
- package/worker/message-service.js.map +1 -0
- package/.editorconfig +0 -9
- package/CODE_OF_CONDUCT.md +0 -76
- package/codecov.yml +0 -8
- package/jest.config.js +0 -22
- package/mixin.d.ts +0 -12
- package/mixin.js +0 -18
- package/setup-test.ts +0 -0
- package/tsconfig.lib.json +0 -12
- package/tsconfig.spec.json +0 -11
- package/tslint.json +0 -14
package/README.md
CHANGED
|
@@ -1,121 +1,481 @@
|
|
|
1
|
-
# hermes
|
|
2
|
-
|
|
1
|
+
# @thalesrc/hermes
|
|
2
|
+
|
|
3
3
|
[](https://www.npmjs.com/package/@thalesrc/hermes)
|
|
4
4
|
[](https://www.npmjs.com/package/@thalesrc/hermes)
|
|
5
|
-
[](https://codecov.io/gh/thalesrc/hermes)
|
|
6
5
|
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Javascript messaging library for cross-context communication
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
**Part of the [Thalesrc](https://github.com/thalesrc/thalesrc) monorepo**
|
|
9
11
|
|
|
10
12
|
## Installation
|
|
11
|
-
`npm i @thalesrc/hermes` or `yarn add @thalesrc/hermes`
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npm install @thalesrc/hermes
|
|
16
|
+
# or
|
|
17
|
+
yarn add @thalesrc/hermes
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @thalesrc/hermes
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
Hermes provides a unified, decorator-based API for cross-context messaging in JavaScript applications. Built on RxJS, it supports:
|
|
25
|
+
|
|
26
|
+
- 🖼️ **Iframe Communication** - Parent-child window messaging
|
|
27
|
+
- 🧩 **Chrome Extensions** - Background scripts, content scripts, and popups
|
|
28
|
+
- 👷 **Web Workers** - Main thread and worker communication
|
|
29
|
+
- 📡 **Broadcast Channel** - Tab-to-tab messaging
|
|
30
|
+
|
|
31
|
+
## Core Concepts
|
|
32
|
+
|
|
33
|
+
### MessageClient & MessageHost
|
|
34
|
+
|
|
35
|
+
The library uses two main abstractions:
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
- **`MessageClient`**: Sends requests and receives responses (uses `@Request` decorator)
|
|
38
|
+
- **`MessageHost`**: Listens for requests and sends responses (uses `@Listen` decorator)
|
|
39
|
+
- **`MessageService`**: Combines both client and host (bidirectional communication)
|
|
16
40
|
|
|
17
|
-
|
|
41
|
+
### Decorators
|
|
18
42
|
|
|
19
|
-
|
|
43
|
+
- **`@Request(path)`**: Marks a method as a message sender
|
|
44
|
+
- **`@Listen(path)`**: Marks a method as a message listener
|
|
20
45
|
|
|
21
|
-
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Iframe Communication
|
|
49
|
+
|
|
50
|
+
Send and receive messages between iframes and parent windows.
|
|
51
|
+
|
|
52
|
+
#### Client-Only (Iframe)
|
|
22
53
|
|
|
23
54
|
```typescript
|
|
24
|
-
// inside iframe
|
|
25
55
|
import { IframeMessageClient, Request } from '@thalesrc/hermes/iframe';
|
|
56
|
+
import { Observable } from 'rxjs';
|
|
26
57
|
|
|
27
|
-
class
|
|
28
|
-
@Request('
|
|
29
|
-
|
|
30
|
-
return null;
|
|
58
|
+
class IframeClient extends IframeMessageClient {
|
|
59
|
+
@Request('greeting')
|
|
60
|
+
sayHello(name: string): Observable<string> {
|
|
61
|
+
return null; // Implementation handled by decorator
|
|
31
62
|
}
|
|
32
63
|
}
|
|
33
64
|
|
|
34
|
-
|
|
65
|
+
// Default: sends messages to parent window
|
|
66
|
+
const client = new IframeClient();
|
|
35
67
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
68
|
+
// Optional: specify channel name
|
|
69
|
+
const clientWithChannel = new IframeClient('my-channel');
|
|
70
|
+
|
|
71
|
+
// Optional: target specific iframe (from parent window)
|
|
72
|
+
const iframe = document.querySelector('iframe');
|
|
73
|
+
const clientToIframe = new IframeClient('my-channel', iframe);
|
|
39
74
|
|
|
40
|
-
//
|
|
41
|
-
|
|
75
|
+
// Optional: use a function to get iframe dynamically
|
|
76
|
+
const clientWithDynamicIframe = new IframeClient('my-channel', () =>
|
|
77
|
+
document.querySelector('iframe#dynamic')
|
|
78
|
+
);
|
|
42
79
|
|
|
80
|
+
client.sayHello('John').subscribe(response => {
|
|
81
|
+
console.log(response); // 'Hello John!'
|
|
82
|
+
});
|
|
43
83
|
```
|
|
44
84
|
|
|
85
|
+
#### Host-Only (Parent Window)
|
|
86
|
+
|
|
45
87
|
```typescript
|
|
46
|
-
// inside parent window
|
|
47
88
|
import { IframeMessageHost, Listen, UpcomingMessage } from '@thalesrc/hermes/iframe';
|
|
48
89
|
import { of } from 'rxjs';
|
|
49
90
|
|
|
50
|
-
class
|
|
51
|
-
@Listen('
|
|
52
|
-
|
|
53
|
-
return of(
|
|
54
|
-
'Hi ' + data + ', here is some data for you',
|
|
55
|
-
'Thales Rocks!!'
|
|
56
|
-
);
|
|
91
|
+
class ParentHost extends IframeMessageHost {
|
|
92
|
+
@Listen('greeting')
|
|
93
|
+
handleGreeting({ data }: UpcomingMessage<string>): Observable<string> {
|
|
94
|
+
return of(`Hello ${data}!`);
|
|
57
95
|
}
|
|
58
96
|
}
|
|
59
97
|
|
|
60
|
-
|
|
98
|
+
// Default: listens to all iframes
|
|
99
|
+
const host = new ParentHost();
|
|
61
100
|
|
|
101
|
+
// Optional: specify channel name
|
|
102
|
+
const hostWithChannel = new ParentHost('my-channel');
|
|
103
|
+
|
|
104
|
+
// Optional: listen only to specific iframe
|
|
105
|
+
const iframe = document.querySelector('iframe');
|
|
106
|
+
const hostForSpecificIframe = new ParentHost('my-channel', iframe);
|
|
107
|
+
|
|
108
|
+
// Optional: use a function to get iframe dynamically
|
|
109
|
+
const hostWithDynamicIframe = new ParentHost('my-channel', () =>
|
|
110
|
+
document.querySelector('iframe#dynamic')
|
|
111
|
+
);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Bidirectional Communication (MessageService)
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { IframeMessageService, Request, Listen } from '@thalesrc/hermes/iframe';
|
|
118
|
+
import { of, Observable } from 'rxjs';
|
|
119
|
+
|
|
120
|
+
class IframeBidirectional extends IframeMessageService {
|
|
121
|
+
// Send messages
|
|
122
|
+
@Request('getData')
|
|
123
|
+
requestData(): Observable<any> {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Receive messages
|
|
128
|
+
@Listen('update')
|
|
129
|
+
handleUpdate(data: any): Observable<string> {
|
|
130
|
+
console.log('Received update:', data);
|
|
131
|
+
return of('Update processed');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Default: communicates with parent window
|
|
136
|
+
const service = new IframeBidirectional();
|
|
137
|
+
|
|
138
|
+
// Optional: specify channel name and target frame
|
|
139
|
+
const iframe = document.querySelector('iframe');
|
|
140
|
+
const serviceWithTarget = new IframeBidirectional('my-channel', iframe);
|
|
141
|
+
|
|
142
|
+
// From within iframe (communicates with parent)
|
|
143
|
+
const iframeService = new IframeBidirectional('my-channel');
|
|
62
144
|
```
|
|
63
145
|
|
|
64
|
-
|
|
146
|
+
---
|
|
65
147
|
|
|
66
148
|
### Chrome Extensions
|
|
67
149
|
|
|
68
|
-
|
|
150
|
+
Communicate across extension contexts (background, content scripts, popups).
|
|
151
|
+
|
|
152
|
+
#### Content Script
|
|
69
153
|
|
|
70
154
|
```typescript
|
|
71
|
-
// content-script or a page etc.
|
|
72
155
|
import { ChromeMessageClient, Request } from '@thalesrc/hermes/chrome';
|
|
156
|
+
import { Observable } from 'rxjs';
|
|
73
157
|
|
|
74
|
-
class
|
|
75
|
-
@Request('
|
|
76
|
-
|
|
158
|
+
class ContentScript extends ChromeMessageClient {
|
|
159
|
+
@Request('fetchData')
|
|
160
|
+
getData(query: string): Observable<any> {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@Request('saveSettings')
|
|
165
|
+
saveSettings(settings: object): Observable<boolean> {
|
|
77
166
|
return null;
|
|
78
167
|
}
|
|
79
168
|
}
|
|
80
169
|
|
|
81
|
-
const
|
|
170
|
+
const contentScript = new ContentScript();
|
|
82
171
|
|
|
83
|
-
|
|
84
|
-
console.log(
|
|
172
|
+
contentScript.getData('user').subscribe(data => {
|
|
173
|
+
console.log('Received:', data);
|
|
85
174
|
});
|
|
175
|
+
```
|
|
86
176
|
|
|
87
|
-
|
|
88
|
-
// 'Thales Rocks!!'
|
|
177
|
+
#### Background Script
|
|
89
178
|
|
|
179
|
+
```typescript
|
|
180
|
+
import { ChromeMessageHost, Listen } from '@thalesrc/hermes/chrome';
|
|
181
|
+
import { of, Observable } from 'rxjs';
|
|
182
|
+
|
|
183
|
+
class BackgroundScript extends ChromeMessageHost {
|
|
184
|
+
@Listen('fetchData')
|
|
185
|
+
handleFetchData(query: string): Observable<any> {
|
|
186
|
+
// Fetch from API or storage
|
|
187
|
+
return of({ name: 'John', age: 30 });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@Listen('saveSettings')
|
|
191
|
+
handleSaveSettings(settings: object): Observable<boolean> {
|
|
192
|
+
// Save to chrome.storage
|
|
193
|
+
return of(true);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const background = new BackgroundScript();
|
|
90
198
|
```
|
|
91
199
|
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Web Workers
|
|
203
|
+
|
|
204
|
+
Communicate between main thread and web workers.
|
|
205
|
+
|
|
206
|
+
#### Main Thread
|
|
207
|
+
|
|
92
208
|
```typescript
|
|
93
|
-
|
|
94
|
-
import {
|
|
95
|
-
|
|
209
|
+
import { WorkerMessageService, Request, Listen } from '@thalesrc/hermes/worker';
|
|
210
|
+
import { of, Observable } from 'rxjs';
|
|
211
|
+
|
|
212
|
+
class MainThread extends WorkerMessageService {
|
|
213
|
+
@Request('processData')
|
|
214
|
+
sendDataToWorker(data: number[]): Observable<number> {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@Listen('progress')
|
|
219
|
+
handleProgress(percent: number): Observable<void> {
|
|
220
|
+
console.log(`Progress: ${percent}%`);
|
|
221
|
+
return of(void 0);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Must provide Worker instance in main thread
|
|
226
|
+
const worker = new Worker('./worker.js');
|
|
227
|
+
const mainService = new MainThread(worker);
|
|
228
|
+
|
|
229
|
+
mainService.sendDataToWorker([1, 2, 3, 4, 5]).subscribe(result => {
|
|
230
|
+
console.log('Worker result:', result);
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### Worker Thread
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { WorkerMessageService, Request, Listen } from '@thalesrc/hermes/worker';
|
|
238
|
+
import { of, Observable } from 'rxjs';
|
|
239
|
+
|
|
240
|
+
class WorkerThread extends WorkerMessageService {
|
|
241
|
+
@Listen('processData')
|
|
242
|
+
handleProcessData(data: number[]): Observable<number> {
|
|
243
|
+
// Heavy computation
|
|
244
|
+
const result = data.reduce((sum, n) => sum + n, 0);
|
|
245
|
+
return of(result);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@Request('progress')
|
|
249
|
+
reportProgress(percent: number): Observable<void> {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Inside worker: no argument needed (uses self)
|
|
255
|
+
const workerService = new WorkerThread();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### Broadcast Channel
|
|
261
|
+
|
|
262
|
+
Communicate between different tabs/windows of the same origin.
|
|
263
|
+
|
|
264
|
+
#### Tab 1
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { BroadcastMessageService, Request, Listen } from '@thalesrc/hermes/broadcast';
|
|
268
|
+
import { of, Observable } from 'rxjs';
|
|
269
|
+
|
|
270
|
+
class Tab1 extends BroadcastMessageService {
|
|
271
|
+
@Request('sync')
|
|
272
|
+
requestSync(data: any): Observable<string> {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@Listen('notification')
|
|
277
|
+
handleNotification(message: string): Observable<void> {
|
|
278
|
+
console.log('Notification:', message);
|
|
279
|
+
return of(void 0);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const tab1 = new Tab1('my-app-channel');
|
|
284
|
+
|
|
285
|
+
tab1.requestSync({ user: 'John' }).subscribe(response => {
|
|
286
|
+
console.log(response); // 'Sync completed'
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### Tab 2
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { BroadcastMessageService, Request, Listen } from '@thalesrc/hermes/broadcast';
|
|
294
|
+
import { of, Observable } from 'rxjs';
|
|
295
|
+
|
|
296
|
+
class Tab2 extends BroadcastMessageService {
|
|
297
|
+
@Listen('sync')
|
|
298
|
+
handleSync(data: any): Observable<string> {
|
|
299
|
+
console.log('Syncing data:', data);
|
|
300
|
+
return of('Sync completed');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@Request('notification')
|
|
304
|
+
sendNotification(message: string): Observable<void> {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const tab2 = new Tab2('my-app-channel');
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Advanced Features
|
|
315
|
+
|
|
316
|
+
### Streaming Responses
|
|
317
|
+
|
|
318
|
+
Return multiple values over time using RxJS operators:
|
|
96
319
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
320
|
+
```typescript
|
|
321
|
+
import { Listen } from '@thalesrc/hermes/iframe';
|
|
322
|
+
import { interval } from 'rxjs';
|
|
323
|
+
import { map, take } from 'rxjs/operators';
|
|
324
|
+
|
|
325
|
+
class StreamingHost extends IframeMessageHost {
|
|
326
|
+
@Listen('countdown')
|
|
327
|
+
handleCountdown(start: number): Observable<number> {
|
|
328
|
+
return interval(1000).pipe(
|
|
329
|
+
map(i => start - i),
|
|
330
|
+
take(start + 1)
|
|
103
331
|
);
|
|
104
332
|
}
|
|
105
333
|
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
The client receives each value as it's emitted:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
client.countdown(5).subscribe(
|
|
340
|
+
value => console.log(value), // 5, 4, 3, 2, 1, 0
|
|
341
|
+
error => console.error(error),
|
|
342
|
+
() => console.log('Complete!')
|
|
343
|
+
);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Error Handling
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { Listen } from '@thalesrc/hermes/iframe';
|
|
350
|
+
import { throwError } from 'rxjs';
|
|
351
|
+
|
|
352
|
+
class ErrorHost extends IframeMessageHost {
|
|
353
|
+
@Listen('riskyOperation')
|
|
354
|
+
handleRiskyOperation(data: any): Observable<any> {
|
|
355
|
+
if (!data.valid) {
|
|
356
|
+
return throwError(() => new Error('Invalid data'));
|
|
357
|
+
}
|
|
358
|
+
return of({ success: true });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Constructor Parameters
|
|
106
364
|
|
|
107
|
-
|
|
365
|
+
#### Iframe
|
|
108
366
|
|
|
367
|
+
**`IframeMessageClient` / `IframeMessageHost` / `IframeMessageService`**
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
constructor(channelName?: string, targetFrame?: HTMLIFrameElement | (() => HTMLIFrameElement))
|
|
109
371
|
```
|
|
110
372
|
|
|
111
|
-
|
|
373
|
+
- **`channelName`** (optional): Channel identifier for namespacing messages. Default: `'hermes-iframe-message'`
|
|
374
|
+
- **`targetFrame`** (optional): Specific iframe to communicate with. Can be:
|
|
375
|
+
- `HTMLIFrameElement`: Direct reference to iframe element
|
|
376
|
+
- `() => HTMLIFrameElement`: Function returning iframe (useful for dynamic iframes)
|
|
377
|
+
- Omit to communicate with parent window (from iframe) or all iframes (from parent)
|
|
378
|
+
|
|
379
|
+
**Examples:**
|
|
380
|
+
```typescript
|
|
381
|
+
// From iframe: communicate with parent
|
|
382
|
+
const client = new IframeMessageClient();
|
|
383
|
+
|
|
384
|
+
// From parent: communicate with specific iframe
|
|
385
|
+
const iframe = document.querySelector('iframe');
|
|
386
|
+
const host = new IframeMessageHost('my-channel', iframe);
|
|
387
|
+
|
|
388
|
+
// Dynamic iframe reference
|
|
389
|
+
const service = new IframeMessageService('my-channel', () =>
|
|
390
|
+
document.querySelector('iframe[data-active="true"]')
|
|
391
|
+
);
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
#### Worker
|
|
395
|
+
|
|
396
|
+
**`WorkerMessageClient` / `WorkerMessageHost` / `WorkerMessageService`**
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
constructor(worker?: Worker)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
- **`worker`** (optional): Worker instance for main thread communication
|
|
403
|
+
- **In main thread**: Must provide `Worker` instance
|
|
404
|
+
- **In worker thread**: Omit parameter (uses `self` automatically)
|
|
405
|
+
|
|
406
|
+
**Examples:**
|
|
407
|
+
```typescript
|
|
408
|
+
// Main thread: must provide worker
|
|
409
|
+
const worker = new Worker('./worker.js');
|
|
410
|
+
const service = new WorkerMessageService(worker);
|
|
411
|
+
|
|
412
|
+
// Inside worker: no parameter needed
|
|
413
|
+
const service = new WorkerMessageService();
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### Broadcast
|
|
417
|
+
|
|
418
|
+
**`BroadcastMessageClient` / `BroadcastMessageHost` / `BroadcastMessageService`**
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
constructor(channelName?: string)
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
- **`channelName`** (optional): Broadcast channel name. Default: `'hermes-broadcast-message'`
|
|
425
|
+
|
|
426
|
+
**Example:**
|
|
427
|
+
```typescript
|
|
428
|
+
const service = new BroadcastMessageService('app-sync-channel');
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### Chrome
|
|
432
|
+
|
|
433
|
+
**`ChromeMessageClient` / `ChromeMessageHost`**
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
constructor(connectionName?: string)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
- **`connectionName`** (optional): Connection identifier. Default: `'hermes-chrome-message'`
|
|
440
|
+
|
|
441
|
+
**Example:**
|
|
442
|
+
```typescript
|
|
443
|
+
const client = new ChromeMessageClient('extension-port');
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## API Reference
|
|
447
|
+
|
|
448
|
+
### Classes
|
|
449
|
+
|
|
450
|
+
- **`MessageClient`** - Base class for message senders
|
|
451
|
+
- **`MessageHost`** - Base class for message receivers
|
|
452
|
+
- **`IframeMessageClient`** - Iframe client implementation
|
|
453
|
+
- **`IframeMessageHost`** - Iframe host implementation
|
|
454
|
+
- **`IframeMessageService`** - Bidirectional iframe communication
|
|
455
|
+
- **`ChromeMessageClient`** - Chrome extension client
|
|
456
|
+
- **`ChromeMessageHost`** - Chrome extension host
|
|
457
|
+
- **`WorkerMessageService`** - Bidirectional worker communication
|
|
458
|
+
- **`BroadcastMessageClient`** - Broadcast channel client
|
|
459
|
+
- **`BroadcastMessageHost`** - Broadcast channel host
|
|
460
|
+
- **`BroadcastMessageService`** - Bidirectional broadcast communication
|
|
461
|
+
|
|
462
|
+
### Decorators
|
|
463
|
+
|
|
464
|
+
- **`@Request(path: string)`** - Decorator for sending messages
|
|
465
|
+
- **`@Listen(path: string)`** - Decorator for receiving messages
|
|
466
|
+
|
|
467
|
+
### Types
|
|
112
468
|
|
|
113
|
-
|
|
469
|
+
- **`Message`** - Message payload structure
|
|
470
|
+
- **`MessageResponse`** - Response payload structure
|
|
471
|
+
- **`UpcomingMessage<T>`** - Incoming message with sender info (iframe)
|
|
114
472
|
|
|
115
|
-
|
|
473
|
+
## Requirements
|
|
116
474
|
|
|
117
|
-
|
|
475
|
+
- RxJS 7.x or higher
|
|
476
|
+
- TypeScript 4.x or higher (for decorator support)
|
|
477
|
+
- Modern browser with ES2015+ support
|
|
118
478
|
|
|
119
|
-
|
|
479
|
+
## License
|
|
120
480
|
|
|
121
|
-
|
|
481
|
+
MIT © [Thalesrc](https://github.com/thalesrc)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../libs/hermes/src/broadcast/default-channel-name.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC","file":"default-channel-name.js","sourcesContent":["export const DEFAULT_CHANNEL_NAME = 'HERMES_DEFAULT_CHANNEL';\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Listen = exports.Request = exports.BroadcastMessageService = exports.BroadcastMessageHost = exports.BroadcastMessageClient = void 0;
|
|
4
|
+
var message_client_1 = require("./message-client");
|
|
5
|
+
Object.defineProperty(exports, "BroadcastMessageClient", { enumerable: true, get: function () { return message_client_1.BroadcastMessageClient; } });
|
|
6
|
+
var message_host_1 = require("./message-host");
|
|
7
|
+
Object.defineProperty(exports, "BroadcastMessageHost", { enumerable: true, get: function () { return message_host_1.BroadcastMessageHost; } });
|
|
8
|
+
var message_service_1 = require("./message-service");
|
|
9
|
+
Object.defineProperty(exports, "BroadcastMessageService", { enumerable: true, get: function () { return message_service_1.BroadcastMessageService; } });
|
|
10
|
+
var request_decorator_1 = require("../request.decorator");
|
|
11
|
+
Object.defineProperty(exports, "Request", { enumerable: true, get: function () { return request_decorator_1.Request; } });
|
|
12
|
+
var listen_decorator_1 = require("../listen.decorator");
|
|
13
|
+
Object.defineProperty(exports, "Listen", { enumerable: true, get: function () { return listen_decorator_1.Listen; } });
|
package/broadcast/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../libs/hermes/src/broadcast/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC","file":"index.js","sourcesContent":["export { BroadcastMessageClient } from './message-client';\nexport { BroadcastMessageHost } from './message-host';\nexport { BroadcastMessageService } from './message-service';\nexport { Request } from '../request.decorator';\nexport { Listen } from '../listen.decorator';\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BroadcastMessageClient = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const message_client_1 = require("../message-client");
|
|
6
|
+
const selectors_1 = require("../selectors");
|
|
7
|
+
const default_channel_name_1 = require("./default-channel-name");
|
|
8
|
+
class BroadcastMessageClient extends message_client_1.MessageClient {
|
|
9
|
+
[selectors_1.RESPONSES$] = new rxjs_1.Subject();
|
|
10
|
+
#channel;
|
|
11
|
+
constructor(channelName = default_channel_name_1.DEFAULT_CHANNEL_NAME) {
|
|
12
|
+
super();
|
|
13
|
+
this.#channel = new BroadcastChannel(channelName);
|
|
14
|
+
this.#channel.addEventListener('message', this.#handler);
|
|
15
|
+
}
|
|
16
|
+
[selectors_1.SEND](message) {
|
|
17
|
+
this.#channel.postMessage(message);
|
|
18
|
+
}
|
|
19
|
+
#handler = (event) => {
|
|
20
|
+
this[selectors_1.RESPONSES$].next(event.data);
|
|
21
|
+
};
|
|
22
|
+
[selectors_1.GET_NEW_ID]() {
|
|
23
|
+
return crypto.randomUUID();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.BroadcastMessageClient = BroadcastMessageClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../libs/hermes/src/broadcast/message-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAM9D,MAAM,OAAO,sBAAuB,SAAQ,aAAa;IAC7C,CAAC,UAAU,CAAC,GAAG,IAAI,OAAO,EAAmB,CAAC;IAExD,QAAQ,CAAmB;IAE3B,YAAY,WAAW,GAAG,oBAAoB;QAC5C,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAElD,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAES,CAAC,IAAI,CAAC,CAAI,OAAmB;QACrC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,QAAQ,GAAG,CAAC,KAAoC,EAAE,EAAE;QAClD,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC,CAAA;IAES,CAAC,UAAU,CAAC;QACpB,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;CACF","file":"message-client.js","sourcesContent":["import { Subject } from \"rxjs\";\nimport { MessageClient } from \"../message-client\";\nimport { MessageResponse } from \"../message-response.type\";\nimport { Message } from \"../message.interface\";\nimport { GET_NEW_ID, RESPONSES$, SEND } from \"../selectors\";\nimport { DEFAULT_CHANNEL_NAME } from \"./default-channel-name\";\n\ninterface MessageEvent<T> {\n data: T;\n}\n\nexport class BroadcastMessageClient extends MessageClient {\n protected [RESPONSES$] = new Subject<MessageResponse>();\n\n #channel: BroadcastChannel;\n\n constructor(channelName = DEFAULT_CHANNEL_NAME) {\n super();\n\n this.#channel = new BroadcastChannel(channelName);\n\n this.#channel.addEventListener('message', this.#handler);\n }\n\n protected [SEND]<T>(message: Message<T>) {\n this.#channel.postMessage(message);\n }\n\n #handler = (event: MessageEvent<MessageResponse>) => {\n this[RESPONSES$].next(event.data);\n }\n\n protected [GET_NEW_ID](): string {\n return crypto.randomUUID();\n }\n}"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BroadcastMessageHost = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const message_host_1 = require("../message-host");
|
|
6
|
+
const default_channel_name_1 = require("./default-channel-name");
|
|
7
|
+
class BroadcastMessageHost extends message_host_1.MessageHost {
|
|
8
|
+
#requests$ = new rxjs_1.Subject();
|
|
9
|
+
#channel;
|
|
10
|
+
constructor(channelName = default_channel_name_1.DEFAULT_CHANNEL_NAME) {
|
|
11
|
+
super();
|
|
12
|
+
this.#channel = new BroadcastChannel(channelName);
|
|
13
|
+
this.#channel.addEventListener('message', this.#handler);
|
|
14
|
+
this.listen(this.#requests$);
|
|
15
|
+
}
|
|
16
|
+
response(message) {
|
|
17
|
+
this.#channel.postMessage(message);
|
|
18
|
+
}
|
|
19
|
+
terminate() {
|
|
20
|
+
this.#channel.removeEventListener('message', this.#handler);
|
|
21
|
+
this.#channel.close();
|
|
22
|
+
}
|
|
23
|
+
#handler = (event) => {
|
|
24
|
+
this.#requests$.next(event.data);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.BroadcastMessageHost = BroadcastMessageHost;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../libs/hermes/src/broadcast/message-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAM9D,MAAM,OAAO,oBAAqB,SAAQ,WAAW;IACnD,UAAU,GAAG,IAAI,OAAO,EAAW,CAAC;IACpC,QAAQ,CAAmB;IAE3B,YAAY,WAAW,GAAG,oBAAoB;QAC5C,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAElD,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAES,QAAQ,CAAC,OAAwB;QACzC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,SAAS;QACP,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,QAAQ,GAAG,CAAC,KAA4B,EAAE,EAAE;QAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAA;CACF","file":"message-host.js","sourcesContent":["import { Subject } from \"rxjs\";\nimport { MessageHost } from \"../message-host\";\nimport { MessageResponse } from \"../message-response.type\";\nimport { Message } from \"../message.interface\";\nimport { DEFAULT_CHANNEL_NAME } from \"./default-channel-name\";\n\ninterface MessageEvent<T> {\n data: T;\n}\n\nexport class BroadcastMessageHost extends MessageHost {\n #requests$ = new Subject<Message>();\n #channel: BroadcastChannel;\n\n constructor(channelName = DEFAULT_CHANNEL_NAME) {\n super();\n\n this.#channel = new BroadcastChannel(channelName);\n\n this.#channel.addEventListener('message', this.#handler);\n this.listen(this.#requests$);\n }\n\n protected response(message: MessageResponse) {\n this.#channel.postMessage(message);\n }\n\n terminate() {\n this.#channel.removeEventListener('message', this.#handler);\n this.#channel.close();\n }\n\n #handler = (event: MessageEvent<Message>) => {\n this.#requests$.next(event.data);\n }\n}"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BroadcastMessageService = void 0;
|
|
4
|
+
const mixin_1 = require("@thalesrc/js-utils/class/mixin");
|
|
5
|
+
const default_channel_name_1 = require("./default-channel-name");
|
|
6
|
+
const message_client_1 = require("./message-client");
|
|
7
|
+
const message_host_1 = require("./message-host");
|
|
8
|
+
class BroadcastMessageService extends (0, mixin_1.mixin)(message_host_1.BroadcastMessageHost, message_client_1.BroadcastMessageClient) {
|
|
9
|
+
constructor(channelName = default_channel_name_1.DEFAULT_CHANNEL_NAME) {
|
|
10
|
+
super([channelName], [channelName]);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.BroadcastMessageService = BroadcastMessageService;
|