@karmaniverous/rrstack
    Preparing search index...

    @karmaniverous/rrstack

    RRStack

    npm version Node Current docs changelog license

    Timezone‑aware RRULE stacking engine for Node/TypeScript.

    RRStack lets you compose a prioritized stack of time‑based rules (using the battle‑tested rrule library) to compute whether a given instant is active or blackout, enumerate active/blackout segments over a window, classify ranges as active/blackout/partial, and derive effective active bounds. It handles real‑world timezone behavior, including DST transitions, by computing coverage in the rule’s IANA timezone.

    • Built on rrule for recurrence logic
    • Uses Luxon for timezone/DST‑correct duration arithmetic
    • Pure library surface (no I/O side effects)
    • JSON persistence and round‑tripping
    • Thoroughly tested (DST edge cases, monthly patterns, segment sweeps, notices)
    npm install @karmaniverous/rrstack
    # or
    yarn add @karmaniverous/rrstack
    # or
    pnpm add @karmaniverous/rrstack
    • ESM and CJS consumers are supported.
    • TypeScript typings are included.
    • Node >= 20.
    import { RRStack } from '@karmaniverous/rrstack';

    // 1) Define rules (JSON serializable)
    const rules = [
    // Daily 05:00–06:00 active
    {
    effect: 'active' as const,
    duration: { hours: 1 },
    options: {
    freq: 'daily',
    byhour: [5],
    byminute: [0],
    bysecond: [0],
    },
    label: 'daily-05',
    },
    // Blackout 05:30–05:45 (overrides active during that slice)
    {
    effect: 'blackout' as const,
    duration: { minutes: 15 },
    options: {
    freq: 'daily',
    byhour: [5],
    byminute: [30],
    bysecond: [0],
    },
    label: 'blk-0530-15m',
    },
    ];

    // 2) Create a stack
    const stack = new RRStack({
    timezone: 'America/Chicago',
    rules,
    });

    // 3) Point query: active?
    const t = Date.now();
    const isActive = stack.isActiveAt(t); // boolean

    // 4) Enumerate segments over a window (half-open [from, to))
    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' }
    }

    // 5) Classify a window
    const status = stack.classifyRange(from, to); // 'active' | 'blackout' | 'partial'

    // 6) Effective bounds (open-sided detection)
    const bounds = stack.getEffectiveBounds(); // { start?: number; end?: number; empty: boolean }

    // 7) Persist / restore
    const json = stack.toJson();
    const same = new RRStack(json);

    For symbol‑level documentation (methods, parameters, and return types with code signatures), visit the hosted API Reference.

    • Real‑world scheduling usually needs more than one RRULE. You have base activation windows, blackout exceptions, and occasional reactivations. RRStack provides a deterministic cascade: later rules override earlier ones at covered instants.
    • Time zones matter. We compute coverage in the rule’s IANA time zone with DST‑aware duration arithmetic (Luxon).
    • Half‑open intervals everywhere. All coverage follows [start, end). In seconds mode ('s'), ends are rounded up to the next second to avoid boundary false negatives.
    • JSON round‑trip. Store and reload stack configuration (with versioning and notices via update()).
    • Pure library surface. No I/O side effects; suitable for Node, browsers, and workers.

    BSD‑3‑Clause © Jason Williscroft


    Built for you with ❤️ on Bali! Find more great tools & templates on my GitHub Profile: https://github.com/karmaniverous