This guide shows how to install RRStack, define rules, query coverage, and persist configuration. For a concise list of methods and types, see Core API and Types.
npm install @karmaniverous/rrstack
# or
yarn add @karmaniverous/rrstack
# or
pnpm add @karmaniverous/rrstack
import { RRStack } from '@karmaniverous/rrstack';
// Daily 05:00–06:00 active
const active = {
effect: 'active' as const,
duration: { hours: 1 },
options: {
freq: 'daily' as const,
byhour: [5],
byminute: [0],
bysecond: [0],
},
};
// Blackout slice 05:30–05:45 (overlays the active slice)
const blackout = {
effect: 'blackout' as const,
duration: { minutes: 15 },
options: {
freq: 'daily' as const,
byhour: [5],
byminute: [30],
bysecond: [0],
},
};
const stack = new RRStack({
timezone: 'America/Chicago',
rules: [active, blackout],
});
Tips
options.freq to declare a span).const now = Date.now();
const isActive = stack.isActiveAt(now); // boolean
const from = Date.UTC(2024, 0, 2, 5, 0, 0);
const to = Date.UTC(2024, 0, 2, 6, 0, 0);
for (const seg of stack.getSegments(from, to)) {
// { start: number; end: number; status: 'active' | 'blackout' }
}
[start, end).'s' (seconds) mode, end times are rounded up to the next integer second.const status = stack.classifyRange(from, to);
// 'active' | 'blackout' | 'partial'
const b = stack.getEffectiveBounds();
// { start?: number; end?: number; empty: boolean }
start omitted for open‑start coverage already active at the domain minimum.end omitted for open‑ended coverage.empty: true means “no active coverage exists”.const json = stack.toJson(); // round-trippable configuration (with version)
const again = new RRStack(json);
Not every schedule needs a recurrence. Omit options.freq to declare a span rule:
const span = {
effect: 'active' as const,
// duration omitted
options: {
starts: Date.UTC(2024, 0, 10, 5, 0, 0),
ends: Date.UTC(2024, 0, 10, 7, 0, 0),
},
};
Coverage is continuous across [starts, ends); either side may be open.
RRStack behaves as if a virtual, open‑ended span rule is prepended:
'auto' (default): opposite of the first rule’s effect, or 'active' when there are no rules.'active' | 'blackout': use exactly that baseline where no rule covers.This applies uniformly to isActiveAt, getSegments, classifyRange, and getEffectiveBounds. See details in Configuration & update().