Example: live.atOrBefore(new Time(t)). Event with the exact key, or the nearest earlier one.
Example: live.bisect(new Time(t)). Insertion index for key
in the sorted live buffer (binary search; O(log N)). Same shape
as Array.prototype semantics: returns the lowest index where
an event with key could be inserted while preserving order.
Events per second over the current buffer. Computed as
length / (timeRange / 1000). Returns 0 if the buffer is
empty or holds a single event (no time span to divide by).
Mirrors LiveView.eventRate; available directly on
LiveSeries for the buffer-as-window pattern where the user
doesn't want a separate windowed view.
Example: live.every(e => e.get('healthy')). True when every event matches.
Optionaloptions: { limit?: number }Example: live.find(e => e.get('value') > 0). First event matching the predicate, or undefined.
Example: live.includesKey(new Time(t)). True when an event with an exactly matching key exists.
Type-parameter order is <ByCol, K> (column name first, then
partition value type) so the explicit-arg form
partitionBy<'host'>('host') binds the literal to ByCol.
That preserves backwards compatibility with the v0.15.0 V8
workaround pattern (which used the explicit-arg form to force
column-literal narrowing through the fused-rolling typing
chain) — and matches what callers usually want when they reach
for the explicit form: declare the partition column.
K (the partition VALUE type) typically narrows from
groups; an explicit <ByCol, K> second arg is rare. If a
caller wants an explicit value union, the natural form is
partitionBy('host', { groups: [...] as const }).
Optionaloptions: LivePartitionedOptions<K>Example: live.pushJson(rows). Bulk JSON-shape ingest: takes
an array of JsonRowForSchema<S> (or the object-form variant),
parses each row through parseJsonRow (translates null
cells to undefined, parses the key into the right
Time/TimeRange/Interval instance), then dispatches to
LiveSeries.pushMany.
Closes the wire→push safety hole: a JsonRowForSchema<S> is
structurally typed against the schema (column count, value
shapes, null permissibility), so a column added or renamed in
the schema breaks the call site at compile time. The previous
live.push(row as never) workaround swallowed mismatches.
Pass a TimeZoneOptions second argument to disambiguate
local-calendar timestamp strings — same semantics as
TimeSeries.fromJSON's parse option, just inlined as
a sibling argument because pushJson has no input envelope
to attach a parse: key to.
Example: live.pushMany(rows). Array-form counterpart to
LiveSeries.push: takes a single ReadonlyArray<RowForSchema<S>>
instead of variadic args. Behavior is identical — same per-row
validation, same 'event' / 'batch' / 'evict' listener
semantics, same retention pass at the end.
Reach for pushMany over push(...rows) when ingesting a
snapshot or any large rows array — variadic spread allocates a
stack frame per element and can blow on multi-thousand-row
snapshots. push(...rows) itself is now a thin wrapper around
this method, so behavior between the two is intentionally
identical.
For JSON-shape rows arriving over the wire, prefer
LiveSeries.pushJson — it accepts the JSON envelope
(nulls, raw timestamps) and parses through parseJsonRow.
Commit granularity differs by backing. On the Event[]
backing each row is appended then its 'event' fires, so a
handler observes length grow row-by-row (1, 2, …) within one
pushMany, and a handler that throws mid-batch leaves only the
rows up to the throw committed. On the chunked columnar backing
(top-level strict time-keyed series) the whole batch is appended
as one chunk before any 'event' fires — so a handler sees the
full post-batch length for every event of the batch, and a
handler that throws mid-fan-out leaves the entire batch committed
(the chunk is already appended). Both leave length and ingested
mutually consistent after a throw; they differ only in how much of
the batch is committed. This is intrinsic to all-or-nothing
columnar append — per-row commit would reintroduce the per-row
Event cost the chunked backing exists to avoid. The cross-backing
contract callers can rely on: every successfully-ingested row fires
exactly one 'event', in order, before 'batch'/'evict'.
Streaming counterpart to batch series.reduce(mapping).
Reduces over the source's current buffer — every push
adds to the reducer state, every retention eviction removes.
The snapshot at any moment is the reduction over what's
currently retained.
Same mapping shape as aggregate / rolling; same trigger
options as rolling. The "window" here is implicit — it's
whatever the source retains. For an explicit time-bounded
window, use rolling(duration, mapping, opts) instead.
Returns a LiveSource<Out> whose schema is
[time, ...mappingColumns]. Composes with the rest of the
live operator surface.
Optionaloptions: LiveRollingOptionsOptionaloptions: LiveRollingOptionsKeyed-form fused multi-window rolling. Maintains N windows in one ingest pass over a single shared deque; emits one merged event per trigger boundary with all windows' columns concatenated.
Use this form when declaring multiple time-windows over the
same source — { '1m': statsMapping, '200ms': samplesMapping }.
Single-window cases keep using the (window, mapping, opts)
shape — both are equivalent for one window, but the legacy
shape is clearer.
Constraints: time-based windows only (object keys are
duration strings); per-window cadence is not supported (single
trigger applies to all windows; users wanting per-window
cadence fall back to two separate rolling() calls). See
PLAN.md "Fused multi-window rolling" for the full rationale.
Optionaloptions: LiveRollingOptionsBounded-memory stream sampling. Thins the event stream going to
downstream consumers without affecting this LiveSeries's own
length, at(i), listeners, or stats() counters.
v0.17.0 ships stride only on the live side — { stride: N },
deterministic 1-in-N, uniform-over-time. Reservoir sampling is
snapshot-side only on this release (TimeSeries.sample); see
SampleStrategy for the rationale (live reservoir's
Algorithm R replacement produces non-prefix evictions; the
existing live-eviction protocol is cutoff-based, so bridging
needs an exact-removal eviction channel arriving with the
streaming RFC's LiveChange model).
Returns a LiveView<S> so the chainable surface
(filter, rolling, reduce, select, …) is immediately
available downstream of the sample.
Multi-entity bias trap. Pre-partition live.sample({stride: N})
applied to a structured input stream (e.g., events arriving in
round-robin host order) silently keeps the same subset of
partitions and drops the rest. The safe shape is to chain after
partitionBy(...), which thins each partition's stream
independently:
// Safe by construction — per-partition counter is implicit
live.partitionBy('host').sample({ stride: 10 }).rolling('5m', m);
Same multi-entity consideration applies to rolling / aggregate /
fill / diff / rate / cumulative / pctChange / reduce:
every stateful live operator silently mixes data across entities
on a multi-entity stream unless scoped per-partition first.
Reducer outputs downstream of sample reflect the sampled
stream; multiply by stride to estimate true counts.
live.stats().ingested continues to count true throughput
upstream of any sample.
Example: live.some(e => e.get('healthy')). True when at least one event matches.
Pipeline stats snapshot — cumulative counters since construction plus current buffer state. Cheap O(1).
ingested: total events accepted (after validation +
#insert). Never decreases.evicted: total events removed from the buffer — by
retention OR by an explicit LiveSeries.clear call.
Both paths fire the 'evict' listener; this counter
matches that same fan-out. Never decreases.rejected: total events silently rejected (drop-mode
out-of-order arrivals). Strict / reorder modes throw on
rejection — those don't count here.length: current buffer size (= this.length).earliestTs / latestTs: timestamps of buffer ends, or
undefined if the buffer is empty.Use case: long-running pipelines that want headline counters
without wiring live.on('batch'/'evict') listeners by hand.
The gRPC experiment's manual-counter pattern is exactly this
shape.
Time span of the current buffer — last.begin() - first.begin()
in milliseconds. Returns 0 if the buffer is empty or holds a
single event. Useful for the "how much data am I holding right
now?" question that buffer-as-window users ask.
O(1) — reads first/last directly.
Example: live.toJSON(). JSON-shape snapshot of the current
buffer, suitable for sending over a WebSocket or any
JSON.stringify-friendly transport. Sugar over
live.toTimeSeries().toJSON(...).
Defaults to rowFormat: 'array' (tuple rows). Pass
{ rowFormat: 'object' } for schema-keyed object rows. The
return type narrows on the option so consumers don't need to
cast result.rows.
Pairs with LiveSeries.fromJSON for snapshot reconstruction; pairs with LiveSeries.pushJson for incremental wire ingest.
Optionaloptions: { rowFormat?: "array" }Example: live.toJSON(). JSON-shape snapshot of the current
buffer, suitable for sending over a WebSocket or any
JSON.stringify-friendly transport. Sugar over
live.toTimeSeries().toJSON(...).
Defaults to rowFormat: 'array' (tuple rows). Pass
{ rowFormat: 'object' } for schema-keyed object rows. The
return type narrows on the option so consumers don't need to
cast result.rows.
Pairs with LiveSeries.fromJSON for snapshot reconstruction; pairs with LiveSeries.pushJson for incremental wire ingest.
Example: live.toObjects(). Returns the current buffer as an
array of schema-keyed object rows — same shape as
TimeSeries.toObjects. Useful when callers want to read
by column name rather than tuple position; not the input form
to pushMany (which takes tuples).
Example: live.toRows(). Returns the current buffer as an
array of normalized typed-row tuples — the same shape
pushMany(rows) accepts. Codec-agnostic: each cell carries its
native runtime value (Time/TimeRange/Interval keys,
undefined for missing data, raw scalars for everything else),
so JSON.stringify is one option but not the only one — the
tuple is also what protobuf / msgpack consumers want before
encoding. For a wire-ready snapshot use LiveSeries.toJSON.
Optionalname: stringStaticfromExample: LiveSeries.fromJSON({ name, schema, rows }). Static
factory: builds a fresh LiveSeries from a JSON snapshot
envelope, parsing each row through parseJsonRow.
The retention/grace/ordering options on the second argument are passed through to the constructor; pass them when you want the reconstructed series to behave like its original (e.g. on a client reconnecting and rehydrating from a server snapshot).
Use parse: { timeZone } when JSON timestamps are local-
calendar strings — same semantics as TimeSeries.fromJSON.
Example:
live.atOrAfter(new Time(t)). Event with the exact key, or the nearest later one.