Hypen
Server SDKs

Go SDK

Server-side Hypen SDK for Go applications

Go SDK

The Go SDK provides a complete server-side Hypen implementation for Go applications with reactive state management, routing, and Remote UI streaming.

Installation

go get github.com/hypen-lang/hypen-golang

Quick Start

package main

import (
    "github.com/hypen-lang/hypen-golang/core"
    "github.com/hypen-lang/hypen-golang/remote"
)

func main() {
    // Create a counter module
    counter := core.NewAppBuilder(map[string]any{"count": 0}, nil).
        OnAction("increment", func(ctx core.ActionHandlerContext) {
            count := ctx.State.Get("count").(int)
            ctx.State.Set("count", count+1)
        }).
        OnAction("decrement", func(ctx core.ActionHandlerContext) {
            count := ctx.State.Get("count").(int)
            ctx.State.Set("count", count-1)
        }).
        Build()

    // Start Remote UI server
    server := remote.NewServer(counter)
    server.Listen(":3000")
}

State Management

The SDK provides reactive ObservableState with automatic change tracking:

// Create observable state
state := core.NewObservableState(map[string]any{
    "user": map[string]any{
        "name": "Alice",
        "email": "alice@example.com",
    },
    "items": []string{},
}, &core.StateObserverOptions{
    OnChange: func(change core.StateChange) {
        fmt.Printf("Changed paths: %v\n", change.Paths)
    },
})

// Get values
name := state.Get("user.name").(string)

// Set values (triggers OnChange)
state.Set("user.name", "Bob")

// Batch multiple updates
state.BatchUpdate(func() {
    state.Set("user.name", "Charlie")
    state.Set("user.email", "charlie@example.com")
})

Action Handlers

Action handlers receive a context object with access to state, routing, and global context:

counter := core.NewAppBuilder(initialState, nil).
    OnAction("submit", func(ctx core.ActionHandlerContext) {
        // Access action details
        payload := ctx.Action.Payload.(map[string]any)

        // Modify state
        ctx.State.Set("submitted", true)

        // Navigate
        ctx.Next.Router.Push("/success")

        // Access other modules
        auth := ctx.Context.GetModule("auth")
        if auth != nil {
            // ...
        }
    }).
    Build()

Handler Context

FieldTypeDescription
ctx.Action.NamestringAction name
ctx.Action.PayloadanyAction payload
ctx.Action.SenderstringSender component ID
ctx.State*ObservableStateReactive state
ctx.Next.Router*HypenRouterRouter for navigation
ctx.ContextGlobalContextCross-module access

Routing

The HypenRouter provides URL-based navigation with pattern matching:

router := core.NewHypenRouter("/")

// Navigate
router.Push("/users/123")
router.Replace("/login")
router.Back()
router.Forward()

// Get current route
route := router.CurrentRoute()
fmt.Println(route.Path)   // "/users/123"
fmt.Println(route.Params) // map[string]string{}

// Pattern matching
match, params := router.MatchPath("/users/:id")
// match = true, params = {"id": "123"}

// Listen for route changes
router.OnRouteChange(func(route core.Route) {
    fmt.Printf("Navigated to: %s\n", route.Path)
})

Event System

The SDK includes a typed event emitter for pub/sub messaging:

emitter := core.CreateEventEmitter()

// Subscribe to events
unsubscribe := emitter.On("userLoggedIn", func(payload any) {
    user := payload.(map[string]any)
    fmt.Printf("User logged in: %s\n", user["name"])
})

// Emit events
emitter.Emit("userLoggedIn", map[string]any{"name": "Alice"})

// Unsubscribe
unsubscribe()

// One-time listener
emitter.Once("ready", func(payload any) {
    fmt.Println("Ready!")
})

Remote UI Server

Stream UI updates over WebSocket:

import "github.com/hypen-lang/hypen-golang/remote"

// Create server with your module
server := remote.NewServer(myModule)

// Configure options
server.SetOptions(remote.ServerOptions{
    HeartbeatInterval: 30 * time.Second,
    MaxConnections:    1000,
})

// Custom HTTP handler integration
http.HandleFunc("/ws", server.HandleWebSocket)
http.ListenAndServe(":3000", nil)

Client Messages

The server handles these client messages:

TypeDescription
dispatchActionDispatch an action from the client
pingHeartbeat ping

Server Messages

The server sends these messages:

TypeDescription
initialTreeFull UI tree on connect
patchIncremental UI updates
stateUpdateState changes
pongHeartbeat response

Testing

Use MockEngine to test modules without the full WASM engine:

func TestCounter(t *testing.T) {
    // Create mock engine
    engine := core.NewMockEngine()

    // Build module with mock
    counter := core.NewAppBuilder(map[string]any{"count": 0}, engine).
        OnAction("increment", func(ctx core.ActionHandlerContext) {
            count := ctx.State.Get("count").(int)
            ctx.State.Set("count", count+1)
        }).
        Build()

    // Trigger action
    engine.TriggerAction("increment", nil)

    // Verify state
    count := counter.State().Get("count").(int)
    if count != 1 {
        t.Errorf("Expected count=1, got %d", count)
    }
}

Component Loading

Discover and load .hypen components from the filesystem:

// Load all components from a directory
loader := core.NewComponentLoader("./components")
components, err := loader.LoadAll()

// Load specific component
component, err := loader.Load("Counter")

Requirements

  • Go 1.21+
  • gorilla/websocket (for WebSocket support)

See Also