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.