API reference
Root module and Node, Redis, HTTP, and testing subpaths.
@helioslx/core is ESM-only. The root entry is runtime-neutral: contracts,
scheduling, validation, and memory stores. Integrations live on explicit
subpaths.
@helioslx/core
createSacnSource(options) / new SacnSource(options)
Creates an sACN output source. Construction does not start the scheduler.
Required
transport: OutputTransport
Optional
| Option | Default | Notes |
|---|---|---|
store | MemoryOutputStore | Output persistence |
clock | SystemClock | Monotonic clock |
logger | no-op | Structured logger |
activeFps | 44 | Rate while dirty/fading |
idleFps | 2 | Keepalive rate |
sendTimeoutMs | — | Per-send timeout |
shutdownTimeoutMs | 2000 | Owned transport shutdown bound |
maxOutputs | 1024 | Max universe/priority pairs |
ownsTransport | false | Close injected transport |
ownsStore | true / false | true for default memory store |
createId | — | UUID factory if needed |
onStart / onStop / onClose | — | Hook failures fail the call |
Methods
| Method | Notes |
|---|---|
universe(n, options?) | Cached handle for (n, priority). Options: priority (default 100), cid, sourceName, idleFps. First call wins defaults. |
start() | Restore once, start scheduling. Safe if already running. After stop(), resumes without reloading store. |
stop() | Pause scheduler; keep targets. Next mutation auto-starts. |
listUniverses() | Immutable snapshots by universe/priority. Does not auto-start. |
getTelemetry() | Synchronous immutable snapshot. |
subscribe(listener) | started, stopped, output-updated, output-cleared, closed. Returns unsubscribe. Listener failures are isolated. |
close() | Drain, stop, onClose, close owned deps. Idempotent. |
Mutations are serialized. Awaiting a mutation means the write is scheduled and persisted — not that a fade finished.
Universe
From source.universe(1, { priority: 100 }):
| Method | Auto-start | Notes |
|---|---|---|
setChannels(values) | yes | Snap map { 1: 255, … } |
fadeChannels(values, { durationMs }) | yes | Shared linear duration |
transition([{ channel, value, durationMs }, …]) | yes | Per-channel durations |
write(values, { durationMs? }) | yes | Exactly 512 values |
get() | no | Snapshot or null |
clear({ signal? }) | yes | Blackout + delete store record; false if absent |
Mutators accept optional signal for queue-time cancellation.
Addressing
OutputAddress:
universe:1..63999priority:0..200, default100
Channel maps: non-empty, keys 1..512, values 0..255. Durations: finite,
non-negative milliseconds.
Snapshots and telemetry
OutputSnapshot includes current/target state, last successfully sent frame,
identity, scheduler state, sequence, timing, active transition count, and last
transport error. Arrays are readonly snapshots.
EngineTelemetry covers scheduler state and aggregate send attempts, successes,
failures, retries, and timeouts. Process telemetry is Node-only
(getNodeRuntimeTelemetry).
ViewerService
new ViewerService({ receiver, … })Required: receiver: Receiver
Optional: store, clock, logger, ownsReceiver (default false),
ownsStore (owned only for default memory store), streamCapacity (32),
maxUniverses (256), maxListeners (256).
| Method | Notes |
|---|---|
start() | Restore selected universes |
setSelectedUniverses / addUniverse / removeUniverse | Membership + persist |
getSelectedUniverses() | Ordered selection |
subscribe(listener) | Sync callback; returns unsubscribe |
packets(capacity?) | Closeable async iterable |
getTelemetry() | Packets, drops/coalesces, selections, streams |
close() | Unsubscribe; close owned deps |
Streams keep at most one queued packet per universe. Newer replaces older. At capacity, oldest is dropped.
Stores and contracts
Root exports: MemoryOutputStore, MemoryViewerStore, OutputTransport,
OutputStore, Receiver, ViewerStore, Clock, Logger, SystemClock.
Errors
All extend SacnError with a stable string code:
| Class | When |
|---|---|
SacnValidationError | Bad universe, priority, channel, frame, fade, FPS, timeout, CID, name |
SacnLifecycleError | Invalid for current lifecycle or capacity |
TransportTimeoutError | Send exceeded bound |
TransportError | Transport failure |
PersistenceError | Storage failure or invalid stored data |
DependencyUnavailableError | Optional integration cannot be used |
Match on class or code, not message text.
@helioslx/core/node
| Export | Purpose |
|---|---|
NodeSacnTransport(options?) | UDP output; port 5568. Options: iface, port, sourceName, unicastDestination, logger, senderFactory. |
NodeSacnReceiver(options?) | Selected-universe receiver. Options: universes, iface, port, logger, receiverFactory. |
createSacnSource(options?) | Source + owned transport. Adds name, transportOptions, installProcessHandlers (default true). |
getNodeRuntimeTelemetry() | CPU, memory, PID, uptime |
With process handlers enabled, a module registry installs SIGINT / SIGTERM /
beforeExit once and closes registered Node sources. Pass
installProcessHandlers: false when the host owns signals.
Multicast destination comes from universe number. Unicast is transport-level, not per-universe.
@helioslx/core/redis
RedisOutputStore and RedisViewerStore accept a Redis client or connection
options, plus optional namespace (default helioslx), version, logger,
and closeClient. Injected clients are caller-owned by default. Records are
validated on load. See Persistence.
@helioslx/core/http
createSacnHttpAdapter(options) → unbound Elysia plugin.
Required: source
Optional: viewer, prefix (/sacn), auth, onViewerPacket, sunset,
openapiPath, maxWebSocketClients, webSocketQueueCapacity
Canonical paths use /universes/:universe/priorities/:priority. Deprecated
/outputs… aliases remain with deprecation headers. Full route table:
HTTP guide.
@helioslx/core/testing
FakeClock, RecordingTransport, FakeReceiver, and createDeferred for
deterministic tests without sockets.