Web pages
Plugins
Features that come in a Go package and an npm package, such as the official plugins, and writing your own.
A plugin adds a feature to web pages in two halves: Go services, and a
JavaScript package whose functions call them. The app uses the Go half with
mygo.Use and imports the JavaScript half from npm.
Official plugins
fetch that makes HTTP requests from Go,
with no CORS, any header, streamed bodies and cancellation.WebSocketA WebSocket whose connections Go
makes, with headers on the handshake.SQLiteLocal databases, parameterized queries and
atomic transactions, using SQLite's C engine compiled with Zig, without cgo.WatchChanges to files, in the directories the app
lets pages watch.UpdaterAn update window in the manner of
Sparkle, which checks for updates and offers to install them. It is all
Go, and its window is a web page or, for apps of native UI, native UI.TerminalA terminal for native UI, a view that
runs the shell or any program with Ghostty's terminal emulator. It is
all Go, with a native library that the CLI puts into apps.GlassLiquid Glass for native UI, as macOS draws
it, on every platform, with macOS's scroll edges and backdrop blurs. It
is all Go, with shaders of its own.GameWhat games need beyond what apps do, opt-in:
a game loop in fixed steps with its input, a camera, and gamepads in
one standard layout. It is all Go.LoadingLoading.dev's 29 loading indicators for
native UI, as they look and move in browsers. It is all Go.SF SymbolsApple's SF Symbols for native UI on
macOS, drawn by the system. It is all Go.AutomationA socket through which another
process, an end-to-end test runner or an agent, drives the running app:
its windows, the elements of their native UI, input, menus, scripts in
pages and screenshots. It is all Go, opt-in, and left out of release
builds; mygo-automation is its command line client for agents.PermissionFlowThe permissions the system
keeps for the user to give, such as Accessibility, Screen Recording and
the microphone: checked, asked for, and a guide to System Settings with
the app to drag into its list, after PermissionFlow. It is all Go.Using plugins
mygo.Use takes the plugins of the app, before App.Run:
import (
"github.com/egoist/mygo"
"github.com/egoist/mygo/plugins/fetch"
"github.com/egoist/mygo/plugins/websocket"
)
func main() {
mygo.Use(fetch.Plugin, websocket.Plugin)
// ...
}and the frontend imports their packages:
bun add @mygo-plugins/fetch @mygo-plugins/websocketimport { fetch } from "@mygo-plugins/fetch";
import { WebSocket } from "@mygo-plugins/websocket";
const res = await fetch("https://api.example.com/me", {
headers: { authorization: `Bearer ${token}` },
});
const ws = new WebSocket("wss://api.example.com/live", [], {
headers: { authorization: `Bearer ${token}` },
});The Plugin variable of an official plugin has the default options, and
its New function takes options, such as the http.Client of the fetch
and WebSocket plugins and an Allow function that decides which requests
pages may make. Each plugin's page lists them.
Using a plugin's JavaScript package without its Go half fails with an error
that says to call mygo.Use.
Writing a plugin
The Go half is a mygo.Plugin: a name, a service whose methods pages call,
and an optional Setup that runs when the app uses it:
package clock
// Plugin gives pages the time of the Go side.
var Plugin = mygo.Plugin{Name: "clock", Service: service{}}
type service struct{}
func (service) Now() time.Time { return time.Now() }The service is bound like those of mygo.Bind, with the same rules for its
methods (contexts, channels, errors), but as plugin:<name> and outside the
generated TypeScript client. Its JavaScript package calls it with call of
mygo-runtime, which it lists as a peer dependency, so that it uses the
app's copy (and as a development dependency, for its own builds and tests):
import { call } from "mygo-runtime";
export const now = async () => new Date(await call<string>("plugin:clock.Now"));Only the app's trusted pages may call a plugin, as with every bound method.
mygo.Use panics when a name is taken or the service's methods use types
that cannot cross to JavaScript, so mistakes show at startup.