npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bobfrankston/msgapidefs

v0.1.40

Published

TypeScript definitions for msgapi JavaScript API (msgview/msger)

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, Mac, Pi) — PRIMARY
├── msgview       - Electron webview host (desktop) — SEMI-RETIRED, see below
├── msga          - .NET MAUI webview host (Android, Windows)
├── build         - Release build orchestration script (build/build.ts)
└── md            - Shared documentation

Installation

npm install @bobfrankston/msgapidefs

Usage

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

Platform 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 | ✅ | ✅ | ❌ | | Android | ❌ | ❌ | ✅ (primary) | | iOS | ❌ | ❌ | ❌ |

msger uses wry (Rust webview) and is the primary host. Desktop platforms (Win/Linux/Mac) are mature. The Pi display bug ("runs but renders shredded scanlines") was WebKitGTK's DMABUF renderer on vc4/v3d — msger now sets WEBKIT_DISABLE_DMABUF_RENDERER=1 automatically on Raspberry Pi hardware (both the JS launcher and the binary), and the Pi renders correctly as of 2026-07-31. msgview is semi-retired as of 2026-07-31 — see below. 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.

msgview is semi-retired (2026-07-31)

msger + msga cover every platform, so the Electron host has no platform to itself. Do not do feature-parity work on msgview. New features land in msger (and msga where it makes sense); msgview catches up only on demand, if a real need appears.

As of 2026-08-01 msgview has no capability msger lacks. The two things that used to be Electron-only are both settled:

  • Unpacked Chrome extensions (-extension) — not how add-ins are done here. window.msgapi (this package) plus injected init scripts is the add-in mechanism: initScript / initScriptPath inject app JS into every document before page scripts run, with the full msgapi surface available — host-native, works in msger and msga, no Chromium required. If something genuinely needs Chrome extension APIs, run it in Chrome.
  • -render off Windows — ported. msger now captures on Linux/Pi through WebKitGTK's snapshot API (see the render row above), verified on a Pi 5 for both PNG and JPEG output.

Engine currency is not a reason: a feature probe on Pi 5 / WebKitGTK 2.50.6 (2026-07-31) passed 23 of 24 modern checks — :has(), CSS nesting, container queries, subgrid, backdrop-filter, color-mix, oklch, Popover, View Transitions, Object.groupBy, Promise.withResolvers, Intl.Segmenter, WebGL2, OffscreenCanvas — failing only the RegExp v flag. The WebKitGTK user-agent token (Version/60.5) is a WebKitGTK version string, not a Safari generation; it says nothing about capability.

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 | | Diagnostics | | | | window.msgapi.diag.* | | diag.getExitReasons(max?) | ❌ | ❌ | ✅ | Why the previous run exited; msga Android 11+ only, [] elsewhere | | 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 | | Media capture (mic/camera) | ❌ | ❌ | ❌ | Not an msgapi API — getUserMedia is the engine's. What's missing is the permission grant, which no host makes: wry's WebKitGTK backend has no permission-request handling (WebKitGTK denies by default), msger ignores WebView2's PermissionRequested, msga lacks RECORD_AUDIO. Deferred; direction is an external speech package, not in-page capture — see msgapi-plan.md. | | 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. Not the add-in mechanism here — app add-ins are window.msgapi + injected init scripts (initScript / initScriptPath), which work in msger and msga without Chromium. Chrome-extension-specific needs go to Chrome. | | Headless mode (-noshow without -save) | ✅ | ✅ | ❌ | msger creates the WebView invisible (with_visible(false)) but still loads HTML and runs scripts — an embedded <script> can query the engine and window.ipc.postMessage a result back. Combined with -result <fieldname> for clean stdout, this lets msger act as a headless CSS/JS resolver (contrast-color(), getComputedStyle(), CSS.supports()). msgview matches with BrowserWindow({show:false}) + backgroundThrottling:false. | | Render to bitmap (-render [file], render option) | ✅ | ✅ | ❌ | Headless page→image capture: the window is never shown; the page loads, settles (renderDelay, default 100ms after load + two rAFs), is screenshotted, and the host exits. -render <file> writes the image (format from extension: .png/.jpg/.bmp) and the JSON result carries render: {path, width, height, format}; bare -render (or API render: true) returns it as render: {data (base64), width, height, format}. Library API: showMessageBox({render: "shot.png" \| true, renderDelay?, renderFormat?}); a failed capture rejects (msger) or sets renderError in the result. Both hosts fall back to capturing whatever rendered if the page hasn't loaded within the timeout (default 30s in render mode). msger: WebView2 DevTools Page.captureScreenshot on Windows, webkit_web_view_get_snapshot() → cairo PNG on Linux/Pi (2026-08-01); msgview: webContents.capturePage(). Both msger paths produce the same base64 PNG, so format transcoding and file writing are shared. Linux caveat: WebKitGTK cannot snapshot a window that was never mapped, so on Linux the render window IS mapped — made invisible by going fully transparent and skipping the taskbar, since Wayland has no way to park a window off-screen. Width/height are physical pixels (include DPI scale) and cover the WebView viewport, not the window frame. |

| Desktop overlay (-overlay, overlay option) | ❌ | ✅ | ❌ | msger-only (2026-08-09). Frameless + transparent window drawn over the desktop, click-through, always-on-top, kept out of the taskbar and never focused — for transient notices such as per-monitor identification. Needs all four of with_decorations(false), with_transparent(true) on the tao window, with_transparent(true) on the wry WebView, and a page that declares its own transparent background (the built-in template does; -url/-raw pages must). Click-through is set_ignore_cursor_events (Windows/Mac/X11/Wayland; best-effort, warns and stays clickable if refused). No taskbar slot on Windows/Linux only — macOS has no per-window equivalent, Dock presence being a process-level activation policy. Pair with timeout: an overlay has no close button and swallows no clicks, so nothing in it can be operated. Each overlay defaults to its own WebView2 profile keyed by screen, because concurrent WebView2 creations against one user-data dir fail with 0x80070057 — which is what launching one overlay per display does. Verified on Windows; Linux/Pi and Mac are built but not yet exercised. Linux/X11 additionally needs a running compositor or transparency renders black. | | Monitor enumeration (-list, listMonitors option) | ❌ | ✅ | ❌ | msger-only (2026-08-09). Prints {"monitors":[{index,name,x,y,width,height,scale,primary}]} and exits without creating a window. Indices match -screen, ordered bottom-row-first then left-to-right (the ordering -screen has always used on Windows, preserved so existing indices keep pointing at the same display). Exists so a caller can discover how many displays there are, and what to label them, before spawning one overlay per display. | | Screen selection (-screen <n>, screen option) | ❌ | ✅ | ❌ | Offsets -pos into the named monitor and picks which display -fullscreen covers. Was Windows-only for -pos; the gate was wrong rather than protective, so Mac/X11 now honor it too (2026-08-09). fullscreen + screen is the only portable way to target a display: Wayland does not let a client position its own window at absolute coordinates, but it does honor fullscreen on a chosen output. |

msger now injects window.msgapi via msger-api.js with 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/.markdown URLs pointing at local files and render them via the shared renderMarkdown() helper in msgcommon/markdown — no extensions, no per-machine setup, GFM with light/dark CSS that follows prefers-color-scheme. Library callers can import { 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 off
    • null / '' - 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 checking
    • null / undefined - Query current state without changing
    • Default: 'auto' (configurable via AUTO_UPDATE in msga.env)
    • Checks on startup then every UPDATE_INTERVAL_MINUTES (default 60)

Diagnostics

Why did the previous run exit? Android records the last few process deaths (ApplicationExitInfo, Android 11+); the host reads its own history on the next run and exposes it to the web app. This distinguishes crash / ANR / low-memory kill / cached-app freezer — no adb needed. Currently msga Android only; other hosts/platforms return [].

  • diag.getExitReasons(max?) - Previous-run exit records, newest first (default/max clamp: 5/16). Returns MsgExitInfo[]:
    • timestamp - Exit time, ms since epoch
    • reason - "LowMemory", "Crash", "CrashNative", "Anr", "Freezer", "ExitSelf", "Signaled", "UserRequested", "UserStopped", "DependencyDied", "ExcessiveResourceUsage", "PackageUpdated", "Other", ... (Android ApplicationExitInfo reason names)
    • description - OS-provided detail (may be null), e.g. "isolated not needed" = renderer cleanup
    • importance - Process state at death (e.g. "Foreground", "Cached") — a Cached + LowMemory kill is normal Android memory reclaim, not a bug
    • pss / rss - Memory at exit, KB
    • processName - Which process died: the main package name, or ...:sandboxed_process* = the WebView renderer. Records cover every process in the package — renderer deaths are routine cleanup, not the app dying; filter by processName to find real main-process exits
    • pid - Process ID at exit
    • status - Signal number for Signaled, exit code for ExitSelf, 0 otherwise
    • stateSummary - App-set marker (ActivityManager.setProcessStateSummary); msga stamps start=<ISO time> v=<version> at each launch so uptime-at-death is computable
    • trace - First ~4KB of the ANR/native-crash trace when the OS captured one (Anr/CrashNative only)
// Exit-history tool in a web app (e.g. houser debug panel)
const exits = await window.msgapi?.diag?.getExitReasons() ?? [];
for (const e of exits) {
    console.log(`${new Date(e.timestamp).toLocaleString()} ${e.reason}` +
        ` (${e.importance ?? '?'}, pss=${e.pss ?? '?'}KB) ${e.description ?? ''}`);
}

Interpreting common reasons: LowMemory = killed by the low-memory killer; Anr = main thread hung; Crash/CrashNative = unhandled exception / native crash; Freezer = app was frozen (Android 12+ cached-app freezing — UDP listeners silently suspended, app not actually dead); UserRequested = swiped away from recents; PackageUpdated = replaced by an APK update; Signaled = killed by signal (check description).

msga also logs new exit records via MsgALog at startup when LOGGING=true in msga.env (Previous exit: ...), deduplicated across runs via a lastSeenExitTimestamp preference. See msga/exit-diagnostics.md for the full design notes.

Window Control

  • toggleFullscreen() - Toggle fullscreen mode
  • setFullscreen(enabled: boolean) - Set fullscreen state
  • minimize() - Minimize window
  • maximize() - Toggle maximize/restore
  • setSize(width, height) - Set window size
  • setPosition(x, y) - Set window position
  • setAlwaysOnTop(enabled: boolean) - Keep window on top
  • close(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 picker
  • fs.selectFiles(options?) - Open multi-file picker
  • fs.saveFileAs(content, filename?, options?) - Save file dialog
  • fs.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' returns Uint8Array.
  • 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 file
  • fs.list(path) - List directory. Returns FileInfo[] with attributes (Win32 flags) and created on Windows.
  • fs.exists(path) - Check if file/directory exists
  • fs.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 host
  • udp.listen(port, callback) - Listen for incoming UDP packets (returns {close()} handle)
  • udp.sendReceive(host, port, data, timeoutMs?) - Send and wait for response
  • udp.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. If tls is true, connect with TLS directly (port 993). Returns a streamId (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. hadError is 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 browser fetch(). Same RequestInit params, returns a real Response object (.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 compatibility

The 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', ...) in preload/preload.ts
  • Mechanism: ipcRenderer.invoke() calls to main process IPC handlers
  • Window control: Electron BrowserWindow API
  • File system: Node.js fs module via IPC, file dialogs via Electron dialog

msger (Rust/wry)

  • Bridge: JavaScript injected via wry with_initialization_script() in msger-native/src/main.rs
  • JS API: msger-native/src/msger-api.js creates window.msgapi object
  • 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() uses window.close() workaround

msga (.NET MAUI)

  • Bridge: JavaScript injected via WebViewControl.EvaluateJavaScriptAsync() in MainPage.xaml.cs
  • JS API: GetMsgApiScript() creates window.msgapi object inline
  • Mechanism: msgapi:// URL interception — JS creates hidden iframes, C# intercepts in OnNavigating
  • Async: Promise callbacks via callNativeAsync()window._msgapiResolve() / _msgapiReject()
  • Window control: Platform-conditional (#if WINDOWS uses WinUI AppWindow APIs)
  • File system: MAUI FilePicker, Windows-specific FileSavePicker/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:

  1. Define the interface in msgapidefs.ts as an optional sub-object on MsgAPI
  2. Implement the native side in each host:
    • msgview: Add IPC handlers in main.ts, expose in preload/preload.ts
    • msger: Add Rust handler in main.rs, expose in msger-api.js
    • msga: Add C# methods + JS injection in GetMsgApiScript() (see msga readme)
  3. 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

License

ISC