HFU Digital Docs
CourseKitStarPlan

RRULE Normalization

Validate and reserialize RFC 5545 RRULE strings before storing them in CourseKit

This package does not expand recurrences itself — that's the job of RecurrenceService in @hfu.digital/coursekit-nestjs, which materializes occurrences from a stored recurrenceRule: string.

What this module does provide is a normalizeRrule round-trip — reparse the upstream RRULE through the rrule library and re-emit a canonical form. That ensures:

  • Invalid rules are rejected at ingestion time, not at materialization time
  • Rule strings stored in your DB are stable across upstream formatting changes (whitespace, ordering)

Usage

import { normalizeRrule, isValidRrule } from '@hfu.digital/coursekit-starplan';

const result = normalizeRrule('FREQ=WEEKLY;BYDAY=MO;COUNT=14');
// → { raw: 'FREQ=WEEKLY;BYDAY=MO;COUNT=14',
//     normalized: 'FREQ=WEEKLY;COUNT=14;BYDAY=MO' }

if (!result) {
    throw new Error('Invalid RRULE');
}

await db.timetableEvent.update({
    where: { id },
    data: { recurrenceRule: result.normalized },
});

// Or as a boolean check
if (isValidRrule(rule)) { /* ... */ }

API

function normalizeRrule(rule: string): NormalizedRecurrence | null;
function isValidRrule(rule: string): boolean;

interface NormalizedRecurrence {
    /** Original RRULE string (trimmed). */
    raw: string;
    /** Reserialized RRULE after a parse round-trip. Useful for storage. */
    normalized: string;
}

normalizeRrule returns null if the input is empty or fails to parse. The RRULE: prefix is optional on input — both 'FREQ=WEEKLY' and 'RRULE:FREQ=WEEKLY' are accepted, and the prefix is stripped from the normalized output.

Tips

  • Always normalize before writing — never trust upstream RRULEs to round-trip identically.
  • For per-pattern recipes (every other week, biweekly with skips, exam-week exceptions, etc.) see the RRULE Patterns guide.

On this page