cli/pkg/collect/events.go
Snider f2bc912ebe feat: infrastructure packages and lint cleanup (#281)
* ci: consolidate duplicate workflows and merge CodeQL configs

Remove 17 duplicate workflow files that were split copies of the
combined originals. Each family (CI, CodeQL, Coverage, PR Build,
Alpha Release) had the same job duplicated across separate
push/pull_request/schedule/manual trigger files.

Merge codeql.yml and codescan.yml into a single codeql.yml with
a language matrix covering go, javascript-typescript, python,
and actions — matching the previous default setup coverage.

Remaining workflows (one per family):
- ci.yml (push + PR + manual)
- codeql.yml (push + PR + schedule, all languages)
- coverage.yml (push + PR + manual)
- alpha-release.yml (push + manual)
- pr-build.yml (PR + manual)
- release.yml (tag push)
- agent-verify.yml, auto-label.yml, auto-project.yml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add collect, config, crypt, plugin packages and fix all lint issues

Add four new infrastructure packages with CLI commands:
- pkg/config: layered configuration (defaults → file → env → flags)
- pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums)
- pkg/plugin: plugin system with GitHub-based install/update/remove
- pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate)

Fix all golangci-lint issues across the entire codebase (~100 errcheck,
staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that
`core go qa` passes with 0 issues.

Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00

133 lines
3.1 KiB
Go

package collect
import (
"sync"
"time"
)
// Event types used by the collection subsystem.
const (
// EventStart is emitted when a collector begins its run.
EventStart = "start"
// EventProgress is emitted to report incremental progress.
EventProgress = "progress"
// EventItem is emitted when a single item is collected.
EventItem = "item"
// EventError is emitted when an error occurs during collection.
EventError = "error"
// EventComplete is emitted when a collector finishes its run.
EventComplete = "complete"
)
// Event represents a collection event.
type Event struct {
// Type is one of the Event* constants.
Type string `json:"type"`
// Source identifies the collector that emitted the event.
Source string `json:"source"`
// Message is a human-readable description of the event.
Message string `json:"message"`
// Data carries optional event-specific payload.
Data any `json:"data,omitempty"`
// Time is when the event occurred.
Time time.Time `json:"time"`
}
// EventHandler handles collection events.
type EventHandler func(Event)
// Dispatcher manages event dispatch. Handlers are registered per event type
// and are called synchronously when an event is emitted.
type Dispatcher struct {
mu sync.RWMutex
handlers map[string][]EventHandler
}
// NewDispatcher creates a new event dispatcher.
func NewDispatcher() *Dispatcher {
return &Dispatcher{
handlers: make(map[string][]EventHandler),
}
}
// On registers a handler for an event type. Multiple handlers can be
// registered for the same event type and will be called in order.
func (d *Dispatcher) On(eventType string, handler EventHandler) {
d.mu.Lock()
defer d.mu.Unlock()
d.handlers[eventType] = append(d.handlers[eventType], handler)
}
// Emit dispatches an event to all registered handlers for that event type.
// If no handlers are registered for the event type, the event is silently dropped.
// The event's Time field is set to now if it is zero.
func (d *Dispatcher) Emit(event Event) {
if event.Time.IsZero() {
event.Time = time.Now()
}
d.mu.RLock()
handlers := d.handlers[event.Type]
d.mu.RUnlock()
for _, h := range handlers {
h(event)
}
}
// EmitStart emits a start event for the given source.
func (d *Dispatcher) EmitStart(source, message string) {
d.Emit(Event{
Type: EventStart,
Source: source,
Message: message,
})
}
// EmitProgress emits a progress event.
func (d *Dispatcher) EmitProgress(source, message string, data any) {
d.Emit(Event{
Type: EventProgress,
Source: source,
Message: message,
Data: data,
})
}
// EmitItem emits an item event.
func (d *Dispatcher) EmitItem(source, message string, data any) {
d.Emit(Event{
Type: EventItem,
Source: source,
Message: message,
Data: data,
})
}
// EmitError emits an error event.
func (d *Dispatcher) EmitError(source, message string, data any) {
d.Emit(Event{
Type: EventError,
Source: source,
Message: message,
Data: data,
})
}
// EmitComplete emits a complete event.
func (d *Dispatcher) EmitComplete(source, message string, data any) {
d.Emit(Event{
Type: EventComplete,
Source: source,
Message: message,
Data: data,
})
}