Provides a Go API for interactive maps within Via applications:
- Plugin serves vendored MapLibre GL JS v4.7.1 assets
- Map struct with pre/post-render source, layer, marker, popup management
- Viewport signal sync (center, zoom, bearing, pitch) via hidden inputs
- FlyTo, SetCenter, SetZoom and other viewport setters via ExecScript
- Idempotent init script with SPA cleanup via MutationObserver
- Example app demonstrating markers, GeoJSON layers, and FlyTo actions
Streams listed in Options.Streams are created by Start() when the
embedded NATS server initializes, replacing manual EnsureStream calls
during setup. Migrates nats-chatroom and pubsub-crud examples.
Port 0 is treated as default (4222) by NATS server, causing hangs when
that port is unavailable. Port -1 (RANDOM_PORT) binds to an OS-assigned
free port, which is correct for an embedded server.
Add 30s keepalive pings to prevent proxy/CDN idle timeouts from killing
SSE connections silently. Track lastSeenAt on each SSE connect attempt
so Datastar's retry signals keep contexts alive through the reaper.
Read-only signals whose value is a function of other signals,
recomputed automatically at sync time. Supports String, Int, Bool,
and Text methods. Components store computed signals on the parent
page context like regular signals.
Move NATS startup from New() to Start(), so tests that don't use
pubsub never block on server initialization. Add a 10s timeout to
WaitForServer() and skip NATS tests gracefully when unavailable.
Unify event attribute construction through buildAttrKey() so debounce,
throttle, and window modifiers compose cleanly. OnChange no longer
hardcodes a 200ms debounce — callers opt in explicitly.
Contexts that lose their SSE connection now pass through a suspended
state before being fully reaped. Suspended contexts keep their shell
(ID, route, CSRF token) but free page resources. On reconnect, the
page init function is re-run for a seamless resume. Contexts past
the TTL trigger a client-side reload instead of a silent dead page.
Configurable via ContextSuspendAfter (default 15m) and ContextTTL
(default 1h).
Extract chat, profile, types, and user data into separate files.
Embed CSS via go:embed. Add a layout with nav, session-based profile
persistence, and a middleware guard that redirects to /profile when
no identity is set.
Datastar aborts SSE on visibilitychange (tab hidden) and reconnects
when visible. The previous cleanup-on-disconnect destroyed the context
before the client could reconnect. Now SSE disconnect does a soft
teardown (mark disconnected, keep context alive) and reconnect drains
stale patches before resuming. The reaper uses disconnect time instead
of creation time so recently-disconnected contexts aren't prematurely
reaped.
- Add navMu to serialize concurrent navigations on the same context
- Replace url.PathEscape with targeted JS string escaper (PathEscape
mangles full paths and doesn't escape single quotes)
- Collapse syncWithViewTransition into syncView(bool) to remove duplication
- Simplify popstate ready guard in navigate.js
- Preserve URL hash during SPA navigation
Swap page content over the existing SSE connection without full page
loads. A persistent Context resets its page-specific state (signals,
actions, intervals, subscriptions) on navigate while preserving the
SSE stream, CSRF token, and session.
- c.Navigate(path) for programmatic SPA navigation from actions
- Injected JS intercepts same-origin <a> clicks (opt out with
data-via-no-boost) and handles popstate for back/forward
- v.Layout() wraps pages in a shared shell for DRY nav/chrome
- View Transition API integration via WithViewTransitions() on
PatchElements and h.DataViewTransition() helper
- POST /_navigate endpoint with CSRF validation and rate limiting
- pageStopChan cancels page-level OnInterval goroutines on navigate
- Includes SPA example with layout, counter, and live clock pages
Replace the exported OnIntervalRoutine struct (Start/Stop/UpdateInterval)
with a single function that auto-starts the goroutine and returns an
idempotent stop closure. Uses close(channel) instead of send-on-channel,
fixing a potential deadlock when the goroutine exits via context disposal.
Closes#5 item 4.
Pub/sub now works out of the box — New() starts a process-scoped
embedded NATS server with JetStream. The PubSub interface remains
for custom backends via Config(Options{PubSub: ...}).
- Move vianats functionality into nats.go (eliminates circular import)
- Add NATSConn(), JetStream(), EnsureStream(), ReplayHistory[T]() to via
- Delete vianats/ package
- Simplify nats-chatroom and pubsub-crud examples
- Rewrite pubsub tests to use real embedded NATS
Fields created via Context.Field are now tracked on the page context,
so ValidateAll() and ResetFields() with no arguments operate on all
fields by default. Explicit field args still work for selective use.
Also switches MinLen/MaxLen to utf8.RuneCountInString for correct
unicode handling and replaces fmt.Errorf with errors.New where
format strings are unnecessary.
Introduces Field, Rule, ValidateAll, ResetFields, and AddError for
declarative input validation. Includes built-in rules (Required,
MinLen, MaxLen, Min, Max, Email, Pattern, Custom) and a signup
example exercising the full API surface.
* feat: add middleware example demonstrating route groups
Self-contained example covering v.Use(), v.Group(), nested groups,
Group.Use(), and middleware chaining with role-based access control.
* feat: add per-action middleware via WithMiddleware ActionOption
Reuses the existing Middleware type so the same auth/logging functions
work at both page and action level. Middleware runs after CSRF and
rate-limit checks, with full access to session and signals.
* feat: add RedirectView helper and refactor session example to use middleware
RedirectView lets middleware abort and redirect in one step. The session
example now uses an authRequired middleware on a route group instead of
an inline check inside the view.
* fix: remove dead code, fix double Load and extractParams mismatch
- Remove componentRegistry (written, never read)
- Remove unused signal methods: Bytes, Int64, Float
- Remove unreachable nil check in registerCtx
- Simplify injectRouteParams (extractParams already returns fresh map)
- Fix double sync.Map.Load in injectSignals
- Merge Shutdown/shutdown into single method
- Inline currSessionNum
- Fix extractParams: mismatched literal segment now returns nil
- Minor: new(bytes.Buffer), go c.Sync(), genRandID reads 4 bytes
One-liner static file serving: v.Static("/assets/", "./public") for
filesystem directories and v.StaticFS("/assets/", fsys) for embed.FS.
Both auto-normalize the URL prefix and disable directory listings.
Add per-context and per-action rate limiting using golang.org/x/time/rate.
Configure globally via Options.ActionRateLimit or per-action with
WithRateLimit(). Defaults to 10 req/s with burst of 20.
Generate a per-context CSRF token (128-bit, crypto/rand) and inject it
as a Datastar signal (via-csrf) alongside via-ctx. Validate with
constant-time comparison on /_action/{id} before executing, returning
403 on mismatch. Transparent to users since Datastar sends all signals
automatically.
Closes#9
Add typed Publish[T] and Subscribe[T] generic helpers that handle
JSON marshaling, along with vianats.EnsureStream and ReplayHistory
helpers. Refactor nats-chatroom to use the new APIs.
Add pubsub-crud example demonstrating CRUD operations with DaisyUI
toast notifications broadcast to all connected clients via NATS.
Add window-scoped keydown dispatching with per-key signal and
preventDefault options. Use comma operator instead of semicolons
in generated ternary expressions to produce valid JavaScript.
OnKeyDownMap merges multiple key bindings into a single
data-on:keydown__window attribute via a JS ternary chain,
avoiding HTML attribute deduplication. WithWindow scopes
any keydown listener to the window object.
Handle SIGINT/SIGTERM in Start() to cleanly drain all contexts,
stop goroutines, close SSE connections, and tear down PubSub.
Fix stopAllRoutines() to close() the channel instead of sending a
single value, so all listening goroutines are notified.
Switch from the standard library log package to rs/zerolog with
ConsoleWriter for colorful terminal output in dev mode and JSON
output in production. Users can now provide their own logger via
Options.Logger or set the level via Options.LogLevel.
Define PubSub and Subscription interfaces in the core via package with
a vianats sub-package providing the embedded NATS + JetStream
implementation. Expose c.Publish() and c.Subscribe() on Context with
automatic subscription cleanup on session close. Refactor the NATS
chatroom example to use the built-in methods instead of manual
subscription tracking.
Demonstrates pub/sub messaging as an alternative to custom Rooms
implementation. Uses delaneyj/toolbelt/embeddednats to run NATS
with JetStream inside the binary - no external server required.
Add NewSQLiteSessionManager helper that creates an SCS session manager
backed by SQLite, allowing sessions to persist across server restarts.
The function handles table creation automatically.
Previously only called Sync() on SSE reconnect (detected via last-event-id
header). This caused issues when application code registered contexts for
updates before the SSE connection was established - patches sent to
patchChan could be dropped.
Now always call Sync() when SSE connects, ensuring clients receive the
full current state regardless of what happened before the connection
was established.
Fixes#2
Flatten DatastarConfig struct into Options (DatastarContent, DatastarPath)
and replace datastarHandlerRegistered bool with sync.Once for thread safety.
Allow users to provide their own Datastar.js script (e.g., Datastar Pro
or custom builds) via Via's Options configuration. Adds DatastarConfig
struct with Content ([]byte) and Path (string) fields.