ooo

package module
v0.0.0-...-6dbeb5f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2026 License: MIT Imports: 45 Imported by: 4

README

ooo logo

ooo

ooo UI demo

Test

State management with real-time network access.

ooo provides a fast, zero-configuration In-memory layer for storing and synchronizing application state or settings. It uses an embedded storage engine with optional persistence via ko, and delivers changes to subscribers using JSON Patch for efficient updates.

When to use ooo
  • Application state/settings that need real-time sync across clients
  • Prototyping real-time features quickly
  • Small to medium datasets where speed matters more than scale
When NOT to use ooo

For large-scale data storage (millions of records, complex queries), use a dedicated database like nopog. You can combine both: ooo for real-time state, nopog for bulk data.

Ecosystem

Package Description
ooo Core server - in-memory state with WebSocket/REST API
ko Persistent storage adapter (LevelDB)
ooo-client JavaScript client with reconnecting WebSocket
auth JWT authentication middleware
mono Full-stack boilerplate (Go + React)
nopog PostgreSQL adapter for large-scale storage
pivot Multi-instance synchronization (AP distributed)

Features

  • Dynamic routing with glob patterns for collections
  • Real-time subscriptions via WebSocket
  • JSON Patch updates for efficient sync
  • Version checking (no data sent on version match while reconnecting)
  • RESTful CRUD reflected to subscribers
  • Filtering and audit middleware
  • Auto-managed timestamps (created, updated) with a monotonic clock for consistency on ntp/ptp synchronizations
  • Built-in web UI for data management

quickstart

client

There's a js client.

server

with go installed get the library

go get github.com/benitogf/ooo

create a file main.go

package main

import "github.com/benitogf/ooo"

func main() {
  server := ooo.Server{}
  server.Start("0.0.0.0:8800")
  server.WaitClose()
}

run the service:

go run main.go

API Reference

UI & Management
Method Description URL
GET Web interface http://{host}:{port}/
GET List all keys (paginated) http://{host}:{port}/?api=keys
GET Server info http://{host}:{port}/?api=info
GET Filter paths http://{host}:{port}/?api=filters
GET Connection state http://{host}:{port}/?api=state
Keys API Query Parameters
Parameter Description Default
page Page number (1-indexed) 1
limit Items per page (max 500) 50
filter Filter by prefix or glob pattern (none)
Data Operations
Method Description URL
POST Create/Update http://{host}:{port}/{key}
GET Read http://{host}:{port}/{key}
PATCH Partial update (JSON Patch) http://{host}:{port}/{key}
DELETE Delete http://{host}:{port}/{key}
WebSocket
Method Description URL
WS Server clock ws://{host}:{port}
WS Subscribe to path ws://{host}:{port}/{key}

control

static routes

Activating this flag will limit the server to process requests defined by filters

server := ooo.Server{}
server.Static = true
Filters

Filters control access and transform data. When Static mode is enabled, only filtered routes are available.

Paths support glob patterns (*) and multi-level globs like users/*/posts/*.

Filter Description
OpenFilter Enable route (required in static mode)
WriteFilter Transform/validate before write
AfterWriteFilter Callback after write completes
ReadObjectFilter Transform single object on read
ReadListFilter Transform list items on read
DeleteFilter Control delete operations
LimitFilter Maintain max entries in a list (auto-cleanup)
OpenFilter
// Enable a route (required when Static=true)
server.OpenFilter("books/*")
WriteFilter
// Validate/transform before write
server.WriteFilter("books/*", func(index string, data json.RawMessage) (json.RawMessage, error) {
    // return error to deny, or modified data
    return data, nil
})
AfterWriteFilter
// Callback after write completes
server.AfterWriteFilter("books/*", func(index string) {
    log.Println("wrote:", index)
})
ReadObjectFilter
// Transform single object on read
server.ReadObjectFilter("books/special", func(index string, data meta.Object) (meta.Object, error) {
    return data, nil
})
ReadListFilter
// Transform list items on read
server.ReadListFilter("books/*", func(index string, items []meta.Object) ([]meta.Object, error) {
    return items, nil
})
DeleteFilter
// Control delete (return error to prevent)
server.DeleteFilter("books/protected", func(key string) error {
    return errors.New("cannot delete")
})
LimitFilter

LimitFilter is implemented using a ReadListFilter (to limit visible items), a noop WriteFilter (to allow writes), a DeleteFilter (to allow deletes), and an AfterWriteFilter (to trigger cleanup). This means it includes open read and write access.

// Limit list to N most recent entries (auto-deletes oldest)
server.LimitFilter("logs/*", filters.LimitFilterConfig{Limit: 100})

// With ascending order (oldest first in results)
server.LimitFilter("events/*", filters.LimitFilterConfig{
    Limit: 50,
    Order: filters.OrderAsc,
})

LimitFilterConfig options:

  • Limit - Maximum number of entries (required)
  • Order - Sort order: OrderDesc (default, most recent first) or OrderAsc (oldest first)
Audit
server.Audit = func(r *http.Request) bool {
    // return true to allow, false to deny (401)
    return r.Header.Get("X-API-Key") == "secret"
}
Custom Endpoints

Register custom HTTP endpoints with typed schemas visible in the UI.

server.Endpoint(ooo.EndpointConfig{
    Path:        "/policies/{id}",
    Description: "Manage access control policies",
    // Vars are route variables (mandatory) - auto-extracted from {id} in path
    Vars: ooo.Vars{"id": "Policy ID"},
    Methods: ooo.Methods{
        "GET": ooo.MethodSpec{
            Response: PolicyResponse{},
            // Params are query parameters (optional) - per method
            Params: ooo.Params{"filter": "Optional filter value"},
        },
        "PUT": ooo.MethodSpec{
            Request:  Policy{},
            Response: PolicyResponse{},
        },
    },
    Handler: func(w http.ResponseWriter, r *http.Request) {
        id := mux.Vars(r)["id"]           // Route variable (mandatory)
        filter := r.URL.Query().Get("filter") // Query param (optional)
        // ... handle request
    },
})
Proxies

Forward filters from remote ooo servers with path remapping.

// Proxy /settings/{deviceID} → /settings on remote
proxy.Route(server, "settings/*", proxy.Config{
    Resolve: func(localPath string) (address, remotePath string, err error) {
        return "localhost:8800", "settings", nil
    },
})

// Proxy list routes: /items/{deviceID}/* → /items/* on remote
proxy.RouteList(server, "items/*/*", proxy.Config{
    Resolve: func(localPath string) (address, remotePath string, err error) {
        parts := strings.SplitN(localPath, "/", 3)
        if len(parts) == 3 {
            return "localhost:8800", "items/" + parts[2], nil
        }
        return "localhost:8800", "items/*", nil
    },
})

I/O Operations

These functions handle JSON serialization/deserialization and provide a more convenient way to work with your data structures than using storage api directly.

Basic Operations
Get a Single Item
// Get retrieves a single item from the specified path
item, err := ooo.Get[YourType](server, "path/to/item")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Item: %+v\n", item.Data)
Get a List of Items
// GetList retrieves all items from a list path (ends with "/*")
items, err := ooo.GetList[YourType](server, "path/to/items/*")
if err != nil {
    log.Fatal(err)
}
for _, item := range items {
    fmt.Printf("Item: %+v (created: %v)\n", item.Data, item.Created)
}
Set an Item
// Set creates or updates an item at the specified path
err := ooo.Set(server, "path/to/item", YourType{
    Field1: "value1",
    Field2: "value2",
})
if err != nil {
    log.Fatal(err)
}
Add to a List
// Push adds an item to a list (path must end with "/*")
index, err := ooo.Push(server, "path/to/items/*", YourType{
    Field1: "new item",
    Field2: "another value",
})
if err != nil {
    log.Fatal(err)
}
fmt.Println("Created at:", index)
Delete Item(s)
// Delete removes item(s) at the specified path
// For single item: ooo.Delete(server, "path/to/item")
// For glob pattern: ooo.Delete(server, "items/*") removes all matching items
err := ooo.Delete(server, "path/to/item")
if err != nil {
    log.Fatal(err)
}
Remote Operations

Perform operations on remote ooo servers using the io package.

RemoteConfig
cfg := io.RemoteConfig{
    Client: &http.Client{Timeout: 10 * time.Second},
    Host:   "localhost:8800",
    SSL:    false, // set to true for HTTPS
}
RemoteGet
item, err := io.RemoteGet[YourType](cfg, "path/to/item")
RemoteSet
err := io.RemoteSet(cfg, "path/to/item", YourType{Field1: "value"})
RemotePush
err := io.RemotePush(cfg, "path/to/items/*", YourType{Field1: "new item"})
RemoteGetList
items, err := io.RemoteGetList[YourType](cfg, "path/to/items/*")
RemoteDelete
err := io.RemoteDelete(cfg, "path/to/item")
Subscribe Clients

Use the Go WebSocket client to subscribe to real-time updates.

SubscribeList
go client.SubscribeList(client.SubscribeConfig{
    Ctx:  ctx,
    Server: client.Server{Protocol: "ws", Host: "localhost:8800"},
}, "items/*", client.SubscribeListEvents[Item]{
    OnMessage: func(items []client.Meta[Item]) { /* handle updates */ },
    OnError:   func(err error) { /* handle error */ },
})
Subscribe
go client.Subscribe(client.SubscribeConfig{
    Ctx:  ctx,
    Server: client.Server{Protocol: "ws", Host: "localhost:8800"},
}, "config", client.SubscribeEvents[Config]{
    OnMessage: func(item client.Meta[Config]) { /* handle updates */ },
    OnError:   func(err error) { /* handle error */ },
})
SubscribeMultipleList2
go client.SubscribeMultipleList2(
    client.SubscribeConfig{
        Ctx:  ctx,
        Server: client.Server{Protocol: "ws", Host: "localhost:8800"},
    },
    [2]string{"products/*", "orders/*"},
    client.SubscribeMultipleList2Events[Product, Order]{
        OnMessage: func(products client.MultiState[Product], orders client.MultiState[Order]) {
            // products.Updated / orders.Updated indicates which changed
        },
    },
)

For JavaScript, use ooo-client.

UI

ooo includes a built-in web-based ui to manage and monitor your data. The ui is automatically available at the root path (/) when the server starts.

Features
  • Storage Browser - Browse all registered filters and their data
  • Live Mode - Real-time WebSocket subscriptions with automatic updates
  • Static Mode - Traditional CRUD operations with JSON editor
  • State Monitor - View active WebSocket connections and subscriptions
  • Filter Management - Visual representation of filter types (open, read-only, write-only, custom, limit)

Documentation

Index

Examples

Constants

View Source
const (
	OrderDesc = filters.OrderDesc // Most recent first (default)
	OrderAsc  = filters.OrderAsc  // Oldest first
)

Order constants for LimitFilterConfig

Variables

View Source
var (
	ErrInvalidPath        = errors.New("ooo: invalid path")
	ErrNotFound           = errors.New("ooo: not found")
	ErrNoop               = errors.New("ooo: noop")
	ErrGlobNotAllowed     = errors.New("ooo: glob pattern not allowed for this operation")
	ErrGlobRequired       = errors.New("ooo: glob pattern required for this operation")
	ErrInvalidStorageData = errors.New("ooo: invalid storage data (empty)")
	ErrInvalidPattern     = errors.New("ooo: invalid pattern")
	ErrInvalidRange       = errors.New("ooo: invalid range")
	ErrInvalidLimit       = errors.New("ooo: invalid limit")
	ErrLockNotFound       = errors.New("ooo: lock not found can't unlock")
	ErrCantLockGlob       = errors.New("ooo: can't lock a glob pattern path")
)

Storage errors

View Source
var (
	ErrServerAlreadyActive = errors.New("ooo: server already active")
	ErrServerStartFailed   = errors.New("ooo: server start failed")
	ErrForcePatchConflict  = errors.New("ooo: ForcePatch and NoPatch cannot both be enabled")
	ErrNegativeWorkers     = errors.New("ooo: Workers cannot be negative")
	ErrNegativeDeadline    = errors.New("ooo: Deadline cannot be negative")
)

Server errors

View Source
var (
	ErrNotAuthorized = errors.New("ooo: request is not authorized")
	ErrInvalidKey    = errors.New("ooo: key is not valid")
	ErrEmptyKey      = errors.New("ooo: empty key")
)

REST/HTTP errors

View Source
var (
	ErrRouteNotDefined     = errors.New("ooo: route not defined, static mode")
	ErrInvalidFilterResult = errors.New("ooo: invalid filter result")
	ErrReservedPath        = errors.New("ooo: filter path conflicts with reserved UI paths")
)

Filter errors

View Source
var (
	NoopHook         = filters.NoopHook
	NoopNotify       = filters.NoopNotify
	NoopFilter       = filters.NoopFilter
	NoopObjectFilter = filters.NoopObjectFilter
	NoopListFilter   = filters.NoopListFilter
)

Re-export filter functions from filters package

View Source
var (
	ErrPathGlobRequired   = errors.New("io: path glob required")
	ErrPathGlobNotAllowed = errors.New("io: path glob not allowed")
)
View Source
var TEST_DATA = json.RawMessage(`{
	"statuses": [
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Mon Sep 24 03:35:21 +0000 2012",
		"id_str": "250075927172759552",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Aggressive Ponytail #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 250075927172759552,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDEEF6",
		  "profile_sidebar_border_color": "C0DEED",
		  "profile_background_tile": false,
		  "name": "Sean Cummings",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "created_at": "Mon Apr 26 06:01:55 +0000 2010",
		  "location": "LA, CA",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "137238150",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": true,
		  "contributors_enabled": false,
		  "favourites_count": 0,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "utc_offset": -28800,
		  "id": 137238150,
		  "profile_use_background_image": true,
		  "listed_count": 2,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 70,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png",
		  "profile_background_color": "C0DEED",
		  "verified": false,
		  "geo_enabled": true,
		  "time_zone": "Pacific Time (US & Canada)",
		  "description": "Born 330 Live 310",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png",
		  "statuses_count": 579,
		  "friends_count": 110,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "sean_cummings"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//itunes.apple.com/us/app/twitter/id409789998?mt=12%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for Mac</a>",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:40:54 +0000 2012",
		"id_str": "249292149810667520",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "FreeBandNames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Thee Namaste Nerdz. #FreeBandNames",
		"metadata": {
		  "iso_language_code": "pl",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249292149810667520,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDFFCC",
		  "profile_sidebar_border_color": "BDDCAD",
		  "profile_background_tile": true,
		  "name": "Chaz Martenstein",
		  "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "created_at": "Tue Apr 07 19:05:07 +0000 2009",
		  "location": "Durham, NC",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "29516238",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://bullcityrecords.com/wnng/",
				  "indices": [
					0,
					32
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 8,
		  "url": "http://bullcityrecords.com/wnng/",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "utc_offset": -18000,
		  "id": 29516238,
		  "profile_use_background_image": true,
		  "listed_count": 118,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 2052,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "profile_background_color": "9AE4E8",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "statuses_count": 7579,
		  "friends_count": 348,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "bullcityrecords"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:30:20 +0000 2012",
		"id_str": "249289491129438208",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				29,
				43
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Mexican Heaven, Mexican Hell #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249289491129438208,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "99CC33",
		  "profile_sidebar_border_color": "829D5E",
		  "profile_background_tile": false,
		  "name": "Thomas John Wakeman",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "created_at": "Tue Sep 01 21:21:35 +0000 2009",
		  "location": "Kingston New York",
		  "follow_request_sent": null,
		  "profile_link_color": "D02B55",
		  "is_translator": false,
		  "id_str": "70789458",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 19,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "utc_offset": -18000,
		  "id": 70789458,
		  "profile_use_background_image": true,
		  "listed_count": 1,
		  "profile_text_color": "3E4415",
		  "lang": "en",
		  "followers_count": 63,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif",
		  "profile_background_color": "352726",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif",
		  "statuses_count": 1048,
		  "friends_count": 63,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "MonkiesFist"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 22:51:18 +0000 2012",
		"id_str": "249279667666817024",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "The Foolish Mortals #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249279667666817024,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "BFAC83",
		  "profile_sidebar_border_color": "615A44",
		  "profile_background_tile": true,
		  "name": "Marty Elmer",
		  "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "created_at": "Mon May 04 00:05:00 +0000 2009",
		  "location": "Wisconsin, USA",
		  "follow_request_sent": null,
		  "profile_link_color": "3B2A26",
		  "is_translator": false,
		  "id_str": "37539828",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://www.omnitarian.me",
				  "indices": [
					0,
					24
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 647,
		  "url": "http://www.omnitarian.me",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "utc_offset": -21600,
		  "id": 37539828,
		  "profile_use_background_image": true,
		  "listed_count": 52,
		  "profile_text_color": "000000",
		  "lang": "en",
		  "followers_count": 608,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "profile_background_color": "EEE3C4",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Central Time (US & Canada)",
		  "description": "Cartoonist, Illustrator, and T-Shirt connoisseur",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "statuses_count": 3575,
		  "friends_count": 249,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "Omnitarian"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//twitter.com/download/iphone%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for iPhone</a>",
		"in_reply_to_status_id": null
	  }
	],
	"search_metadata": {
	  "max_id": 250126199840518145,
	  "since_id": 24012619984051000,
	  "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
	  "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
	  "count": 4,
	  "completed_in": 0.035,
	  "since_id_str": "24012619984051000",
	  "query": "%23freebandnames",
	  "max_id_str": "250126199840518145",
	  "something": "something 🧰"
	}
  }`)

https://gist.github.com/slaise/9b9d63e0d59e8c8923bbd9d53f5beb61 https://medium.com/geekculture/my-golang-json-evaluation-20a9ca6ef79c

View Source
var TEST_DATA_UPDATE = json.RawMessage(`{
	"statuses": [
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Mon Sep 24 03:35:21 +0000 2012",
		"id_str": "250075927172759552",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Aggressive Ponytail #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 250075927172759552,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDEEF6",
		  "profile_sidebar_border_color": "C0DEED",
		  "profile_background_tile": false,
		  "name": "Sean Cummings",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "created_at": "Mon Apr 26 06:01:55 +0000 2010",
		  "location": "LA, CA",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "137238150",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": true,
		  "contributors_enabled": false,
		  "favourites_count": 0,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "utc_offset": -28800,
		  "id": 137238150,
		  "profile_use_background_image": true,
		  "listed_count": 2,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 70,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png",
		  "profile_background_color": "C0DEED",
		  "verified": false,
		  "geo_enabled": true,
		  "time_zone": "Pacific Time (US & Canada)",
		  "description": "Born 330 Live 310",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png",
		  "statuses_count": 579,
		  "friends_count": 110,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "sean_cummings"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//itunes.apple.com/us/app/twitter/id409789998?mt=12%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for Mac</a>",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:40:54 +0000 2012",
		"id_str": "249292149810667520",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "FreeBandNames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Thee Namaste Nerdz. #FreeBandNames",
		"metadata": {
		  "iso_language_code": "pl",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249292149810667520,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDFFCC",
		  "profile_sidebar_border_color": "BDDCAD",
		  "profile_background_tile": true,
		  "name": "Chaz Martenstein",
		  "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "created_at": "Tue Apr 07 19:05:07 +0000 2009",
		  "location": "Durham, NC",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "29516238",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://bullcityrecords.com/wnng/",
				  "indices": [
					0,
					32
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 8,
		  "url": "http://bullcityrecords.com/wnng/",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "utc_offset": -18000,
		  "id": 29516238,
		  "profile_use_background_image": true,
		  "listed_count": 118,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 2052,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "profile_background_color": "9AE4E8",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "statuses_count": 7579,
		  "friends_count": 348,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "bullcityrecords"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:30:20 +0000 2012",
		"id_str": "249289491129438208",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				29,
				43
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Mexican Heaven, Mexican Hell #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249289491129438208,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "99CC33",
		  "profile_sidebar_border_color": "829D5E",
		  "profile_background_tile": false,
		  "name": "Thomas John Wakeman",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "created_at": "Tue Sep 01 21:21:35 +0000 2009",
		  "location": "Kingston New York",
		  "follow_request_sent": null,
		  "profile_link_color": "D02B55",
		  "is_translator": false,
		  "id_str": "70789458",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 19,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "utc_offset": -18000,
		  "id": 70789458,
		  "profile_use_background_image": true,
		  "listed_count": 1,
		  "profile_text_color": "3E4415",
		  "lang": "en",
		  "followers_count": 63,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif",
		  "profile_background_color": "352726",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif",
		  "statuses_count": 1048,
		  "friends_count": 63,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "MonkiesFist"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 22:51:18 +0000 2012",
		"id_str": "249279667666817024",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "The Foolish Mortals #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249279667666817024,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "BFAC83",
		  "profile_sidebar_border_color": "615A44",
		  "profile_background_tile": true,
		  "name": "Marty Elmer",
		  "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "created_at": "Mon May 04 00:05:00 +0000 2009",
		  "location": "Wisconsin, USA",
		  "follow_request_sent": null,
		  "profile_link_color": "3B2A26",
		  "is_translator": false,
		  "id_str": "37539828",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://www.omnitarian.me",
				  "indices": [
					0,
					24
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 647,
		  "url": "http://www.omnitarian.me",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "utc_offset": -21600,
		  "id": 37539828,
		  "profile_use_background_image": true,
		  "listed_count": 52,
		  "profile_text_color": "000000",
		  "lang": "en",
		  "followers_count": 608,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "profile_background_color": "EEE3C4",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Central Time (US & Canada)",
		  "description": "Cartoonist, Illustrator, and T-Shirt connoisseur",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "statuses_count": 3575,
		  "friends_count": 249,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "Omnitarian"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//twitter.com/download/iphone%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for iPhone</a>",
		"in_reply_to_status_id": null
	  }
	],
	"search_metadata": {
	  "max_id": 250126199840518145,
	  "since_id": 24012619984051000,
	  "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
	  "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
	  "count": 4,
	  "completed_in": 0.035,
	  "since_id_str": "24012619984051000",
	  "query": "%23freebandnames",
	  "max_id_str": "250126199840518145",
	  "something": "something else 🧰"
	}
  }`)

Functions

func ClientCompatibilityTest

func ClientCompatibilityTest(t *testing.T, server *Server)

ClientCompatibilityTest covers the key behaviors expected by ooo-client: 1. Object lifecycle: create (updated=0) → update (updated>0) → delete (empty object) 2. List lifecycle: create → update → delete single item 3. Glob delete: multiple items deleted with single broadcast returning empty list 4. List sort order: newest first (descending by created) 5. Nested paths: box/*/things/* pattern matching

func Delete

func Delete(server *Server, path string) error

Delete removes an item at the specified path from storage. The path must not contain glob patterns.

func Get

func Get[T any](server *Server, path string) (client.Meta[T], error)

func GetList

func GetList[T any](server *Server, path string) ([]client.Meta[T], error)

func Patch

func Patch[T any](server *Server, path string, item T) error

Patch applies a partial update to an existing item at the specified path. The path must not contain glob patterns and the item must already exist. The patch is merged with the existing data using JSON merge semantics.

func Push

func Push[T any](server *Server, path string, item T) (string, error)

func Set

func Set[T any](server *Server, path string, item T) error

func StorageAfterWriteTest

func StorageAfterWriteTest(db storage.Database, t *testing.T)

StorageAfterWriteTest tests that the AfterWrite callback is called on write operations

func StorageBatchSetTest

func StorageBatchSetTest(server *Server, t *testing.T, n int)

func StorageBeforeReadTest

func StorageBeforeReadTest(db storage.Database, t *testing.T)

StorageBeforeReadTest tests that the BeforeRead callback is called on read operations

func StorageGetNRangeTest

func StorageGetNRangeTest(server *Server, t *testing.T, n int)

StorageGetNRangeTest testing storage GetN function

func StorageGetNTest

func StorageGetNTest(server *Server, t *testing.T, n int)

StorageGetNTest testing storage GetN function

func StorageKeysRangeTest

func StorageKeysRangeTest(server *Server, t *testing.T, n int)

StorageKeysRangeTest testing storage KeysRange function

func StorageListTest

func StorageListTest(server *Server, t *testing.T)

StorageListTest testing storage function

func StorageObjectTest

func StorageObjectTest(server *Server, t *testing.T)

StorageObjectTest testing storage function

func StorageSetGetDelTestBenchmark

func StorageSetGetDelTestBenchmark(db storage.Database, b *testing.B)

StorageSetGetDelTest testing storage function

func StreamBroadcastFilterTest

func StreamBroadcastFilterTest(t *testing.T, server *Server)

StreamBroadcastFilterTest testing stream function Note: This test uses raw websocket to verify snapshot/patch protocol behavior

func StreamBroadcastForcePatchTest

func StreamBroadcastForcePatchTest(t *testing.T, server *Server)

StreamBroadcastForcePatchTest testing stream function Note: This test uses raw websocket to verify ForcePatch behavior (no snapshots after first)

func StreamBroadcastNoPatchTest

func StreamBroadcastNoPatchTest(t *testing.T, server *Server)

StreamBroadcastNoPatchTest testing stream function Note: This test uses raw websocket to verify NoPatch behavior (all messages are snapshots)

func StreamBroadcastPatchTest

func StreamBroadcastPatchTest(t *testing.T, server *Server)

func StreamBroadcastTest

func StreamBroadcastTest(t *testing.T, server *Server)

StreamBroadcastTest testing stream function Note: Uses raw websocket to verify patch protocol and meta.Object structure

func StreamGlobBroadcastConcurrentTest

func StreamGlobBroadcastConcurrentTest(t *testing.T, server *Server, n int)

func StreamGlobBroadcastTest

func StreamGlobBroadcastTest(t *testing.T, server *Server, n int)

StreamGlobBroadcastTest testing stream function Note: This test uses raw websocket to verify patch/snapshot protocol behavior

func StreamItemGlobBroadcastTest

func StreamItemGlobBroadcastTest(t *testing.T, server *Server)

StreamItemGlobBroadcastTest testing stream function Note: Uses raw websocket to verify patch protocol and meta.Object structure

func StreamLimitFilterTest

func StreamLimitFilterTest(t *testing.T, server *Server)

StreamLimitFilterTest tests that the LimitFilter correctly maintains the limit when items are inserted and broadcast to subscribed clients. The client should never see more than the limit number of items due to ReadListFilter. Note: This test uses raw websocket to verify patch protocol and limit enforcement

func Time

func Time() string

Time returns a string timestamp using the monotonic clock

func WatchStorageNoopTest

func WatchStorageNoopTest(db storage.Database, t *testing.T)

WatchStorageNoopTest tests that WatchStorageNoop properly drains events from sharded channels

Types

type Apply

type Apply = filters.Apply

Re-export filter types from filters package

type ApplyList

type ApplyList = filters.ApplyList

Re-export filter types from filters package

type ApplyObject

type ApplyObject = filters.ApplyObject

Re-export filter types from filters package

type Block

type Block = filters.Block

Re-export filter types from filters package

type EndpointConfig

type EndpointConfig struct {
	Path        string
	Methods     Methods
	Description string
	Vars        Vars // Route variables like {id} - mandatory, auto-extracted from path if nil
	Handler     http.HandlerFunc
}

EndpointConfig configures a custom endpoint

type FetchResult

type FetchResult struct {
	Data    []byte
	Version int64
}

FetchResult holds the result of a fetch operation for initial WebSocket message

type FilterConfig

type FilterConfig = filters.Config

Re-export filter types from filters package

type LimitFilterConfig

type LimitFilterConfig = filters.LimitFilterConfig

LimitFilterConfig is an alias for filters.LimitFilterConfig for convenience. Use this with LimitFilter to configure limit and sort order.

type LimitFunc

type LimitFunc = filters.LimitFunc

LimitFunc is an alias for filters.LimitFunc for convenience. Use this to provide a dynamic limit function that is called each time the limit is needed.

type MethodSpec

type MethodSpec struct {
	Request  any    // Go type for request body, nil for GET/DELETE
	Response any    // Go type for response body, nil if status-only
	Params   Params // Query parameters like ?category=x - optional
}

MethodSpec defines the specification for an HTTP method

type Methods

type Methods map[string]MethodSpec

Methods maps HTTP method to its specification

type Notify

type Notify = filters.Notify

Re-export filter types from filters package

type Params

type Params map[string]string

Params maps query parameter name to its description (e.g., ?category=x)

type Server

type Server struct {
	Name   string
	Router *mux.Router
	Stream stream.Stream

	NoBroadcastKeys []string
	Audit           audit
	Workers         int
	ForcePatch      bool
	NoPatch         bool
	OnSubscribe     stream.Subscribe
	OnUnsubscribe   stream.Unsubscribe
	OnStart         func()
	OnClose         func()

	Deadline       time.Duration
	AllowedOrigins []string
	AllowedMethods []string
	AllowedHeaders []string
	ExposedHeaders []string
	Storage        storage.Database
	Address        string

	Silence           bool
	Static            bool
	Tick              time.Duration
	Console           *coat.Console
	Signal            chan os.Signal
	Client            *http.Client
	ReadTimeout       time.Duration
	WriteTimeout      time.Duration
	ReadHeaderTimeout time.Duration
	IdleTimeout       time.Duration
	OnStorageEvent    storage.EventCallback
	BeforeRead        func(key string)
	GetPivotInfo      func() *ui.PivotInfo // Optional: returns pivot status for UI
	// contains filtered or unexported fields
}

Server is the main application struct for the ooo server.

Name: display name for the server, shown in the storage explorer title

Router: can be predefined with routes and passed to be extended

Stream: manages WebSocket connections and broadcasts

NoBroadcastKeys: array of keys that should not broadcast on changes

Audit: function to audit requests, returns true to approve, false to deny

Workers: number of workers to use as readers of the storage->broadcast channel

ForcePatch: flag to force patch operations even if the patch is bigger than the snapshot

NoPatch: flag to disable patch operations entirely, always send full snapshots

OnSubscribe: function to monitor subscribe events, can return error to deny subscription

OnUnsubscribe: function to monitor unsubscribe events

OnStart: function that triggers after the server has started successfully

OnClose: function that triggers after closing the application

Deadline: time duration of a request before timing out

AllowedOrigins: list of allowed origins for cross domain access, defaults to ["*"]

AllowedMethods: list of allowed methods for cross domain access, defaults to ["GET", "POST", "DELETE", "PUT", "PATCH"]

AllowedHeaders: list of allowed headers for cross domain access, defaults to ["Authorization", "Content-Type"]

ExposedHeaders: list of exposed headers for cross domain access, defaults to nil

Storage: database interface implementation

Address: the address the server is listening on (populated after Start)

Silence: output silence flag, suppresses console output when true

Static: static routing flag, when true only filtered routes are allowed

Tick: time interval between ticks on the clock websocket

Console: logging console for the server

Signal: os signal channel for graceful shutdown

Client: http client to make requests

ReadTimeout: maximum duration for reading the entire request

WriteTimeout: maximum duration before timing out writes of the response

ReadHeaderTimeout: amount of time allowed to read request headers

IdleTimeout: maximum amount of time to wait for the next request

OnStorageEvent: callback function triggered on storage events

BeforeRead: callback function triggered before read operations

Example
package main

import (
	"github.com/benitogf/ooo"
)

func main() {
	app := ooo.Server{}
	app.Start("localhost:8800")
	app.WaitClose()
}

func (*Server) Active

func (server *Server) Active() bool

Active check if the server is active

func (*Server) AfterWriteFilter

func (server *Server) AfterWriteFilter(path string, apply Notify, cfg ...FilterConfig)

AfterWriteFilter add a filter that triggers after a successful write

func (*Server) Close

func (server *Server) Close(sig os.Signal)

Close : shutdown the http server and database connection

func (*Server) DeleteFilter

func (server *Server) DeleteFilter(path string, apply Block, cfg ...FilterConfig)

DeleteFilter add a filter that runs before delete

func (*Server) Endpoint

func (server *Server) Endpoint(cfg EndpointConfig)

Endpoint registers a custom HTTP endpoint with metadata for UI visibility

func (*Server) LimitFilter

func (server *Server) LimitFilter(path string, cfg filters.LimitFilterConfig)

LimitFilter creates a limit filter for a glob pattern path that maintains a maximum number of entries. Uses a ReadListFilter (meta-based) to limit the view (so clients never see more than limit items) and AfterWrite to delete old entries. Also adds write and delete filters to allow creating and deleting items.

func (*Server) OpenFilter

func (server *Server) OpenFilter(name string, cfg ...FilterConfig)

OpenFilter open noop read and write filters For glob paths like "things/*", this also enables reading individual items like "things/123"

func (*Server) ReadListFilter

func (server *Server) ReadListFilter(path string, apply ApplyList, cfg ...FilterConfig)

ReadListFilter add a filter for []meta.Object reads. For glob paths like "things/*", individual item reads (e.g., "things/123") will also be allowed if no explicit ReadObjectFilter is registered for that path.

func (*Server) ReadObjectFilter

func (server *Server) ReadObjectFilter(path string, apply ApplyObject, cfg ...FilterConfig)

ReadObjectFilter add a filter for single meta.Object reads

func (*Server) RegisterLimitFilter

func (server *Server) RegisterLimitFilter(lf *filters.LimitFilter, description string, schema map[string]any)

RegisterLimitFilter registers a limit filter and tracks it for the ui. The LimitFilter should already be created and its filters added to the server. This method stores a reference to the filter for lazy evaluation of dynamic limits.

func (*Server) RegisterPreClose

func (server *Server) RegisterPreClose(cleanup func())

RegisterPreClose registers a cleanup function to be called at the very start of Close(), before stream and storage cleanup. This is useful for stopping background goroutines that depend on the stream being active. Multiple functions can be registered and will be called in registration order.

func (*Server) RegisterProxy

func (server *Server) RegisterProxy(info ui.ProxyInfo)

RegisterProxy registers a proxy route for UI visibility

func (*Server) RegisterProxyCleanup

func (server *Server) RegisterProxyCleanup(cleanup func())

RegisterProxyCleanup registers a cleanup function to be called when the server closes. This is used by proxy routes to clean up their remote subscriptions.

func (*Server) Start

func (server *Server) Start(address string)

Start initializes and starts the http server and database connection. Panics if startup fails. Use StartWithError for error handling. If the server is already active, this is a no-op (does not panic).

func (*Server) StartWithError

func (server *Server) StartWithError(address string) error

StartWithError initializes and starts the http server and database connection. Returns an error if startup fails instead of calling log.Fatal.

func (*Server) Validate

func (server *Server) Validate() error

Validate checks the server configuration for common issues. Call this before Start() to catch configuration errors early.

func (*Server) WaitClose

func (server *Server) WaitClose()

WaitClose : Blocks waiting for SIGINT, SIGTERM, SIGKILL, SIGHUP

func (*Server) WriteFilter

func (server *Server) WriteFilter(path string, apply Apply, cfg ...FilterConfig)

WriteFilter add a filter that triggers on write

type Vars

type Vars map[string]string

Vars maps route variable name to its description (e.g., {id} in path)

Directories

Path Synopsis
samples
01_basic_server command
Package main demonstrates a basic ooo server setup.
Package main demonstrates a basic ooo server setup.
02_static_routes_filters_audit command
Package main demonstrates static routes, filters, and audit middleware.
Package main demonstrates static routes, filters, and audit middleware.
04_io_operations command
Package main demonstrates I/O operations with typed helpers.
Package main demonstrates I/O operations with typed helpers.
08_remote_io_operations command
Package main demonstrates remote I/O operations.
Package main demonstrates remote I/O operations.
09_custom_endpoints command
Package main demonstrates custom HTTP endpoints with server.Endpoint().
Package main demonstrates custom HTTP endpoints with server.Endpoint().
12_limit_filter command
Package main demonstrates the LimitFilter for capped collections.
Package main demonstrates the LimitFilter for capped collections.
13_limit_filter_with_validation command
Package main demonstrates LimitFilter with custom write validation.
Package main demonstrates LimitFilter with custom write validation.
Build script for ooo website Generates the samples section from the samples directory
Build script for ooo website Generates the samples section from the samples directory
preview command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL