@bobfrankston/msgapidefs
v0.1.34
Published
TypeScript definitions for msgapi JavaScript API (msgview/msger)
Downloads
648
Maintainers
Readme
@bobfrankston/msgapidefs
⚠️ SECURITY: msgapi gives full native OS access (filesystem, processes, networking). Intended ONLY for trusted apps — native apps written as web pages. Do not expose to untrusted content.
⚠️ EXPERIMENTAL: APIs are experimental and subject to change.
TypeScript definitions for the msgapi JavaScript API used by msga, msger, and msgview. See the Typescript definitions in msgapidefs.ts for more info
Workspace Structure
msgx (workspace root)
├── msgapidefs - TypeScript type definitions for window.msgapi (this package)
├── msgcommon - Shared CLI parsing, templates, pinning utilities
├── msger - Rust/wry webview host (Windows, Linux)
├── msgview - Electron webview host (Windows, Linux, Mac, Pi)
├── msga - .NET MAUI webview host (Android, Windows)
├── build - Release build orchestration script (build/build.ts)
└── md - Shared documentationInstallation
npm install @bobfrankston/msgapidefsUsage
// Import to register global window.msgapi type
import '../node_modules/@bobfrankston/msgapidefs/msgapidefs.js';
// Note: '../node_modules/@bobfrankston/msgapidefs/index.js' may also work via package.json exports
// Or import types explicitly
import type { MsgAPI, MsgResult } from '../node_modules/@bobfrankston/msgapidefs/msgapidefs.js';
// msgapi provides APIs not available in browsers/webviews
if (window.msgapi) {
// Window control beyond browser capabilities
window.msgapi.setFullscreen(true);
window.msgapi.setAlwaysOnTop(true);
// File system access (requires allowFs flag)
const file = await window.msgapi.fs?.selectFile();
}
// For standard browser APIs, use native methods:
document.title = 'My App'; // Instead of msgapi.setTitle()
localStorage.setItem('key', 'value'); // Instead of msgapi.saveData()
window.close(); // Instead of msgapi.close() if not passing resultPlatform Support
✅ = Current | 🔧 = Possible (recompile/rebuild needed) | ❌ = Not feasible
| Platform | msger (Rust/wry) | msgview (Electron) | msga (.NET MAUI) | |----------|:----------------:|:------------------:|:----------------:| | Windows | ✅ | ✅ | ✅ | | Linux | ✅ | ✅ | ❌ | | macOS | 🔧 wry supports it | ✅ | 🔧 Mac Catalyst | | Raspberry Pi | ⚠️ runs but no display | ✅ | ❌ | | Android | ❌ | ❌ | ✅ (primary) | | iOS | ❌ | ❌ | ❌ |
msger uses wry (Rust webview). Desktop platforms (Win/Linux/Mac) are mature. msger can cross-compile and run on the Pi but does not display properly (wry/GTK rendering issues) — not a priority to fix. msgview runs wherever Electron does — desktop only, no mobile. msga targets Android primarily; Windows for dev/testing. iOS would be the most realistic path — MAUI supports iOS natively, so msga could target it with an iOS build profile, Apple developer account, and platform-specific conditional code (
#if IOS) for permissions and APIs. No current plans.
Implementation Status
✅ = Implemented | ⚠️ = Partial | ❌ = Not implemented
| API | msgview | msger | msga | Notes |
|-----|:-------:|:-----:|:----:|-------|
| Configuration | | | | |
| version | ❌ | ❌ | ✅ | window.msgapi.version string |
| setLogging(value?) | ❌ | ❌ | ✅ | Returns previous state |
| setAutoUpdate(value?) | ❌ | ❌ | ✅ | 'auto'/'check'/'off'; msga Android only |
| Window Control | | | | window.msgapi.* |
| toggleFullscreen() | ❌ | ✅ | ✅ | msger: via window.msgapi (msger-api.js) |
| setFullscreen(bool) | ✅ | ✅ | ✅ | |
| minimize() | ✅ | ✅ | ✅ | msga: Windows only |
| maximize() | ✅ | ✅ | ✅ | msga: Windows only |
| setSize(w, h) | ✅ | ✅ | ✅ | msga: Windows only |
| setPosition(x, y) | ✅ | ✅ | ✅ | msga: Windows only |
| setAlwaysOnTop(bool) | ✅ | ✅ | ❌ | |
| close(result?) | ✅ | ✅ | ✅ | |
| navigate(url) | ❌ | ❌ | ✅ | msga only |
| reload() | ❌ | ❌ | ✅ | msga only |
| File System | | | | window.msgapi.fs.* |
| fs.selectFile() | ❌ | ❌ | ✅ | msga: full JS bridge wired |
| fs.selectFiles() | ❌ | ❌ | ✅ | msga: full JS bridge wired |
| fs.saveFileAs() | ❌ | ❌ | ✅ | msga: Windows only |
| fs.selectFolder() | ❌ | ❌ | ✅ | msga: Windows only |
| fs.read(path) | ❌ | ❌ | ✅ | msga: binary→base64→TextDecoder |
| fs.readAsDataUrl(path) | ❌ | ❌ | ✅ | msga: auto-detects MIME from extension |
| fs.write(path, content) | ❌ | ❌ | ✅ | |
| fs.list(path) | ❌ | ❌ | ✅ | msga: includes attributes/created on Windows |
| fs.exists(path) | ❌ | ❌ | ✅ | |
| fs.delete(path) | ❌ | ❌ | ✅ | |
| Shell | | | | window.msgapi.shell.* |
| shell.exec() | ❌ | ❌ | ✅ | Process with stdout/stderr; limited on non-rooted Android |
| shell.open() | ❌ | ❌ | ✅ | Android: Intent.ACTION_VIEW; Windows: ShellExecute |
| shell.showInFolder() | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
| shell.trash() | ❌ | ❌ | ❌ | Defined in interface, not yet implemented |
| UDP Networking | | | | window.msgapi.udp.* |
| udp.send() | ✅ | ✅ | ✅ | Node dgram / Rust UdpSocket / C# UdpClient |
| udp.listen() | ✅ | ✅ | ✅ | Background receive loop in all hosts |
| udp.sendReceive() | ✅ | ✅ | ✅ | Send + wait with timeout |
| udp.broadcast() | ✅ | ✅ | ✅ | 255.255.255.255 broadcast |
| TCP Networking | | | | window.msgapi.tcp.* |
| tcp.connect() | ✅ | ✅ | ✅ | Node net/tls / Rust TcpStream+native-tls / C# TcpClient+SslStream |
| tcp.write() | ✅ | ✅ | ✅ | UTF-8 string data |
| tcp.onData() | ✅ | ✅ | ✅ | Persistent callback, fires multiple times |
| tcp.onClose() | ✅ | ✅ | ✅ | Connection closed notification |
| tcp.onError() | ✅ | ✅ | ✅ | Error notification |
| tcp.upgradeTLS() | ✅ | ✅ | ✅ | STARTTLS — upgrade plaintext to TLS |
| tcp.close() | ✅ | ✅ | ✅ | Close connection |
| HTTP Fetch | | | | window.msgapi.http.* |
| http.fetch() | ✅ | ✅ | ✅ | Native HTTP — bypasses CORS/mixed-content |
| Host Launcher Features | | | | Not part of window.msgapi; CLI / launcher-side |
| Markdown rendering (.md/.markdown auto-render) | ✅ | ✅ | ❌ | Local file:// URL → rendered HTML via msgcommon/markdown (marked + light/dark CSS). msga could match it in MainPage.xaml.cs URL interception — not done yet. |
| Unpacked Chrome extension loading (-extension <dir>) | ✅ | ❌ | ❌ | msgview-only via session.extensions.loadExtension. wry has no public extension API; WebKitGTK has none at all. msga would need a separate Android/WebView2 plumbing — not planned. |
msger now injects
window.msgapiviamsger-api.jswith window control, UDP, and HTTP. File system and shell are not yet implemented in msger or msgview.Markdown rendering (last two rows): both msger and msgview detect
.md/.markdownURLs pointing at local files and render them via the sharedrenderMarkdown()helper inmsgcommon/markdown— no extensions, no per-machine setup, GFM with light/dark CSS that followsprefers-color-scheme. Library callers canimport { renderMarkdown, looksLikeMarkdown } from '@bobfrankston/msgcommon/markdown'directly. Extension loading is msgview-only by design: see the row's note.
httpudp-client auto-detects window.msgapi?.udp and uses native UDP when available, falling back to the httpudp WebSocket proxy otherwise.
See msgapi-plan.md for the full implementation plan and roadmap.
API Reference
Configuration
version- Host app version string (e.g.,"1.2.0")setLogging(value?)- Control debug logging. Returns previous state as string.'msga'- C# host logging only (logit-msga)'msgapi'- JS bridge logging only (logit-msgapi)'on'- Both streams'off'/'none'- All offnull/''- Query current state without changing- Returns:
'msga','msgapi','msga,msgapi', or'none'
setAutoUpdate(value?)- Control APK auto-update behavior. Returns previous state as string. msga Android only.'auto'- Check versions.json, download + install silently (no prompt)'check'- Check versions.json, prompt "Install now?" before downloading'off'- No update checkingnull/undefined- Query current state without changing- Default:
'auto'(configurable viaAUTO_UPDATEin msga.env) - Checks on startup then every
UPDATE_INTERVAL_MINUTES(default 60)
Window Control
toggleFullscreen()- Toggle fullscreen modesetFullscreen(enabled: boolean)- Set fullscreen stateminimize()- Minimize windowmaximize()- Toggle maximize/restoresetSize(width, height)- Set window sizesetPosition(x, y)- Set window positionsetAlwaysOnTop(enabled: boolean)- Keep window on topclose(result?)- Close window and return result to parent process
File System
File dialogs (user picks interactively — safe without special permissions):
fs.selectFile(options?)- Open file pickerfs.selectFiles(options?)- Open multi-file pickerfs.saveFileAs(content, filename?, options?)- Save file dialogfs.selectFolder(options?)- Folder picker
Direct path operations (give the webview full filesystem access — msga only currently):
fs.read(path, options?)- Read file. Options:{ encoding: 'utf8' | 'base64' | 'binary' }. Default'utf8'returns string;'base64'returns raw base64 string (efficient for images);'binary'returnsUint8Array.fs.readAsDataUrl(path, mimeType?)- Read file as data URL (data:image/jpeg;base64,...). Auto-detects MIME from extension if omitted.fs.write(path, content)- Write filefs.list(path)- List directory. ReturnsFileInfo[]withattributes(Win32 flags) andcreatedon Windows.fs.exists(path)- Check if file/directory existsfs.delete(path)- Delete file or directory
Note: File system operations are currently implemented only in msga. msger and msgview do not yet have
window.msgapi.fs— these hosts have their own internal fs mechanisms but have not wired them to the msgapi interface.
UDP Networking
Native UDP socket access — no WebSocket proxy needed when running inside a msgapi host.
udp.send(host, port, data)- Send UDP packet to specific hostudp.listen(port, callback)- Listen for incoming UDP packets (returns{close()}handle)udp.sendReceive(host, port, data, timeoutMs?)- Send and wait for responseudp.broadcast(port, data)- Broadcast to local network
TCP Networking
Native TCP/TLS socket access — for IMAP, SMTP, and other stream protocols. Connections are long-lived; use stream IDs to manage multiple connections. TLS is handled natively — JS never sees the raw TLS handshake.
tcp.connect(host, port, tls)- Connect to host:port. Iftlsis true, connect with TLS directly (port 993). Returns astreamId(number).tcp.write(streamId, data)- Write UTF-8 string data to stream.tcp.onData(streamId, callback)- Register handler for incoming data. Persistent — fires multiple times as data arrives.tcp.onClose(streamId, callback)- Register handler for connection close.hadErroris true if closed due to error.tcp.onError(streamId, callback)- Register handler for errors.tcp.upgradeTLS(streamId, servername)- Upgrade a plaintext connection to TLS (STARTTLS). Used by IMAP/SMTP after negotiating capabilities on plaintext.tcp.close(streamId)- Close the connection.
// Connect to Gmail IMAP (direct TLS on port 993)
const id = await window.msgapi.tcp.connect('imap.gmail.com', 993, true);
window.msgapi.tcp.onData(id, (data) => console.log('Server:', data));
window.msgapi.tcp.onClose(id, (hadError) => console.log('Closed', hadError));
window.msgapi.tcp.onError(id, (msg) => console.error('Error:', msg));
// STARTTLS example (connect plaintext, then upgrade)
const id2 = await window.msgapi.tcp.connect('imap.example.com', 143, false);
// ... negotiate STARTTLS capability ...
await window.msgapi.tcp.upgradeTLS(id2, 'imap.example.com');
// ... now on encrypted connection ...Platform implementations: msgview uses Node.js net/tls, msger uses Rust std::net::TcpStream + native-tls, msga uses C# TcpClient + SslStream.
Used by: @bobfrankston/iflow-direct BridgeTransport — enables browser/WebView IMAP without a Node.js server.
Shell / Process Execution
Native process execution — run commands, open files with default handler. msga only currently.
shell.exec(command, args?, options?)- Run external command and capture output. Returns{ stdout, stderr, exitCode }. Options:cwd,timeout(ms),stdin,encoding('utf8'|'base64'). Limited on non-rooted Android.shell.open(path)- Open file/URL with system default handler (Windows: ShellExecute, Android: Intent.ACTION_VIEW)shell.showInFolder(path)- Select file in Explorer / file manager (not yet implemented)shell.trash(path)- Move to Recycle Bin / Trash (not yet implemented)
HTTP Fetch
Native HTTP client — bypasses browser CORS and mixed-content restrictions. Use when an HTTPS-served web app needs to reach HTTP endpoints (e.g., local devices on the LAN).
http.fetch(url, init?)- Drop-in replacement for browserfetch(). SameRequestInitparams, returns a realResponseobject (.ok,.status,.json(),.text(),.headers,.clone(), etc.)
// HTTP from an HTTPS page — blocked by browsers, works via msgapi
const r = await window.msgapi.http.fetch('http://192.168.1.5/api/status');
const data = await r.json();
// POST with headers and body
const r2 = await window.msgapi.http.fetch('http://192.168.1.5/api/cmd', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cmd: 'reboot' })
});Platform implementations: msga uses C# HttpClient, msger uses Rust ureq, msgview uses Node.js fetch(). All return a real Response object constructed on the JS side.
Unsupported init fields (silently ignored — not meaningful for native HTTP): mode, credentials, cache, signal, redirect. new Headers() objects are normalized to plain objects.
Examples
See samples.html for interactive examples and demos of all API features!
Test in msgview:
msgview -url "file:///path/to/node_modules/@bobfrankston/msgapidefs/samples.html"Test in msger:
msger -url "file:///path/to/node_modules/@bobfrankston/msgapidefs/samples.html"Test in browser:
# Open samples.html in your browser to test browser compatibilityThe samples demonstrate:
- Window control (fullscreen, minimize, maximize, resize, position)
- Data persistence (save/load user preferences, session data, recent files)
- Presentation mode example
- Session management
- And more!
Cross-Platform Implementation
msgapidefs defines the TypeScript interface only. Each host implements the window.msgapi bridge differently:
msgview (Electron)
- Bridge: Electron
contextBridge.exposeInMainWorld('msgapi', ...)inpreload/preload.ts - Mechanism:
ipcRenderer.invoke()calls to main process IPC handlers - Window control: Electron
BrowserWindowAPI - File system: Node.js
fsmodule via IPC, file dialogs via Electrondialog
msger (Rust/wry)
- Bridge: JavaScript injected via wry
with_initialization_script()inmsger-native/src/main.rs - JS API:
msger-native/src/msger-api.jscreateswindow.msgapiobject - Mechanism:
window.ipc.postMessage(JSON.stringify(...))to Rust IPC handler - Capabilities: Window control, UDP, HTTP fetch via
window.msgapi - Limitations: wry 0.47.2 has IPC issues;
close()useswindow.close()workaround
msga (.NET MAUI)
- Bridge: JavaScript injected via
WebViewControl.EvaluateJavaScriptAsync()inMainPage.xaml.cs - JS API:
GetMsgApiScript()createswindow.msgapiobject inline - Mechanism:
msgapi://URL interception — JS creates hidden iframes, C# intercepts inOnNavigating - Async: Promise callbacks via
callNativeAsync()→window._msgapiResolve()/_msgapiReject() - Window control: Platform-conditional (
#if WINDOWSuses WinUI AppWindow APIs) - File system: MAUI
FilePicker, Windows-specificFileSavePicker/FolderPicker - Platforms: Android (primary target), Windows (development/testing)
Adding New Sub-Objects
Each capability is an optional sub-object on MsgAPI (e.g., fs?, udp?). To add a new one:
- Define the interface in
msgapidefs.tsas an optional sub-object onMsgAPI - Implement the native side in each host:
- msgview: Add IPC handlers in
main.ts, expose inpreload/preload.ts - msger: Add Rust handler in
main.rs, expose inmsger-api.js - msga: Add C# methods + JS injection in
GetMsgApiScript()(see msga readme)
- msgview: Add IPC handlers in
- Browser code uses optional chaining:
window.msgapi?.newFeature?.method()— works everywhere, undefined when unavailable
See msgapi-plan.md for the full feature matrix and implementation status across all three hosts.
Related Packages
- @bobfrankston/msgview - Electron-based message box (slower, works on Pi)
- @bobfrankston/msger - Rust/wry-based message box (fast, lightweight)
- msga - .NET MAUI Android/Windows wrapper app
License
ISC
