Lifecycle and ownership
start, stop, close, and who owns transports and stores.
Lifecycle
| Call | Effect |
|---|---|
First mutation or start() | Restore persisted targets (once), start scheduler |
stop() | Pause scheduling; keep in-memory and stored targets |
close() | Drain work, stop, run onClose, close owned deps |
close() is idempotent. After stop(), the next mutation auto-starts again
without reloading the store. Calling start() while already running is safe.
await source.start();
await source.stop();
await source.close();Hooks onStart, onStop, and onClose run during those transitions. A hook
failure fails the lifecycle call.
Ownership
By default:
- Injected transports, receivers, and stores stay caller-owned
- The default memory store is source-owned
createSacnSourcefrom@helioslx/core/nodeowns its transport- Redis stores own clients they create from
url; injected clients are caller-owned unlesscloseClient: true
Transfer ownership explicitly:
const source = createSacnSource({
transport,
store,
ownsTransport: true,
ownsStore: true,
});The HTTP adapter owns neither the source nor the viewer.
Process signals (Node)
createSacnSource from @helioslx/core/node registers the source and, unless
you pass installProcessHandlers: false, installs process-wide SIGINT,
SIGTERM, and beforeExit handlers once. Those handlers close every registered
Node source.
Opt out when the host application already owns signal handling:
const source = createSacnSource({
name: "Host-managed",
installProcessHandlers: false,
});Shutdown timing
shutdownTimeoutMs (default 2000) bounds owned transport shutdown. Bound your
own process exit separately — an unawaited mutation can still be in flight when
the process dies.
close() stops package output. It does not guarantee a safe physical state on
fixtures. Configure receiver source-loss behavior, and send a deliberate
blackout if your venue requires it. See Safety.