@bobfrankston/msgapidefs 0.1.24 → 0.1.25
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/UDP-PLAN.md +499 -0
- package/msgapi-plan.md +209 -0
- package/package.json +1 -1
package/UDP-PLAN.md
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
# UDP Support Plan for msgapi
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Add UDP networking capabilities to msgapi, enabling lightweight, connectionless communication for:
|
|
6
|
+
- Local device control and discovery
|
|
7
|
+
- IoT messaging
|
|
8
|
+
- Low-latency event broadcasting
|
|
9
|
+
- Inter-process communication
|
|
10
|
+
- Home automation integration
|
|
11
|
+
|
|
12
|
+
**Target Use Case**: Trusted, friendly applications controlled by the user. This is for power users running their own apps, not for sandboxed web content.
|
|
13
|
+
|
|
14
|
+
**Reference Implementation**: `y:\dev\homecontrol\utils\netsupport`
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## API Design
|
|
19
|
+
|
|
20
|
+
### msgapi.udp Object
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
interface UdpSocket {
|
|
24
|
+
/** Local port this socket is bound to */
|
|
25
|
+
readonly port: number;
|
|
26
|
+
|
|
27
|
+
/** Local address this socket is bound to */
|
|
28
|
+
readonly address: string;
|
|
29
|
+
|
|
30
|
+
/** Send a UDP message */
|
|
31
|
+
send(message: string | Uint8Array, port: number, address?: string): Promise<void>;
|
|
32
|
+
|
|
33
|
+
/** Register a message handler (non-exclusive, multiple handlers allowed) */
|
|
34
|
+
onMessage(handler: (message: UdpMessage) => void): void;
|
|
35
|
+
|
|
36
|
+
/** Remove a message handler */
|
|
37
|
+
offMessage(handler: (message: UdpMessage) => void): void;
|
|
38
|
+
|
|
39
|
+
/** Close the socket */
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface UdpMessage {
|
|
44
|
+
/** Message content as string */
|
|
45
|
+
data: string;
|
|
46
|
+
|
|
47
|
+
/** Raw message content as Uint8Array */
|
|
48
|
+
buffer: Uint8Array;
|
|
49
|
+
|
|
50
|
+
/** Sender's address */
|
|
51
|
+
remoteAddress: string;
|
|
52
|
+
|
|
53
|
+
/** Sender's port */
|
|
54
|
+
remotePort: number;
|
|
55
|
+
|
|
56
|
+
/** Size of message in bytes */
|
|
57
|
+
size: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface UdpOptions {
|
|
61
|
+
/** Port to bind to (0 = random available port) */
|
|
62
|
+
port?: number;
|
|
63
|
+
|
|
64
|
+
/** Address to bind to (default: '0.0.0.0' for all interfaces) */
|
|
65
|
+
address?: string;
|
|
66
|
+
|
|
67
|
+
/** Enable SO_REUSEADDR (allows multiple sockets on same port) */
|
|
68
|
+
reuseAddress?: boolean;
|
|
69
|
+
|
|
70
|
+
/** Enable SO_BROADCAST for broadcast messages */
|
|
71
|
+
broadcast?: boolean;
|
|
72
|
+
|
|
73
|
+
/** Join multicast group(s) */
|
|
74
|
+
multicast?: string | string[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface MsgAPI {
|
|
78
|
+
// ... existing properties ...
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* UDP networking operations namespace
|
|
82
|
+
* Requires -allowUdp flag when launching msgview/msger
|
|
83
|
+
*/
|
|
84
|
+
udp?: {
|
|
85
|
+
/**
|
|
86
|
+
* Create a UDP socket
|
|
87
|
+
* @param options - Socket configuration options
|
|
88
|
+
* @returns UDP socket instance
|
|
89
|
+
* @example
|
|
90
|
+
* // Simple receiver on port 8080
|
|
91
|
+
* const socket = await msgapi.udp.create({ port: 8080 });
|
|
92
|
+
* socket.onMessage(msg => {
|
|
93
|
+
* console.log(`Received from ${msg.remoteAddress}:${msg.remotePort}: ${msg.data}`);
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* // Broadcast sender with reuse address
|
|
98
|
+
* const socket = await msgapi.udp.create({
|
|
99
|
+
* port: 0, // Random port
|
|
100
|
+
* broadcast: true,
|
|
101
|
+
* reuseAddress: true
|
|
102
|
+
* });
|
|
103
|
+
* await socket.send('Hello Network!', 8080, '255.255.255.255');
|
|
104
|
+
*/
|
|
105
|
+
create(options?: UdpOptions): Promise<UdpSocket>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create a multicast receiver
|
|
109
|
+
* Convenience method for multicast scenarios
|
|
110
|
+
* @param multicastGroup - Multicast group address (e.g., '239.255.255.250')
|
|
111
|
+
* @param port - Port to listen on
|
|
112
|
+
* @returns UDP socket joined to multicast group
|
|
113
|
+
* @example
|
|
114
|
+
* const socket = await msgapi.udp.createMulticast('239.255.0.1', 5353);
|
|
115
|
+
* socket.onMessage(msg => console.log('Multicast:', msg.data));
|
|
116
|
+
*/
|
|
117
|
+
createMulticast(multicastGroup: string, port: number): Promise<UdpSocket>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Send a one-off UDP message without creating a persistent socket
|
|
121
|
+
* @param message - Message to send
|
|
122
|
+
* @param port - Destination port
|
|
123
|
+
* @param address - Destination address (default: 'localhost')
|
|
124
|
+
* @example
|
|
125
|
+
* await msgapi.udp.send('Quick message', 8080, '192.168.1.100');
|
|
126
|
+
*/
|
|
127
|
+
send(message: string | Uint8Array, port: number, address?: string): Promise<void>;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Implementation Requirements
|
|
135
|
+
|
|
136
|
+
### Permission/Security Model
|
|
137
|
+
|
|
138
|
+
UDP requires explicit permission, similar to `allowFs`:
|
|
139
|
+
|
|
140
|
+
**Command-line flag:**
|
|
141
|
+
- **msgview**: `-allowUdp`
|
|
142
|
+
- **msger**: `-allowUdp`
|
|
143
|
+
|
|
144
|
+
**Config file (recommended for friendly apps):**
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"url": "http://localhost:8080/myapp",
|
|
148
|
+
"flags": {
|
|
149
|
+
"allowFs": true,
|
|
150
|
+
"allowUdp": true
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Rationale**: This is for trusted applications the user controls. Like `allowFs`, `allowUdp` gives power to friendly apps that need it. The user explicitly enables it in their config file.
|
|
156
|
+
|
|
157
|
+
**Philosophy**: We're not sandboxing untrusted web content - we're empowering user-controlled applications with full system capabilities when requested.
|
|
158
|
+
|
|
159
|
+
### Non-Exclusive Listening
|
|
160
|
+
|
|
161
|
+
Multiple handlers can listen to the same port:
|
|
162
|
+
- `reuseAddress: true` enables SO_REUSEADDR socket option
|
|
163
|
+
- Multiple `onMessage()` handlers can be registered on same socket
|
|
164
|
+
- Useful for multiple windows/instances receiving same broadcasts
|
|
165
|
+
|
|
166
|
+
### Platform-Specific Notes
|
|
167
|
+
|
|
168
|
+
#### Node.js/msgview (Electron)
|
|
169
|
+
- Use Node.js `dgram` module
|
|
170
|
+
- Full UDP support including multicast
|
|
171
|
+
- Works on Windows, Linux, macOS
|
|
172
|
+
|
|
173
|
+
#### Rust/msger (wry)
|
|
174
|
+
- Use Rust `tokio::net::UdpSocket`
|
|
175
|
+
- Full platform support
|
|
176
|
+
- Async message handling via Tokio runtime
|
|
177
|
+
|
|
178
|
+
#### C#/msga (.NET MAUI)
|
|
179
|
+
- Use `System.Net.Sockets.UdpClient`
|
|
180
|
+
- Platform: Windows, Android (iOS restrictions may apply)
|
|
181
|
+
- iOS: UDP requires special entitlements, may be restricted
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Usage Examples
|
|
186
|
+
|
|
187
|
+
### Example 1: Simple Sender/Receiver
|
|
188
|
+
|
|
189
|
+
**Sender:**
|
|
190
|
+
```javascript
|
|
191
|
+
const socket = await msgapi.udp.create({ port: 0 }); // Random port
|
|
192
|
+
await socket.send('Hello!', 8080, '192.168.1.100');
|
|
193
|
+
await socket.close();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Receiver:**
|
|
197
|
+
```javascript
|
|
198
|
+
const socket = await msgapi.udp.create({
|
|
199
|
+
port: 8080,
|
|
200
|
+
reuseAddress: true // Allow multiple receivers
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
socket.onMessage(msg => {
|
|
204
|
+
console.log(`From ${msg.remoteAddress}: ${msg.data}`);
|
|
205
|
+
// Echo back
|
|
206
|
+
socket.send(`Echo: ${msg.data}`, msg.remotePort, msg.remoteAddress);
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Example 2: Broadcast Discovery
|
|
211
|
+
|
|
212
|
+
**Service Announcer:**
|
|
213
|
+
```javascript
|
|
214
|
+
const socket = await msgapi.udp.create({
|
|
215
|
+
broadcast: true
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
setInterval(async () => {
|
|
219
|
+
await socket.send(
|
|
220
|
+
JSON.stringify({ service: 'MyApp', port: 9000 }),
|
|
221
|
+
8888,
|
|
222
|
+
'255.255.255.255'
|
|
223
|
+
);
|
|
224
|
+
}, 5000);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Service Discovery:**
|
|
228
|
+
```javascript
|
|
229
|
+
const socket = await msgapi.udp.create({
|
|
230
|
+
port: 8888,
|
|
231
|
+
reuseAddress: true
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
socket.onMessage(msg => {
|
|
235
|
+
const service = JSON.parse(msg.data);
|
|
236
|
+
console.log(`Found: ${service.service} at ${msg.remoteAddress}:${service.port}`);
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Example 3: Multicast Communication
|
|
241
|
+
|
|
242
|
+
**Multicast Sender:**
|
|
243
|
+
```javascript
|
|
244
|
+
const socket = await msgapi.udp.create({ broadcast: true });
|
|
245
|
+
await socket.send('Event occurred!', 5353, '239.255.0.1');
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Multicast Receiver:**
|
|
249
|
+
```javascript
|
|
250
|
+
const socket = await msgapi.udp.createMulticast('239.255.0.1', 5353);
|
|
251
|
+
socket.onMessage(msg => {
|
|
252
|
+
console.log('Multicast event:', msg.data);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Example 4: Home Automation Control
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
// Config file enables UDP:
|
|
260
|
+
// { "flags": { "allowUdp": true }, ... }
|
|
261
|
+
|
|
262
|
+
// Listen for button press events
|
|
263
|
+
const listener = await msgapi.udp.create({
|
|
264
|
+
port: 7777,
|
|
265
|
+
reuseAddress: true
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
listener.onMessage(async msg => {
|
|
269
|
+
const event = JSON.parse(msg.data);
|
|
270
|
+
|
|
271
|
+
if (event.type === 'button_press') {
|
|
272
|
+
console.log(`Button ${event.button} pressed`);
|
|
273
|
+
|
|
274
|
+
// Send command to light controller
|
|
275
|
+
const controller = await msgapi.udp.create();
|
|
276
|
+
await controller.send(
|
|
277
|
+
JSON.stringify({ command: 'toggle', device: 'light1' }),
|
|
278
|
+
8888,
|
|
279
|
+
'light-controller.local' // DNS resolution supported
|
|
280
|
+
);
|
|
281
|
+
await controller.close();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Example 5: Multiple Listeners (Non-Exclusive)
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// Window 1: Logger
|
|
290
|
+
const socket1 = await msgapi.udp.create({
|
|
291
|
+
port: 9000,
|
|
292
|
+
reuseAddress: true
|
|
293
|
+
});
|
|
294
|
+
socket1.onMessage(msg => console.log('[LOG]', msg.data));
|
|
295
|
+
|
|
296
|
+
// Window 2: Processor
|
|
297
|
+
const socket2 = await msgapi.udp.create({
|
|
298
|
+
port: 9000,
|
|
299
|
+
reuseAddress: true
|
|
300
|
+
});
|
|
301
|
+
socket2.onMessage(msg => processMessage(msg.data));
|
|
302
|
+
|
|
303
|
+
// Both receive the same messages!
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Implementation Checklist
|
|
309
|
+
|
|
310
|
+
### Phase 1: Core Implementation
|
|
311
|
+
|
|
312
|
+
#### msgapidefs (TypeScript definitions)
|
|
313
|
+
- [ ] Add `UdpSocket` interface
|
|
314
|
+
- [ ] Add `UdpMessage` interface
|
|
315
|
+
- [ ] Add `UdpOptions` interface
|
|
316
|
+
- [ ] Add `msgapi.udp` namespace to `MsgAPI` interface
|
|
317
|
+
- [ ] Update README.md with UDP examples
|
|
318
|
+
- [ ] Add to msgapi-plan.md status table
|
|
319
|
+
|
|
320
|
+
#### msgview (Electron/Node.js)
|
|
321
|
+
- [ ] Implement `dgram`-based UDP socket wrapper
|
|
322
|
+
- [ ] Add `-allowUdp` flag to CLI parser
|
|
323
|
+
- [ ] Wire up IPC handlers for UDP operations
|
|
324
|
+
- [ ] Handle socket cleanup on window close
|
|
325
|
+
- [ ] Implement message handler registration/removal
|
|
326
|
+
- [ ] Add error handling for network errors
|
|
327
|
+
- [ ] Test on Windows, Linux, macOS
|
|
328
|
+
|
|
329
|
+
#### msger (Rust/wry)
|
|
330
|
+
- [ ] Add `tokio::net::UdpSocket` wrapper
|
|
331
|
+
- [ ] Add `--allow-udp` CLI flag
|
|
332
|
+
- [ ] Implement message handler callback system
|
|
333
|
+
- [ ] Handle async message reception in Tokio runtime
|
|
334
|
+
- [ ] Wire up webview invoke handlers
|
|
335
|
+
- [ ] Implement socket cleanup on window close
|
|
336
|
+
- [ ] Test on Windows (primary target)
|
|
337
|
+
|
|
338
|
+
#### msga (C#/.NET MAUI)
|
|
339
|
+
- [ ] Implement `UdpClient` wrapper in Services/
|
|
340
|
+
- [ ] Add allowUdp config option
|
|
341
|
+
- [ ] Wire up JavaScript-to-C# bridge for UDP
|
|
342
|
+
- [ ] Handle message callbacks via WebView
|
|
343
|
+
- [ ] Test on Windows, Android
|
|
344
|
+
- [ ] Document iOS limitations
|
|
345
|
+
|
|
346
|
+
### Phase 2: Advanced Features
|
|
347
|
+
- [ ] Multicast support testing
|
|
348
|
+
- [ ] Broadcast support testing
|
|
349
|
+
- [ ] IPv6 support
|
|
350
|
+
- [ ] Socket options (TTL, buffer sizes)
|
|
351
|
+
- [ ] Rate limiting/throttling options
|
|
352
|
+
- [ ] Connection statistics/monitoring
|
|
353
|
+
|
|
354
|
+
### Phase 3: Documentation & Examples
|
|
355
|
+
- [ ] Add UDP section to main README
|
|
356
|
+
- [ ] Create UDP examples repository
|
|
357
|
+
- [ ] Document common patterns (discovery, control, etc.)
|
|
358
|
+
- [ ] Add troubleshooting guide
|
|
359
|
+
- [ ] Document firewall configuration needs
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Security Considerations
|
|
364
|
+
|
|
365
|
+
### Trust Model
|
|
366
|
+
|
|
367
|
+
**This is for friendly applications, not adversarial content.**
|
|
368
|
+
|
|
369
|
+
The user:
|
|
370
|
+
- Writes or controls the HTML/JS being loaded
|
|
371
|
+
- Explicitly enables `allowUdp` in their config file
|
|
372
|
+
- Understands they're giving the app network access
|
|
373
|
+
- Similar trust level to `allowFs` (full file system access)
|
|
374
|
+
|
|
375
|
+
### Design Principles
|
|
376
|
+
1. **Explicit opt-in**: Require `allowUdp` in config or CLI flag
|
|
377
|
+
2. **Full power when enabled**: No artificial restrictions for trusted apps
|
|
378
|
+
3. **Clear documentation**: User understands what they're enabling
|
|
379
|
+
4. **Useful error messages**: If UDP fails, explain clearly why
|
|
380
|
+
|
|
381
|
+
### Reasonable Safeguards
|
|
382
|
+
1. **Message size limits**: Respect UDP's 64KB datagram limit (natural constraint)
|
|
383
|
+
2. **Socket cleanup**: Auto-close sockets when window closes (resource management)
|
|
384
|
+
3. **Error handling**: Clear errors for invalid addresses/ports
|
|
385
|
+
|
|
386
|
+
### NOT Implemented (Trust-Based)
|
|
387
|
+
- ❌ Rate limiting (user's app, user's choice)
|
|
388
|
+
- ❌ IP restrictions (user may need any destination)
|
|
389
|
+
- ❌ Port restrictions (user may need privileged ports with sudo)
|
|
390
|
+
- ❌ DNS blocking (user should be able to resolve hostnames)
|
|
391
|
+
- ❌ Audit logging (adds overhead, user controls the app)
|
|
392
|
+
|
|
393
|
+
**Rationale**: If we trust the user with `allowFs` (full filesystem), we can trust them with UDP. Both are powerful, both require explicit enablement.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Testing Strategy
|
|
398
|
+
|
|
399
|
+
### Unit Tests
|
|
400
|
+
- Socket creation with various options
|
|
401
|
+
- Message sending/receiving
|
|
402
|
+
- Handler registration/removal
|
|
403
|
+
- Socket cleanup
|
|
404
|
+
- Error conditions
|
|
405
|
+
|
|
406
|
+
### Integration Tests
|
|
407
|
+
- msgview ↔ msgview communication
|
|
408
|
+
- msger ↔ msger communication
|
|
409
|
+
- Cross-implementation (msgview ↔ msger)
|
|
410
|
+
- Broadcast reception by multiple instances
|
|
411
|
+
- Multicast group joining
|
|
412
|
+
|
|
413
|
+
### Platform Tests
|
|
414
|
+
- Windows (primary target)
|
|
415
|
+
- Linux (msgview, msger)
|
|
416
|
+
- macOS (msgview)
|
|
417
|
+
- Android (msga)
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Reference: netsupport Comparison
|
|
422
|
+
|
|
423
|
+
Based on `y:\dev\homecontrol\utils\netsupport`:
|
|
424
|
+
|
|
425
|
+
### Key Concepts to Adopt
|
|
426
|
+
- Simple socket creation API
|
|
427
|
+
- Non-exclusive listening (SO_REUSEADDR)
|
|
428
|
+
- Clean message handler pattern
|
|
429
|
+
- Automatic cleanup on close
|
|
430
|
+
|
|
431
|
+
### Differences from netsupport
|
|
432
|
+
- **msgapi**: Integrated into window API (security via flag)
|
|
433
|
+
- **netsupport**: Standalone utility (assumed trusted)
|
|
434
|
+
- **msgapi**: Promise-based async API
|
|
435
|
+
- **netsupport**: May use callback patterns
|
|
436
|
+
- **msgapi**: Cross-platform (Electron, Rust, C#)
|
|
437
|
+
- **netsupport**: Node.js focused
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Future Enhancements
|
|
442
|
+
|
|
443
|
+
### Potential Additions
|
|
444
|
+
- **TCP support**: For reliable, connection-oriented communication
|
|
445
|
+
- **WebSocket support**: For browser-like bidirectional communication
|
|
446
|
+
- **HTTP client**: Simple fetch-like API for web requests
|
|
447
|
+
- **MQTT support**: For IoT messaging patterns
|
|
448
|
+
- **Serial port**: For hardware device communication
|
|
449
|
+
|
|
450
|
+
### Not Planned (Use Native APIs Instead)
|
|
451
|
+
- File transfers over network (use fs API + HTTP)
|
|
452
|
+
- Complex protocol implementations (use dedicated libraries)
|
|
453
|
+
- VPN/tunnel features (system-level concern)
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Open Questions
|
|
458
|
+
|
|
459
|
+
1. **DNS resolution**: Allow hostnames in send()?
|
|
460
|
+
- **Recommendation**: YES - trust the user's app, support `socket.send('msg', 8080, 'myserver.local')`
|
|
461
|
+
|
|
462
|
+
2. **Socket lifetime**: Auto-close on window close, or persist?
|
|
463
|
+
- **Recommendation**: Auto-close for resource management
|
|
464
|
+
|
|
465
|
+
3. **Error handling**: Silent fail or throw/reject?
|
|
466
|
+
- **Recommendation**: Reject promises with descriptive errors
|
|
467
|
+
|
|
468
|
+
4. **Buffer encoding**: String (UTF-8) vs raw bytes?
|
|
469
|
+
- **Recommendation**: Support both (string + Uint8Array) ✅ Already in API
|
|
470
|
+
|
|
471
|
+
5. **Privileged ports**: Allow binding to ports < 1024?
|
|
472
|
+
- **Recommendation**: YES - if user runs msgview with appropriate permissions, allow it
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Timeline Estimate
|
|
477
|
+
|
|
478
|
+
- **Phase 1 (Core)**: 2-3 weeks per implementation
|
|
479
|
+
- msgview: 1 week (Node.js dgram is straightforward)
|
|
480
|
+
- msger: 2 weeks (Tokio async + webview bridge)
|
|
481
|
+
- msga: 2 weeks (C# + bridge wiring)
|
|
482
|
+
|
|
483
|
+
- **Phase 2 (Advanced)**: 1 week per implementation
|
|
484
|
+
|
|
485
|
+
- **Phase 3 (Docs)**: 1 week overall
|
|
486
|
+
|
|
487
|
+
**Total**: 8-10 weeks for full implementation across all platforms
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Status
|
|
492
|
+
|
|
493
|
+
- [x] Planning document created
|
|
494
|
+
- [ ] msgapidefs types added
|
|
495
|
+
- [ ] msgview implementation
|
|
496
|
+
- [ ] msger implementation
|
|
497
|
+
- [ ] msga implementation
|
|
498
|
+
- [ ] Documentation complete
|
|
499
|
+
- [ ] Testing complete
|
package/msgapi-plan.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# msgapi Implementation Plan
|
|
2
|
+
|
|
3
|
+
Shared planning document for msgapi across implementations:
|
|
4
|
+
- **TypeScript**: msgapidefs (this repo) - types/definitions
|
|
5
|
+
- **TypeScript/Electron**: msgview
|
|
6
|
+
- **Rust/wry**: msger
|
|
7
|
+
- **C#/.NET MAUI**: msga (y:\dev\utils\msga)
|
|
8
|
+
|
|
9
|
+
## Current Status
|
|
10
|
+
|
|
11
|
+
### Implemented APIs
|
|
12
|
+
|
|
13
|
+
| API | msgview | msger | msga | Notes |
|
|
14
|
+
|-----|---------|-------|------|-------|
|
|
15
|
+
| **Window Control** |
|
|
16
|
+
| toggleFullscreen() | ✅ | ✅ | ⚠️ | msga: C# exists, not wired |
|
|
17
|
+
| setFullscreen(bool) | ✅ | ✅ | ⚠️ | msga: C# exists, not wired |
|
|
18
|
+
| minimize() | ✅ | ✅ | ⚠️ | msga: Windows only in C# |
|
|
19
|
+
| maximize() | ✅ | ✅ | ⚠️ | msga: Windows only in C# |
|
|
20
|
+
| setSize(w,h) | ✅ | ✅ | ⚠️ | msga: Windows only in C# |
|
|
21
|
+
| setPosition(x,y) | ✅ | ✅ | ⚠️ | msga: Windows only in C# |
|
|
22
|
+
| setAlwaysOnTop(bool) | ✅ | ✅ | ❌ | |
|
|
23
|
+
| close(result?) | ✅ | ✅ | ⚠️ | msga: basic close only |
|
|
24
|
+
| **File System** |
|
|
25
|
+
| fs.selectFile() | ✅ | ✅ | ⚠️ | msga: C# exists, not wired |
|
|
26
|
+
| fs.selectFiles() | ✅ | ✅ | ⚠️ | msga: C# exists, not wired |
|
|
27
|
+
| fs.saveFileAs() | ✅ | ✅ | ❌ | |
|
|
28
|
+
| fs.selectFolder() | ✅ | ✅ | ❌ | |
|
|
29
|
+
| fs.read(path) | ✅ | ✅ | ❌ | Requires allowFs flag |
|
|
30
|
+
| fs.write(path) | ✅ | ✅ | ❌ | Requires allowFs flag |
|
|
31
|
+
| fs.list(path) | ✅ | ✅ | ❌ | Requires allowFs flag |
|
|
32
|
+
| fs.exists(path) | ✅ | ✅ | ❌ | Requires allowFs flag |
|
|
33
|
+
| fs.delete(path) | ✅ | ✅ | ❌ | Requires allowFs flag |
|
|
34
|
+
|
|
35
|
+
Legend: ✅ Implemented | ⚠️ Partial/Not wired | ❌ Not implemented
|
|
36
|
+
|
|
37
|
+
### msga Wiring Status
|
|
38
|
+
|
|
39
|
+
The MsgApiBridge.cs file in msga has C# implementations but the JavaScript bridge is not connected:
|
|
40
|
+
- JavaScript injection exists in MainPage.xaml.cs
|
|
41
|
+
- C# handler methods exist in Services/MsgApiBridge.cs
|
|
42
|
+
- Missing: WebView JavaScript-to-C# message handler wiring
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Planned Features
|
|
47
|
+
|
|
48
|
+
### 1. UDP Communication
|
|
49
|
+
|
|
50
|
+
**Use case**: Local device control, IoT messaging, low-latency communication
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface MsgAPI {
|
|
54
|
+
udp: {
|
|
55
|
+
// Send UDP datagram
|
|
56
|
+
send(host: string, port: number, data: string | Uint8Array): Promise<void>;
|
|
57
|
+
|
|
58
|
+
// Listen for UDP datagrams on a port
|
|
59
|
+
listen(port: number, callback: (data: Uint8Array, sender: {host: string, port: number}) => void): Promise<{close: () => void}>;
|
|
60
|
+
|
|
61
|
+
// Send and wait for response (with timeout)
|
|
62
|
+
sendReceive(host: string, port: number, data: string | Uint8Array, timeoutMs?: number): Promise<Uint8Array>;
|
|
63
|
+
|
|
64
|
+
// Broadcast to subnet
|
|
65
|
+
broadcast(port: number, data: string | Uint8Array): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Implementation notes**:
|
|
71
|
+
- TypeScript: Define types only (browser cannot do UDP)
|
|
72
|
+
- Electron (msgview): Use dgram module
|
|
73
|
+
- Rust (msger): Use std::net::UdpSocket or tokio UDP
|
|
74
|
+
- C#/MAUI (msga): Use System.Net.Sockets.UdpClient
|
|
75
|
+
|
|
76
|
+
**Security considerations**:
|
|
77
|
+
- Require explicit allowUdp flag or permission prompt
|
|
78
|
+
- Consider port restrictions (no well-known ports < 1024?)
|
|
79
|
+
- Localhost-only mode option
|
|
80
|
+
|
|
81
|
+
### 2. Enhanced File System
|
|
82
|
+
|
|
83
|
+
**Use case**: Full file system access for local apps, document management
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
interface FsExtended {
|
|
87
|
+
// Already planned in msgapidefs
|
|
88
|
+
read(path: string): Promise<Uint8Array>;
|
|
89
|
+
write(path: string, content: string | Uint8Array): Promise<void>;
|
|
90
|
+
list(path: string): Promise<FsEntry[]>;
|
|
91
|
+
exists(path: string): Promise<boolean>;
|
|
92
|
+
delete(path: string): Promise<void>;
|
|
93
|
+
|
|
94
|
+
// New additions
|
|
95
|
+
mkdir(path: string, recursive?: boolean): Promise<void>;
|
|
96
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
97
|
+
copy(src: string, dest: string): Promise<void>;
|
|
98
|
+
stat(path: string): Promise<FsStat>;
|
|
99
|
+
watch(path: string, callback: (event: FsWatchEvent) => void): Promise<{close: () => void}>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface FsEntry {
|
|
103
|
+
name: string;
|
|
104
|
+
path: string;
|
|
105
|
+
isDirectory: boolean;
|
|
106
|
+
size: number;
|
|
107
|
+
modified: Date;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface FsStat {
|
|
111
|
+
size: number;
|
|
112
|
+
created: Date;
|
|
113
|
+
modified: Date;
|
|
114
|
+
accessed: Date;
|
|
115
|
+
isDirectory: boolean;
|
|
116
|
+
isFile: boolean;
|
|
117
|
+
isSymlink: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface FsWatchEvent {
|
|
121
|
+
type: 'create' | 'modify' | 'delete' | 'rename';
|
|
122
|
+
path: string;
|
|
123
|
+
newPath?: string; // For rename
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Security considerations**:
|
|
128
|
+
- Require allowFs flag
|
|
129
|
+
- Consider sandboxed paths (app data directory only by default)
|
|
130
|
+
- Full access requires explicit user consent
|
|
131
|
+
|
|
132
|
+
### 3. System Integration (Lower Priority)
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
interface MsgAPI {
|
|
136
|
+
system?: {
|
|
137
|
+
// Clipboard
|
|
138
|
+
clipboard: {
|
|
139
|
+
read(): Promise<string>;
|
|
140
|
+
write(text: string): Promise<void>;
|
|
141
|
+
readImage?(): Promise<Uint8Array>;
|
|
142
|
+
writeImage?(data: Uint8Array): Promise<void>;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Notifications
|
|
146
|
+
notify(title: string, body: string, options?: NotifyOptions): Promise<void>;
|
|
147
|
+
|
|
148
|
+
// Shell/external commands (very restricted)
|
|
149
|
+
openExternal(url: string): Promise<void>; // Open in default browser/app
|
|
150
|
+
|
|
151
|
+
// Environment info
|
|
152
|
+
platform: 'windows' | 'macos' | 'linux' | 'android' | 'ios';
|
|
153
|
+
arch: string;
|
|
154
|
+
version: string;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Implementation Priorities
|
|
162
|
+
|
|
163
|
+
### Phase 1: Wire Up msga (Current)
|
|
164
|
+
1. Connect JavaScript bridge to C# handlers in msga
|
|
165
|
+
2. Test existing window control APIs on Android/Windows
|
|
166
|
+
3. Ensure parity with msgview/msger for basic features
|
|
167
|
+
|
|
168
|
+
### Phase 2: UDP Support
|
|
169
|
+
1. Add UDP types to msgapidefs
|
|
170
|
+
2. Implement in msger (Rust) - fastest iteration
|
|
171
|
+
3. Port to msgview (Electron)
|
|
172
|
+
4. Port to msga (C#/MAUI)
|
|
173
|
+
|
|
174
|
+
### Phase 3: Enhanced File System
|
|
175
|
+
1. Add new fs types to msgapidefs
|
|
176
|
+
2. Implement mkdir, rename, copy, stat
|
|
177
|
+
3. Add file watching (platform-specific)
|
|
178
|
+
|
|
179
|
+
### Phase 4: System Integration
|
|
180
|
+
1. Clipboard support
|
|
181
|
+
2. Notifications
|
|
182
|
+
3. Platform detection
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Cross-Implementation Testing
|
|
187
|
+
|
|
188
|
+
All implementations should pass the same test suite. Use samples.html as base:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
msgapidefs/samples.html - Interactive test page
|
|
192
|
+
msgapidefs/tests/ - Automated tests (TODO)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Test categories:
|
|
196
|
+
- [ ] Window control (fullscreen, minimize, maximize, resize, position)
|
|
197
|
+
- [ ] File dialogs (select file, select files, save as, select folder)
|
|
198
|
+
- [ ] File system read/write (when allowFs enabled)
|
|
199
|
+
- [ ] UDP send/receive (when implemented)
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Notes
|
|
204
|
+
|
|
205
|
+
- msgapi is injected as `window.msgapi` global
|
|
206
|
+
- All async methods return Promises
|
|
207
|
+
- Errors thrown as standard JavaScript errors
|
|
208
|
+
- Platform-specific features return undefined/null if unavailable
|
|
209
|
+
- Version property indicates implementation: `msgapi.version` = "msga/1.0" etc.
|