Documentation Index
Fetch the complete documentation index at: https://abxbus.archivebox.io/llms.txt
Use this file to discover all available pages before exploring further.
Event patterns are shared across both APIs:
bus.on(pattern, handler) for subscriptions
bus.find(pattern, ...) for history/future lookup
Both accept the same pattern forms:
- event class
- string event type name
'*' wildcard (match everything)
Go and Rust use string event type names for low-level registration/lookup, plus typed helpers (OnTyped[...] in Go, EventSpec/BaseEvent in Rust) when you want static payload/result typing.
| Pattern | Matches | Best for |
|---|
Event class (UserActionEvent) | One concrete event type | Strong typing end-to-end |
String ('UserActionEvent') | Events by type name | Dynamic routing/config-driven keys; primary Go/Rust pattern form |
'*' | All event types | Global observers, logging, bridges |
.on(...) and .find(...) use the same pattern model
Use whichever operation you need, with the same pattern key:
- subscribe:
bus.on(UserActionEvent, handler)
- find by class:
await bus.find(UserActionEvent)
- find by string:
await bus.find('UserActionEvent')
- wildcard subscribe/find:
bus.on('*', ...), await bus.find('*', ...)
Examples
Python
TypeScript
Rust
Go
from typing import Any
from abxbus import BaseEvent, EventBus
class UserActionEvent(BaseEvent[str]):
action: str
bus = EventBus('AppBus')
async def on(event: UserActionEvent) -> str:
# event is strongly typed here
return f'action:{event.action}'
def on_by_name(event: BaseEvent[Any]) -> None:
# string patterns are looser; payload fields are not statically known
print('by-name', event.event_type, getattr(event, 'action', None))
# by-name UserActionEvent click
def on_any(event: BaseEvent[Any]) -> None:
print('wildcard', event.event_type)
# wildcard UserActionEvent
bus.on(UserActionEvent, on)
bus.on('UserActionEvent', on_by_name)
bus.on('*', on_any)
event = await bus.emit(UserActionEvent(action='click')).now()
result = await event.event_result()
typed_match = await bus.find(UserActionEvent) # UserActionEvent | None
named_match = await bus.find('UserActionEvent') # BaseEvent[Any] | None
wildcard_match = await bus.find('*', future=5) # BaseEvent[Any] | None
import { BaseEvent, EventBus } from 'abxbus'
import { z } from 'zod'
const UserActionEvent = BaseEvent.extend('UserActionEvent', {
action: z.string(),
event_result_type: z.string(),
})
const bus = new EventBus('AppBus')
bus.on(UserActionEvent, (event) => {
// event is strongly typed here
return `action:${event.action}`
})
bus.on('UserActionEvent', (event) => {
// string patterns are looser; event is BaseEvent-like at compile time
console.log('by-name', event.event_type)
return undefined
})
bus.on('*', (event) => {
console.log('wildcard', event.event_type)
return undefined
})
const typedMatch = await bus.find(UserActionEvent) // InstanceType<typeof UserActionEvent> | null
const namedMatch = await bus.find('UserActionEvent') // BaseEvent | null
const wildcardMatch = await bus.find('*', { future: 5 }) // BaseEvent | null
use abxbus_rust::{
event,
event_bus::EventBus,
BaseEvent,
};
use futures::executor::block_on;
event! {
struct UserActionEvent {
action: String,
event_result_type: String,
}
}
let bus = EventBus::new(Some("AppBus".to_string()));
bus.on(UserActionEvent, |event: UserActionEvent| async move {
Ok(format!("action:{}", event.action))
});
let event = bus.emit(UserActionEvent {
action: "click".to_string(),
..Default::default()
});
block_on(event.now());
let typed_match = block_on(bus.find::<UserActionEvent>(true, None));
let named_match = block_on(bus.find("UserActionEvent", true, None, None));
let wildcard_match = block_on(bus.find("*", false, Some(5.0), None));
package main
import (
"fmt"
abxbus "github.com/ArchiveBox/abxbus/abxbus-go/v2"
)
type UserActionPayload struct {
Action string `json:"action"`
}
func main() {
bus := abxbus.NewEventBus("AppBus", nil)
abxbus.OnTyped[UserActionPayload, string](
bus,
"UserActionEvent",
"on",
func(payload UserActionPayload) (string, error) {
return fmt.Sprintf("action:%s", payload.Action), nil
},
nil,
)
bus.On("UserActionEvent", "on_by_name", func(event *abxbus.BaseEvent) (any, error) {
fmt.Println("by-name", event.EventType, event.Payload["action"])
// by-name UserActionEvent click
return nil, nil
}, nil)
bus.On("*", "on_any", func(event *abxbus.BaseEvent) (any, error) {
fmt.Println("wildcard", event.EventType)
// wildcard UserActionEvent
return nil, nil
}, nil)
event, err := abxbus.NewBaseEventWithResult[UserActionPayload, string](
"UserActionEvent",
UserActionPayload{Action: "click"},
)
if err != nil {
panic(err)
}
_, err = bus.Emit(event).EventResult()
if err != nil {
panic(err)
}
namedMatch, err := bus.Find("UserActionEvent", nil, nil)
if err != nil {
panic(err)
}
wildcardMatch, err := bus.Find("*", nil, &abxbus.FindOptions{Future: 5.0})
if err != nil {
panic(err)
}
_, _ = namedMatch, wildcardMatch
}
Why event classes are preferred for typing
Event classes preserve the most useful static typing:
- handler input shape is specific (payload fields are known)
- event result typing stays aligned with
event_result_type / generic result type
.find(EventClass) returns the specific event type
String keys and '*' are intentionally looser:
- Python: treat as
BaseEvent[Any]
- TypeScript: typed as base
BaseEvent/unknown-oriented handler return checks
- Rust: typed handlers use
bus.on(MyEvent, ...); raw string/wildcard registration is reserved for low-level forwarding internals.
- Go: typed handlers use
OnTyped[...]; string/wildcard handlers receive *BaseEvent
Use string/wildcard patterns when you need dynamic behavior. Use classes whenever you want strict payload/result type hints through handlers and lookups.