@aspectly/transports 0.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/LICENSE +20 -0
- package/README.md +368 -0
- package/dist/BaseTransport-CxzIr1Ds.d.mts +80 -0
- package/dist/BaseTransport-CxzIr1Ds.d.ts +80 -0
- package/dist/cefsharp.d.mts +37 -0
- package/dist/cefsharp.d.ts +37 -0
- package/dist/cefsharp.js +65 -0
- package/dist/cefsharp.js.map +1 -0
- package/dist/cefsharp.mjs +62 -0
- package/dist/cefsharp.mjs.map +1 -0
- package/dist/iframe.d.mts +35 -0
- package/dist/iframe.d.ts +35 -0
- package/dist/iframe.js +75 -0
- package/dist/iframe.js.map +1 -0
- package/dist/iframe.mjs +72 -0
- package/dist/iframe.mjs.map +1 -0
- package/dist/index.d.mts +85 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +374 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +358 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-native.d.mts +36 -0
- package/dist/react-native.d.ts +36 -0
- package/dist/react-native.js +65 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +62 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/window.d.mts +36 -0
- package/dist/window.d.ts +36 -0
- package/dist/window.js +79 -0
- package/dist/window.js.map +1 -0
- package/dist/window.mjs +76 -0
- package/dist/window.mjs.map +1 -0
- package/package.json +97 -0
- package/src/BaseTransport.test.ts +60 -0
- package/src/BaseTransport.ts +27 -0
- package/src/TransportRegistry.test.ts +345 -0
- package/src/TransportRegistry.ts +120 -0
- package/src/cefsharp.ts +3 -0
- package/src/iframe.ts +3 -0
- package/src/index.ts +26 -0
- package/src/react-native.ts +3 -0
- package/src/transports/CefSharpTransport.test.ts +187 -0
- package/src/transports/CefSharpTransport.ts +73 -0
- package/src/transports/IframeTransport.test.ts +212 -0
- package/src/transports/IframeTransport.ts +79 -0
- package/src/transports/NullTransport.test.ts +64 -0
- package/src/transports/NullTransport.ts +27 -0
- package/src/transports/PostMessageTransport.ts +50 -0
- package/src/transports/ReactNativeTransport.test.ts +196 -0
- package/src/transports/ReactNativeTransport.ts +73 -0
- package/src/transports/WindowTransport.ts +84 -0
- package/src/types.ts +69 -0
- package/src/window.ts +3 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Zhan Isaakian
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# @aspectly/transports
|
|
2
|
+
|
|
3
|
+
Transport layer for cross-platform communication between WebViews, iframes, and web applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm
|
|
9
|
+
npm install @aspectly/transports
|
|
10
|
+
|
|
11
|
+
# pnpm
|
|
12
|
+
pnpm add @aspectly/transports
|
|
13
|
+
|
|
14
|
+
# yarn
|
|
15
|
+
yarn add @aspectly/transports
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Overview
|
|
19
|
+
|
|
20
|
+
`@aspectly/transports` provides a flexible abstraction layer for platform detection and message passing across different execution contexts. It automatically detects the current environment (CefSharp, React Native WebView, iframe, or browser) and provides a unified API for bidirectional communication.
|
|
21
|
+
|
|
22
|
+
### Built-in Transports
|
|
23
|
+
|
|
24
|
+
| Transport | Detection | Priority | Use Case |
|
|
25
|
+
|-----------|-----------|----------|----------|
|
|
26
|
+
| **CefSharpTransport** | `window.CefSharp.PostMessage` | 100 | Desktop apps with CefSharp (Chromium Embedded Framework for .NET) |
|
|
27
|
+
| **ReactNativeTransport** | `window.ReactNativeWebView.postMessage` | 90 | React Native WebView |
|
|
28
|
+
| **IframeTransport** | `window.parent !== window` | 80 | Web content inside iframes |
|
|
29
|
+
| **NullTransport** | Always available | - | Fallback for SSR/testing (no-op) |
|
|
30
|
+
|
|
31
|
+
### Auto-Detection
|
|
32
|
+
|
|
33
|
+
The `TransportRegistry` automatically selects the appropriate transport based on priority order. Higher priority transports are checked first, ensuring the most specific transport is used.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Auto-Detection (Recommended)
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { detectTransport } from '@aspectly/transports';
|
|
41
|
+
|
|
42
|
+
// Automatically detect the current environment
|
|
43
|
+
const transport = detectTransport();
|
|
44
|
+
console.log(`Using transport: ${transport.name}`); // 'cefsharp', 'react-native', 'iframe', or 'null'
|
|
45
|
+
|
|
46
|
+
// Send a message
|
|
47
|
+
transport.send(JSON.stringify({ type: 'hello', data: 'world' }));
|
|
48
|
+
|
|
49
|
+
// Subscribe to incoming messages
|
|
50
|
+
const unsubscribe = transport.subscribe((message) => {
|
|
51
|
+
const data = JSON.parse(message);
|
|
52
|
+
console.log('Received:', data);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Cleanup when done
|
|
56
|
+
unsubscribe();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Direct Transport Usage
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { CefSharpTransport } from '@aspectly/transports';
|
|
63
|
+
|
|
64
|
+
const transport = new CefSharpTransport();
|
|
65
|
+
|
|
66
|
+
if (transport.isAvailable()) {
|
|
67
|
+
transport.send(JSON.stringify({ method: 'getData' }));
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Custom Transport Registration
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { registerTransport, detectTransport } from '@aspectly/transports';
|
|
75
|
+
|
|
76
|
+
// Register a custom transport with high priority
|
|
77
|
+
registerTransport({
|
|
78
|
+
name: 'electron',
|
|
79
|
+
priority: 150, // Higher than CefSharp (100)
|
|
80
|
+
detect: () => {
|
|
81
|
+
return typeof window !== 'undefined' &&
|
|
82
|
+
window.electron?.send !== undefined;
|
|
83
|
+
},
|
|
84
|
+
createTransport: () => new ElectronTransport(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Now auto-detection will check your custom transport first
|
|
88
|
+
const transport = detectTransport();
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### Transport Interface
|
|
94
|
+
|
|
95
|
+
The core interface implemented by all transports:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface Transport {
|
|
99
|
+
readonly name: string;
|
|
100
|
+
isAvailable(): boolean;
|
|
101
|
+
send(message: string): void;
|
|
102
|
+
subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Properties:**
|
|
107
|
+
- `name` - Unique identifier for the transport (e.g., 'cefsharp', 'react-native')
|
|
108
|
+
|
|
109
|
+
**Methods:**
|
|
110
|
+
- `isAvailable()` - Check if the transport is available in the current environment
|
|
111
|
+
- `send(message: string)` - Send a message to the parent/host context
|
|
112
|
+
- `subscribe(listener)` - Subscribe to incoming messages, returns cleanup function
|
|
113
|
+
|
|
114
|
+
### Types
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// Message listener callback
|
|
118
|
+
type TransportListener = (message: string) => void;
|
|
119
|
+
|
|
120
|
+
// Cleanup function returned by subscribe
|
|
121
|
+
type TransportUnsubscribe = () => void;
|
|
122
|
+
|
|
123
|
+
// Factory function for creating transports
|
|
124
|
+
type TransportFactory = () => Transport;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### TransportDetector Interface
|
|
128
|
+
|
|
129
|
+
Used for registering custom transports:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface TransportDetector {
|
|
133
|
+
readonly name: string;
|
|
134
|
+
readonly priority: number;
|
|
135
|
+
detect(): boolean;
|
|
136
|
+
createTransport(): Transport;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Properties:**
|
|
141
|
+
- `name` - Transport identifier
|
|
142
|
+
- `priority` - Detection priority (higher values checked first)
|
|
143
|
+
|
|
144
|
+
**Methods:**
|
|
145
|
+
- `detect()` - Return `true` if this transport is available
|
|
146
|
+
- `createTransport()` - Create and return a transport instance
|
|
147
|
+
|
|
148
|
+
### BaseTransport Class
|
|
149
|
+
|
|
150
|
+
Abstract base class providing common functionality:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
abstract class BaseTransport implements Transport {
|
|
154
|
+
abstract readonly name: string;
|
|
155
|
+
abstract isAvailable(): boolean;
|
|
156
|
+
abstract send(message: string): void;
|
|
157
|
+
abstract subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
158
|
+
|
|
159
|
+
protected hasWindow(): boolean;
|
|
160
|
+
protected getWindow(): Window | null;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Protected Helpers:**
|
|
165
|
+
- `hasWindow()` - Check if `window` is defined (SSR-safe)
|
|
166
|
+
- `getWindow()` - Safely get the `window` object or `null`
|
|
167
|
+
|
|
168
|
+
### TransportRegistry
|
|
169
|
+
|
|
170
|
+
Global registry for managing transport detectors:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
class TransportRegistry {
|
|
174
|
+
register(detector: TransportDetector): void;
|
|
175
|
+
unregister(name: string): void;
|
|
176
|
+
getDetectors(): readonly TransportDetector[];
|
|
177
|
+
detect(forceRedetect?: boolean): Transport;
|
|
178
|
+
clearCache(): void;
|
|
179
|
+
reset(): void;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Methods:**
|
|
184
|
+
- `register(detector)` - Register a custom transport detector
|
|
185
|
+
- `unregister(name)` - Remove a detector by name
|
|
186
|
+
- `getDetectors()` - Get all registered detectors
|
|
187
|
+
- `detect(forceRedetect?)` - Detect and return the appropriate transport (cached)
|
|
188
|
+
- `clearCache()` - Clear the cached transport
|
|
189
|
+
- `reset()` - Reset to default state (built-in detectors only)
|
|
190
|
+
|
|
191
|
+
### Convenience Functions
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Detect the current transport
|
|
195
|
+
function detectTransport(forceRedetect?: boolean): Transport;
|
|
196
|
+
|
|
197
|
+
// Register a custom detector
|
|
198
|
+
function registerTransport(detector: TransportDetector): void;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Built-in Transports
|
|
202
|
+
|
|
203
|
+
### CefSharpTransport
|
|
204
|
+
|
|
205
|
+
For desktop applications using [CefSharp](https://cefsharp.github.io/) (Chromium Embedded Framework for .NET).
|
|
206
|
+
|
|
207
|
+
**Detection:** Checks for `window.CefSharp.PostMessage`
|
|
208
|
+
**Priority:** 100 (highest)
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { CefSharpTransport } from '@aspectly/transports';
|
|
212
|
+
|
|
213
|
+
const transport = new CefSharpTransport();
|
|
214
|
+
transport.send(JSON.stringify({ action: 'openFile' }));
|
|
215
|
+
|
|
216
|
+
const unsubscribe = transport.subscribe((message) => {
|
|
217
|
+
console.log('From C#:', message);
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Messaging:**
|
|
222
|
+
- Sends messages via `window.CefSharp.PostMessage(message)`
|
|
223
|
+
- Receives messages via `window.postMessage` events
|
|
224
|
+
|
|
225
|
+
### ReactNativeTransport
|
|
226
|
+
|
|
227
|
+
For web content running inside [React Native WebView](https://github.com/react-native-webview/react-native-webview).
|
|
228
|
+
|
|
229
|
+
**Detection:** Checks for `window.ReactNativeWebView.postMessage`
|
|
230
|
+
**Priority:** 90
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { ReactNativeTransport } from '@aspectly/transports';
|
|
234
|
+
|
|
235
|
+
const transport = new ReactNativeTransport();
|
|
236
|
+
transport.send(JSON.stringify({ action: 'navigate' }));
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Messaging:**
|
|
240
|
+
- Sends messages via `window.ReactNativeWebView.postMessage(message)`
|
|
241
|
+
- Messages are wrapped in quotes for iOS compatibility: `'${message}'`
|
|
242
|
+
- Receives messages via `window.postMessage` events
|
|
243
|
+
|
|
244
|
+
### IframeTransport
|
|
245
|
+
|
|
246
|
+
For web content running inside an iframe.
|
|
247
|
+
|
|
248
|
+
**Detection:** Checks if `window.parent !== window`
|
|
249
|
+
**Priority:** 80
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { IframeTransport } from '@aspectly/transports';
|
|
253
|
+
|
|
254
|
+
// Default: sends to any origin ('*')
|
|
255
|
+
const transport = new IframeTransport();
|
|
256
|
+
|
|
257
|
+
// Specify target origin for security
|
|
258
|
+
const secureTransport = new IframeTransport('https://parent-domain.com');
|
|
259
|
+
|
|
260
|
+
transport.send(JSON.stringify({ type: 'ready' }));
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Constructor:**
|
|
264
|
+
- `new IframeTransport(targetOrigin?: string)` - Default: `'*'`
|
|
265
|
+
|
|
266
|
+
**Messaging:**
|
|
267
|
+
- Sends messages via `window.parent.postMessage(message, targetOrigin)`
|
|
268
|
+
- Receives messages via `window.postMessage` events
|
|
269
|
+
|
|
270
|
+
### NullTransport
|
|
271
|
+
|
|
272
|
+
Fallback transport that performs no operations. Used when no other transport is available (e.g., SSR, testing).
|
|
273
|
+
|
|
274
|
+
**Priority:** Always available (fallback)
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { NullTransport } from '@aspectly/transports';
|
|
278
|
+
|
|
279
|
+
const transport = new NullTransport();
|
|
280
|
+
transport.isAvailable(); // true
|
|
281
|
+
transport.send('message'); // No-op (logs warning in development)
|
|
282
|
+
transport.subscribe(() => {}); // Returns no-op cleanup function
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Behavior:**
|
|
286
|
+
- `isAvailable()` always returns `true`
|
|
287
|
+
- `send()` is a no-op (logs warning in development mode)
|
|
288
|
+
- `subscribe()` returns a no-op cleanup function
|
|
289
|
+
|
|
290
|
+
## Creating Custom Transports
|
|
291
|
+
|
|
292
|
+
### Example: Electron Transport
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { BaseTransport, registerTransport } from '@aspectly/transports';
|
|
296
|
+
import type { TransportListener, TransportUnsubscribe } from '@aspectly/transports';
|
|
297
|
+
|
|
298
|
+
// Extend BaseTransport for helper methods
|
|
299
|
+
class ElectronTransport extends BaseTransport {
|
|
300
|
+
readonly name = 'electron';
|
|
301
|
+
|
|
302
|
+
isAvailable(): boolean {
|
|
303
|
+
const win = this.getWindow();
|
|
304
|
+
return typeof win?.electron?.send === 'function';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
send(message: string): void {
|
|
308
|
+
const win = this.getWindow();
|
|
309
|
+
if (!win?.electron?.send) {
|
|
310
|
+
console.warn('[ElectronTransport] electron.send not available');
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
win.electron.send('message', message);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
subscribe(listener: TransportListener): TransportUnsubscribe {
|
|
317
|
+
const win = this.getWindow();
|
|
318
|
+
if (!win?.electron?.on) {
|
|
319
|
+
return () => {};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const handler = (_event: any, message: string) => {
|
|
323
|
+
listener(message);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
win.electron.on('message', handler);
|
|
327
|
+
return () => {
|
|
328
|
+
win.electron?.removeListener('message', handler);
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Register with high priority
|
|
334
|
+
registerTransport({
|
|
335
|
+
name: 'electron',
|
|
336
|
+
priority: 150,
|
|
337
|
+
detect: () => {
|
|
338
|
+
return typeof window !== 'undefined' &&
|
|
339
|
+
window.electron?.send !== undefined;
|
|
340
|
+
},
|
|
341
|
+
createTransport: () => new ElectronTransport(),
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Implementation Checklist
|
|
346
|
+
|
|
347
|
+
When creating a custom transport:
|
|
348
|
+
|
|
349
|
+
1. ✓ Extend `BaseTransport` or implement `Transport` interface
|
|
350
|
+
2. ✓ Provide unique `name` property
|
|
351
|
+
3. ✓ Implement `isAvailable()` to check environment
|
|
352
|
+
4. ✓ Implement `send()` to send messages
|
|
353
|
+
5. ✓ Implement `subscribe()` to receive messages
|
|
354
|
+
6. ✓ Return cleanup function from `subscribe()`
|
|
355
|
+
7. ✓ Handle SSR/missing globals safely
|
|
356
|
+
8. ✓ Create a `TransportDetector` for registration
|
|
357
|
+
9. ✓ Set appropriate `priority` (higher = checked first)
|
|
358
|
+
|
|
359
|
+
## Related Packages
|
|
360
|
+
|
|
361
|
+
- [`@aspectly/core`](../core) - Core bridge protocol and message handling
|
|
362
|
+
- [`@aspectly/web`](../web) - Web/iframe integration with React hooks
|
|
363
|
+
- [`@aspectly/react-native`](../react-native) - React Native WebView integration
|
|
364
|
+
- [`@aspectly/react-native-web`](../react-native-web) - Universal React Native Web support
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
MIT
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message listener callback type
|
|
3
|
+
*/
|
|
4
|
+
type TransportListener = (message: string) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Cleanup function returned by subscribe
|
|
7
|
+
*/
|
|
8
|
+
type TransportUnsubscribe = () => void;
|
|
9
|
+
/**
|
|
10
|
+
* Transport interface - defines how messages are sent and received
|
|
11
|
+
* between different environments (CefSharp, React Native, iframe, etc.)
|
|
12
|
+
*/
|
|
13
|
+
interface Transport {
|
|
14
|
+
/**
|
|
15
|
+
* Unique name of the transport (e.g., 'cefsharp', 'react-native', 'iframe')
|
|
16
|
+
*/
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if this transport is available in the current environment
|
|
20
|
+
*/
|
|
21
|
+
isAvailable(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Send a message to the parent/host context
|
|
24
|
+
* @param message Serialized message string (usually JSON)
|
|
25
|
+
*/
|
|
26
|
+
send(message: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to incoming messages from the parent/host context
|
|
29
|
+
* @param listener Callback function for incoming messages
|
|
30
|
+
* @returns Cleanup function to unsubscribe
|
|
31
|
+
*/
|
|
32
|
+
subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Transport factory function type
|
|
36
|
+
*/
|
|
37
|
+
type TransportFactory = () => Transport;
|
|
38
|
+
/**
|
|
39
|
+
* Transport detector - used for auto-detection
|
|
40
|
+
*/
|
|
41
|
+
interface TransportDetector {
|
|
42
|
+
/**
|
|
43
|
+
* Name of the transport this detector is for
|
|
44
|
+
*/
|
|
45
|
+
readonly name: string;
|
|
46
|
+
/**
|
|
47
|
+
* Priority for detection order (higher = checked first)
|
|
48
|
+
* Default priorities: CefSharp=100, ReactNative=90, Iframe=80
|
|
49
|
+
*/
|
|
50
|
+
readonly priority: number;
|
|
51
|
+
/**
|
|
52
|
+
* Check if this transport is available
|
|
53
|
+
*/
|
|
54
|
+
detect(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Create the transport instance
|
|
57
|
+
*/
|
|
58
|
+
createTransport(): Transport;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Abstract base class for transports
|
|
63
|
+
* Provides common functionality for message handling
|
|
64
|
+
*/
|
|
65
|
+
declare abstract class BaseTransport implements Transport {
|
|
66
|
+
abstract readonly name: string;
|
|
67
|
+
abstract isAvailable(): boolean;
|
|
68
|
+
abstract send(message: string): void;
|
|
69
|
+
abstract subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
70
|
+
/**
|
|
71
|
+
* Helper to check if window is defined (for SSR safety)
|
|
72
|
+
*/
|
|
73
|
+
protected hasWindow(): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Helper to safely get window object
|
|
76
|
+
*/
|
|
77
|
+
protected getWindow(): Window | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { BaseTransport as B, type TransportListener as T, type TransportUnsubscribe as a, type TransportDetector as b, type Transport as c, type TransportFactory as d };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message listener callback type
|
|
3
|
+
*/
|
|
4
|
+
type TransportListener = (message: string) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Cleanup function returned by subscribe
|
|
7
|
+
*/
|
|
8
|
+
type TransportUnsubscribe = () => void;
|
|
9
|
+
/**
|
|
10
|
+
* Transport interface - defines how messages are sent and received
|
|
11
|
+
* between different environments (CefSharp, React Native, iframe, etc.)
|
|
12
|
+
*/
|
|
13
|
+
interface Transport {
|
|
14
|
+
/**
|
|
15
|
+
* Unique name of the transport (e.g., 'cefsharp', 'react-native', 'iframe')
|
|
16
|
+
*/
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if this transport is available in the current environment
|
|
20
|
+
*/
|
|
21
|
+
isAvailable(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Send a message to the parent/host context
|
|
24
|
+
* @param message Serialized message string (usually JSON)
|
|
25
|
+
*/
|
|
26
|
+
send(message: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to incoming messages from the parent/host context
|
|
29
|
+
* @param listener Callback function for incoming messages
|
|
30
|
+
* @returns Cleanup function to unsubscribe
|
|
31
|
+
*/
|
|
32
|
+
subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Transport factory function type
|
|
36
|
+
*/
|
|
37
|
+
type TransportFactory = () => Transport;
|
|
38
|
+
/**
|
|
39
|
+
* Transport detector - used for auto-detection
|
|
40
|
+
*/
|
|
41
|
+
interface TransportDetector {
|
|
42
|
+
/**
|
|
43
|
+
* Name of the transport this detector is for
|
|
44
|
+
*/
|
|
45
|
+
readonly name: string;
|
|
46
|
+
/**
|
|
47
|
+
* Priority for detection order (higher = checked first)
|
|
48
|
+
* Default priorities: CefSharp=100, ReactNative=90, Iframe=80
|
|
49
|
+
*/
|
|
50
|
+
readonly priority: number;
|
|
51
|
+
/**
|
|
52
|
+
* Check if this transport is available
|
|
53
|
+
*/
|
|
54
|
+
detect(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Create the transport instance
|
|
57
|
+
*/
|
|
58
|
+
createTransport(): Transport;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Abstract base class for transports
|
|
63
|
+
* Provides common functionality for message handling
|
|
64
|
+
*/
|
|
65
|
+
declare abstract class BaseTransport implements Transport {
|
|
66
|
+
abstract readonly name: string;
|
|
67
|
+
abstract isAvailable(): boolean;
|
|
68
|
+
abstract send(message: string): void;
|
|
69
|
+
abstract subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
70
|
+
/**
|
|
71
|
+
* Helper to check if window is defined (for SSR safety)
|
|
72
|
+
*/
|
|
73
|
+
protected hasWindow(): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Helper to safely get window object
|
|
76
|
+
*/
|
|
77
|
+
protected getWindow(): Window | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { BaseTransport as B, type TransportListener as T, type TransportUnsubscribe as a, type TransportDetector as b, type Transport as c, type TransportFactory as d };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { B as BaseTransport, T as TransportListener, a as TransportUnsubscribe, b as TransportDetector } from './BaseTransport-CxzIr1Ds.mjs';
|
|
2
|
+
export { c as Transport } from './BaseTransport-CxzIr1Ds.mjs';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
CefSharp?: {
|
|
7
|
+
PostMessage: (message: string) => void;
|
|
8
|
+
BindObjectAsync: (...args: unknown[]) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Transport for CefSharp (Chromium Embedded Framework for .NET)
|
|
14
|
+
* Used in desktop applications with embedded Chromium browsers
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { CefSharpTransport } from '@aspectly/transports/cefsharp';
|
|
19
|
+
*
|
|
20
|
+
* const transport = new CefSharpTransport();
|
|
21
|
+
* if (transport.isAvailable()) {
|
|
22
|
+
* transport.send(JSON.stringify({ type: 'hello' }));
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class CefSharpTransport extends BaseTransport {
|
|
27
|
+
readonly name = "cefsharp";
|
|
28
|
+
isAvailable(): boolean;
|
|
29
|
+
send(message: string): void;
|
|
30
|
+
subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detector for auto-detection registry
|
|
34
|
+
*/
|
|
35
|
+
declare const cefSharpDetector: TransportDetector;
|
|
36
|
+
|
|
37
|
+
export { CefSharpTransport, TransportListener, TransportUnsubscribe, cefSharpDetector };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { B as BaseTransport, T as TransportListener, a as TransportUnsubscribe, b as TransportDetector } from './BaseTransport-CxzIr1Ds.js';
|
|
2
|
+
export { c as Transport } from './BaseTransport-CxzIr1Ds.js';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
CefSharp?: {
|
|
7
|
+
PostMessage: (message: string) => void;
|
|
8
|
+
BindObjectAsync: (...args: unknown[]) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Transport for CefSharp (Chromium Embedded Framework for .NET)
|
|
14
|
+
* Used in desktop applications with embedded Chromium browsers
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { CefSharpTransport } from '@aspectly/transports/cefsharp';
|
|
19
|
+
*
|
|
20
|
+
* const transport = new CefSharpTransport();
|
|
21
|
+
* if (transport.isAvailable()) {
|
|
22
|
+
* transport.send(JSON.stringify({ type: 'hello' }));
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class CefSharpTransport extends BaseTransport {
|
|
27
|
+
readonly name = "cefsharp";
|
|
28
|
+
isAvailable(): boolean;
|
|
29
|
+
send(message: string): void;
|
|
30
|
+
subscribe(listener: TransportListener): TransportUnsubscribe;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detector for auto-detection registry
|
|
34
|
+
*/
|
|
35
|
+
declare const cefSharpDetector: TransportDetector;
|
|
36
|
+
|
|
37
|
+
export { CefSharpTransport, TransportListener, TransportUnsubscribe, cefSharpDetector };
|
package/dist/cefsharp.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/BaseTransport.ts
|
|
4
|
+
var BaseTransport = class {
|
|
5
|
+
/**
|
|
6
|
+
* Helper to check if window is defined (for SSR safety)
|
|
7
|
+
*/
|
|
8
|
+
hasWindow() {
|
|
9
|
+
return typeof window !== "undefined";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Helper to safely get window object
|
|
13
|
+
*/
|
|
14
|
+
getWindow() {
|
|
15
|
+
return this.hasWindow() ? window : null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/transports/CefSharpTransport.ts
|
|
20
|
+
var CefSharpTransport = class extends BaseTransport {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.name = "cefsharp";
|
|
24
|
+
}
|
|
25
|
+
isAvailable() {
|
|
26
|
+
const win = this.getWindow();
|
|
27
|
+
return typeof win?.CefSharp?.PostMessage === "function";
|
|
28
|
+
}
|
|
29
|
+
send(message) {
|
|
30
|
+
const win = this.getWindow();
|
|
31
|
+
if (!win?.CefSharp?.PostMessage) {
|
|
32
|
+
console.warn("[CefSharpTransport] CefSharp.PostMessage is not available");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
win.CefSharp.PostMessage(message);
|
|
36
|
+
}
|
|
37
|
+
subscribe(listener) {
|
|
38
|
+
const win = this.getWindow();
|
|
39
|
+
if (!win) {
|
|
40
|
+
return () => {
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const handler = (event) => {
|
|
44
|
+
if (typeof event.data === "string") {
|
|
45
|
+
listener(event.data);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
win.addEventListener("message", handler);
|
|
49
|
+
return () => win.removeEventListener("message", handler);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var cefSharpDetector = {
|
|
53
|
+
name: "cefsharp",
|
|
54
|
+
priority: 100,
|
|
55
|
+
// Highest priority - check first
|
|
56
|
+
detect: () => {
|
|
57
|
+
return typeof window !== "undefined" && typeof window.CefSharp?.PostMessage === "function";
|
|
58
|
+
},
|
|
59
|
+
createTransport: () => new CefSharpTransport()
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
exports.CefSharpTransport = CefSharpTransport;
|
|
63
|
+
exports.cefSharpDetector = cefSharpDetector;
|
|
64
|
+
//# sourceMappingURL=cefsharp.js.map
|
|
65
|
+
//# sourceMappingURL=cefsharp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/BaseTransport.ts","../src/transports/CefSharpTransport.ts"],"names":[],"mappings":";;;AAMO,IAAe,gBAAf,MAAkD;AAAA;AAAA;AAAA;AAAA,EAU7C,SAAA,GAAqB;AAC7B,IAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKU,SAAA,GAA2B;AACnC,IAAA,OAAO,IAAA,CAAK,SAAA,EAAU,GAAI,MAAA,GAAS,IAAA;AAAA,EACrC;AACF,CAAA;;;ACAO,IAAM,iBAAA,GAAN,cAAgC,aAAA,CAAc;AAAA,EAA9C,WAAA,GAAA;AAAA,IAAA,KAAA,CAAA,GAAA,SAAA,CAAA;AACL,IAAA,IAAA,CAAS,IAAA,GAAO,UAAA;AAAA,EAAA;AAAA,EAEhB,WAAA,GAAuB;AACrB,IAAA,MAAM,GAAA,GAAM,KAAK,SAAA,EAAU;AAC3B,IAAA,OAAO,OAAO,GAAA,EAAK,QAAA,EAAU,WAAA,KAAgB,UAAA;AAAA,EAC/C;AAAA,EAEA,KAAK,OAAA,EAAuB;AAC1B,IAAA,MAAM,GAAA,GAAM,KAAK,SAAA,EAAU;AAC3B,IAAA,IAAI,CAAC,GAAA,EAAK,QAAA,EAAU,WAAA,EAAa;AAC/B,MAAA,OAAA,CAAQ,KAAK,2DAA2D,CAAA;AACxE,MAAA;AAAA,IACF;AACA,IAAA,GAAA,CAAI,QAAA,CAAS,YAAY,OAAO,CAAA;AAAA,EAClC;AAAA,EAEA,UAAU,QAAA,EAAmD;AAC3D,IAAA,MAAM,GAAA,GAAM,KAAK,SAAA,EAAU;AAC3B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAGA,IAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAA8B;AAC7C,MAAA,IAAI,OAAO,KAAA,CAAM,IAAA,KAAS,QAAA,EAAU;AAClC,QAAA,QAAA,CAAS,MAAM,IAAI,CAAA;AAAA,MACrB;AAAA,IACF,CAAA;AAEA,IAAA,GAAA,CAAI,gBAAA,CAAiB,WAAW,OAAO,CAAA;AACvC,IAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,EACzD;AACF;AAKO,IAAM,gBAAA,GAAsC;AAAA,EACjD,IAAA,EAAM,UAAA;AAAA,EACN,QAAA,EAAU,GAAA;AAAA;AAAA,EACV,QAAQ,MAAM;AACZ,IAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAClB,OAAO,MAAA,CAAO,UAAU,WAAA,KAAgB,UAAA;AAAA,EACjD,CAAA;AAAA,EACA,eAAA,EAAiB,MAAM,IAAI,iBAAA;AAC7B","file":"cefsharp.js","sourcesContent":["import type { Transport, TransportListener, TransportUnsubscribe } from './types';\r\n\r\n/**\r\n * Abstract base class for transports\r\n * Provides common functionality for message handling\r\n */\r\nexport abstract class BaseTransport implements Transport {\r\n abstract readonly name: string;\r\n\r\n abstract isAvailable(): boolean;\r\n abstract send(message: string): void;\r\n abstract subscribe(listener: TransportListener): TransportUnsubscribe;\r\n\r\n /**\r\n * Helper to check if window is defined (for SSR safety)\r\n */\r\n protected hasWindow(): boolean {\r\n return typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Helper to safely get window object\r\n */\r\n protected getWindow(): Window | null {\r\n return this.hasWindow() ? window : null;\r\n }\r\n}\r\n","import { BaseTransport } from '../BaseTransport';\r\nimport type { TransportListener, TransportUnsubscribe, TransportDetector } from '../types';\r\n\r\ndeclare global {\r\n interface Window {\r\n CefSharp?: {\r\n PostMessage: (message: string) => void;\r\n BindObjectAsync: (...args: unknown[]) => Promise<void>;\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Transport for CefSharp (Chromium Embedded Framework for .NET)\r\n * Used in desktop applications with embedded Chromium browsers\r\n *\r\n * @example\r\n * ```typescript\r\n * import { CefSharpTransport } from '@aspectly/transports/cefsharp';\r\n *\r\n * const transport = new CefSharpTransport();\r\n * if (transport.isAvailable()) {\r\n * transport.send(JSON.stringify({ type: 'hello' }));\r\n * }\r\n * ```\r\n */\r\nexport class CefSharpTransport extends BaseTransport {\r\n readonly name = 'cefsharp';\r\n\r\n isAvailable(): boolean {\r\n const win = this.getWindow();\r\n return typeof win?.CefSharp?.PostMessage === 'function';\r\n }\r\n\r\n send(message: string): void {\r\n const win = this.getWindow();\r\n if (!win?.CefSharp?.PostMessage) {\r\n console.warn('[CefSharpTransport] CefSharp.PostMessage is not available');\r\n return;\r\n }\r\n win.CefSharp.PostMessage(message);\r\n }\r\n\r\n subscribe(listener: TransportListener): TransportUnsubscribe {\r\n const win = this.getWindow();\r\n if (!win) {\r\n return () => {};\r\n }\r\n\r\n // CefSharp sends messages via window.postMessage\r\n const handler = (event: MessageEvent): void => {\r\n if (typeof event.data === 'string') {\r\n listener(event.data);\r\n }\r\n };\r\n\r\n win.addEventListener('message', handler);\r\n return () => win.removeEventListener('message', handler);\r\n }\r\n}\r\n\r\n/**\r\n * Detector for auto-detection registry\r\n */\r\nexport const cefSharpDetector: TransportDetector = {\r\n name: 'cefsharp',\r\n priority: 100, // Highest priority - check first\r\n detect: () => {\r\n return typeof window !== 'undefined' &&\r\n typeof window.CefSharp?.PostMessage === 'function';\r\n },\r\n createTransport: () => new CefSharpTransport(),\r\n};\r\n"]}
|