Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIModule ¶ added in v0.1.0
type APIModule interface {
model.ModuleNaming // provides ModelName() — identity
MountAPI(r Router)
}
APIModule is a module that exposes a server API. It is consumed by the server entry point (!wasm): which passes it the host's Router, and the module registers its own routes/handlers. Since Router is isomorphic, the module never imports net/http to describe its API. The concrete transport (binary upload, another protocol mounted as a route) is the module's internal decision.
type Caller ¶ added in v0.1.4
type Caller interface {
// Call invokes op; result/err arrive asynchronously via callback
// (works for wasm fetch and for in-process test doubles alike).
// Implementations MUST propagate every error — never swallow.
Call(op string, args model.Encodable, callback func(result []byte, err error))
// Dispatch is fire-and-forget (no response expected).
Dispatch(op string, args model.Encodable)
}
Caller is how a client-side view invokes a named server operation without knowing the wire protocol or transport. It mirrors APIModule: APIModule is the mount-side contract, Caller is the call-side contract.
op is the logical operation name (e.g. "list_services") — NEVER a wire-level method. Translating op to the concrete envelope is the adapter's job (e.g. mcp.NewCaller adapts *mcp.Client). Test doubles satisfy Caller with canned bytes and no transport.
type Context ¶ added in v0.1.0
type Context interface {
Method() string
Path() string
Body() []byte
GetHeader(key string) string
SetHeader(key, value string)
WriteStatus(code int)
Write(b []byte) (int, error)
// Request-scoped values (middleware passes data to the next handler).
SetValue(key string, v any)
Value(key string) any
// Isomorphic cookies.
SetCookie(c Cookie) // writes a cookie to the response
Cookie(name string) (Cookie, bool) // reads a cookie from the request; ok=false if not found
// Request-scoped identity. An auth middleware records the caller;
// handlers and mounted modules read it.
SetUserID(id string) // records the authenticated identity (id "" = anonymous)
UserID() string // reads the identity; "" if no valid session
}
Context is the minimal abstraction seen by a handler: request → response. Same interface signature for both native (!wasm) and edge/wasm targets.
Ownership: a Context belongs to ONE goroutine (the handler's); implementations are not required to be safe for concurrent use (same contract as http.ResponseWriter). To feed it from other goroutines, send the data over a channel to the owning goroutine — never share the Context itself.
type Cookie ¶ added in v0.1.1
type Cookie struct {
Name string // e.g. "session_id", "user_pref"
Value string // e.g. "abc123xyz789"
Path string // e.g. "/", "/api"; omit for "/"
Domain string // e.g. "example.com"; omit for current domain
MaxAge int // >0 seconds; 0 = session; <0 = delete now
Secure bool // true = HTTPS only
HttpOnly bool // true = no JavaScript access
SameSite SameSite // SameSiteLax, SameSiteStrict, SameSiteNone
}
Cookie is the isomorphic representation of an HTTP cookie. It does not reference net/http: each concrete implementer maps it to its transport (net/http.Cookie on native; Set-Cookie header on edge/wasm).
type HandlerFunc ¶ added in v0.1.0
type HandlerFunc func(Context)
HandlerFunc is the dispatch unit: receives a Context and responds to it.
type Middleware ¶ added in v0.1.0
type Middleware func(HandlerFunc) HandlerFunc
Middleware wraps a handler to add cross-cutting logic (auth, logging). Operate ONLY on Context — never on concrete transport types.
type Route ¶ added in v0.1.1
type Route interface {
// Requires binds an RBAC permission to the route: the (resource, action) pair.
//
// Both are typed (model.Resource, model.Action). They used to be two bare strings in a
// row, so swapping them compiled — and the failure was not an error but a SILENT denial
// at runtime, in the one place where silence is unacceptable. Now it does not compile.
//
// The resource is open vocabulary: the app declares its own ("service_catalog"). The
// action is a closed CRUD set (model.Read, model.Update, …): persistence has four verbs
// and no tool in this ecosystem ever needed a fifth.
Requires(resource model.Resource, action model.Action) Route
// Authenticated marks the route as reachable by any identity, with no permission check.
// For operations on the CALLER themselves, where authentication already is the check.
Authenticated() Route
// Public marks the route as reachable with no identity at all.
Public() Route
}
Route describes a registered route and allows annotating it. It is returned by each Router registration method. Annotations are declarative: the contract does not enforce them — each concrete implementer (native server, edge runtime) enforces them.
type RouteInfo ¶ added in v0.1.1
type RouteInfo struct {
Method string // e.g. "GET", "POST"
Path string // e.g. "/api/users", "/api/orders/:id"
Resource model.Resource // required by AccessGuarded; must be empty otherwise
Action model.Action // e.g. model.Read; 0 = none
// Access is what the route declared. The ZERO VALUE is model.AccessGuarded: a route that
// annotates nothing is unreachable until it declares a Resource, and an enforcer must
// reject it loudly at startup.
//
// It replaced a `Public bool` alongside an empty-or-not Resource. That encoding made an
// illegal state writable — a route could be Public AND carry a Requires, and the gate
// silently dropped the permission check: a route that looked protected and was not.
Access model.Access
// Dir is the directory served by PublicDir; "" for every other route.
// It exists so a whole served directory is visible to introspection instead of
// being smuggled past the router by a file-server fallback.
Dir string
}
RouteInfo is the read-only view of a registered route — for introspection.
type Router ¶
type Router interface {
Get(path string, h HandlerFunc) Route
Post(path string, h HandlerFunc) Route
Put(path string, h HandlerFunc) Route
Delete(path string, h HandlerFunc) Route
Options(path string, h HandlerFunc) Route
Handle(method, path string, h HandlerFunc) Route
Stream(path string, h StreamFunc) Route
Socket(path string, h SocketFunc) Route
// PublicAsset registers ONE route serving ONE file to the browser: generated
// content such as index.html, the stylesheet, the JS bundle or the wasm binary.
//
// It is public by construction — a browser fetching an asset has no identity
// yet. It returns no Route: there is no permission to attach, so an asset can
// neither be left private by accident (a silent 403 on a blank page) nor be
// wrongly gated. Serving a file that DOES need permissions is a normal route:
// Get(path, h).Requires(resource, action) — which fails closed if forgotten.
PublicAsset(path string, h HandlerFunc)
// PublicDir serves a whole directory under a prefix (e.g. "web/public").
// Same contract as PublicAsset: public by construction, no Route to gate.
PublicDir(prefix string, dir string)
Use(m ...Middleware)
// Routes enumerates the registered routes and their metadata.
Routes() []RouteInfo
}
Router is what a module registers its routes on. A concrete implementer (native server, edge runtime) satisfies this interface; modules and hosts only consume it.
type SameSite ¶ added in v0.1.1
type SameSite int
SameSite types the SameSite policy — illegal state not representable (not a string).
type Socket ¶ added in v0.1.0
Socket is the bidirectional upgraded connection (WebSocket). Isomorphic abstraction: does not touch concrete upgrade mechanisms.
type SocketFunc ¶ added in v0.1.0
type SocketFunc func(Socket)
SocketFunc is a handler that receives a typed Socket.
type StreamFunc ¶ added in v0.1.0
type StreamFunc func(Streamer)
StreamFunc is a handler that receives a typed Streamer.
type Streamer ¶ added in v0.1.0
type Streamer interface {
Context
Flush() // sends to the client what has been written so far, without closing the response
}
Streamer is a Context that also flushes writes immediately. Used for incremental responses (SSE, streaming).
Ownership: same single-goroutine contract as Context. A push loop (SSE hub, broker) must deliver messages to the handler's goroutine via a channel; only that goroutine calls Write/Flush.