Reference
Functions
Section titled “Functions”build(document: YrnkDocument): Record<string, unknown>
The mirror image of parse: the typed model back to the document’s array-and-object representation (JSON.stringify the result for the wire form). Round-tripping is the identity — building a document parsed from the DSL yields the original spelling, structurally.
dateLiteralProblem
Section titled “dateLiteralProblem”dateLiteralProblem(value: string): string | null
Why the string is not a Yrnk date literal, or null when it is. The spelling is zero-padded YYYY-MM-DD, the date must exist in the proleptic Gregorian calendar, and years run 1–9999.
descriptionProblem
Section titled “descriptionProblem”descriptionProblem(value: string): string | null
Why the string cannot be a description, or null when it can: the label rules with a 1000 code point cap, and LF permitted as the one line break.
ensureResolvable
Section titled “ensureResolvable”ensureResolvable(document: YrnkDocument, schedules?: Iterable<YrnkSchedule>): void
Would every name these schedules write be answered by this document’s definitions and bindings? The same validation every query runs first, reachable on its own for a caller that wants a wiring mistake surfaced before a schedule is stored or a question is asked. Consults the definitions and the bindings’ names only and never invokes a resolver, so passing says the references are answerable, not what the answers will be.
hasMatchIn
Section titled “hasMatchIn”hasMatchIn(document: YrnkDocument, schedule: YrnkSchedule, after: YrnkInstant, through: YrnkInstant): boolean
Is there a scheduled point after after, through through? The substance of a firing decision — “is there a scheduled point after the previous run, through now?” maps onto it directly. A point exactly at after does not count (it was the previous judgment’s “now”, already counted); a point exactly at through counts in this judgment. An all-day occurrence counts while its day overlaps the period, however late in the day it is asked: a day is due for as long as it lasts.
isTimeLiteral
Section titled “isTimeLiteral”isTimeLiteral(value: string): boolean
Whether the string is a time literal: zero-padded HH:MM, 00:00 through 23:59. The end-of-day token “24:00” is not a time literal — it is legal only as a window end.
labelProblem
Section titled “labelProblem”labelProblem(value: string): string | null
Why the string cannot be a label, or null when it can: at least one non-whitespace character, at most 100 code points, and no control characters or invisible characters that can spoof what a reader sees.
matches
Section titled “matches”matches(document: YrnkDocument, schedule: YrnkSchedule, at: YrnkInstant): boolean
Is the given instant an occurrence of this schedule? For a timed occurrence the answer is instant equality — the given instant, ignoring anything finer than a second (no scheduled point is finer), equals the occurrence’s instant. An all-day occurrence matches on the day alone: yes for every instant whose local date, read in the document timezone, is that day.
Questions are asked per schedule; the top-level OR of the schedules list is composed by the caller (any for the judgments, a merge for the enumeration).
nameProblem
Section titled “nameProblem”nameProblem(name: string): string | null
Why the string cannot be a name, or null when it can: at least one non-whitespace character, none of RESERVED_WORDS, and no literal shape (digits only, HH:MM, or YYYY-MM-DD). The literal shapes matter for reading, not for tidiness: a date-list position tells its two forms apart by shape, so a date-shaped name would read as a date list of one, and a digits-only name would read as a day of month in the days axis.
occurrencesIn
Section titled “occurrencesIn”occurrencesIn(document: YrnkDocument, schedule: YrnkSchedule, from: YrnkInstant, through: YrnkInstant): YrnkOccurrence[]
Which occurrences lie from from through through (both boundary instants included)? Timed occurrences are answered as Temporal.ZonedDateTime on the document timezone’s clock, all-day occurrences as Temporal.PlainDate; the two kinds stay distinct, and the answer is in ascending order. Unlike the period judgment, an enumeration has no previous window: the caller names two instants, and both are part of what it names.
parse(input: string | unknown, options?: YrnkParseOptions): YrnkDocument
Parses a Yrnk document (a JSON string or a decoded value) into the typed model. Each schedule is delegated to the schedule parser; what can only be validated with the whole document and its definitions together — resolvability of every name, the data behind the built-in vocabulary, and the declarations the document makes — happens here. The returned document is deeply frozen: the model is data, and the queries trust it not to change underneath them.
timezoneProblem
Section titled “timezoneProblem”timezoneProblem(timezone: string): string | null
Why the string cannot be the document timezone, or null when it can. The spec limits timezone to IANA tz database names; Temporal also accepts fixed offsets as time zone identifiers, so those are told apart and rejected here. Backward links (Japan, US/Eastern) are tz database entries and pass.
windowProblem
Section titled “windowProblem”windowProblem(start: string, end: string): string | null
Why the values cannot be a time window [start, end), or null when they can: start is zero-padded HH:MM, end is HH:MM or the end-of-day token “24:00”, and start is strictly before end (a window crossing midnight cannot be written).
Classes
Section titled “Classes”YrnkError
Section titled “YrnkError”class YrnkError extends Error
Every failure this library reports. instanceof YrnkError answers “did Yarunoka reject this”, and code answers what kind of rejection it was. Environment problems (a missing Temporal) are thrown as plain Errors instead: they are breakage of the runtime the library stands on, not an answer about the input.
Members
Section titled “Members”override readonly name = 'YrnkError'readonly code: YrnkErrorCodeconstructor(code: YrnkErrorCode, message: string)
Constants
Section titled “Constants”RESERVED_WORDS
Section titled “RESERVED_WORDS”const RESERVED_WORDS: readonly string[] = [ // Calendar vocabulary (days) and the window vocabulary 'weekday', 'weekend', 'holiday', 'business_day', 'business_holiday', 'business_hour', // Day names 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', // Ordinal words '1st', '2nd', '3rd', '4th', '5th', 'last', // Special days 'last_day_of_month', // Structural words of shift / if 'not', 'prev', 'next', 'or_same', // Unit words of every 'hour', 'minute', 'second', 'day', // Structural keys of the document, schedules, and calendar 'version', 'timezone', 'resolvers', 'calendar', 'schedules', 'years', 'months', 'days', 'shift', 'if', 'times', 'allday', 'every', 'between', 'from', 'until', 'holidays', 'business_holidays', 'business_days', 'workweek', 'business_hours', 'date_sets', // The annotation fields 'label', 'description',]The words a name must not collide with. Deliberately duplicated content of the name enum in the spec’s primitives.schema.json; agreement is verified by a test.
SUPPORTED_VERSION
Section titled “SUPPORTED_VERSION”const SUPPORTED_VERSION = '1.0'
The spec version this implementation reads.
YrnkCalendar
Section titled “YrnkCalendar”type YrnkCalendar = { readonly holidays?: YrnkDateSet; readonly businessHolidays?: YrnkDateSet; readonly businessDays?: YrnkDateSet; readonly workweek?: readonly YrnkDayName[]; readonly businessHours?: readonly (readonly [string, string])[]; readonly dateSets: Readonly<Record<string, readonly string[]>>;};The definitions part. The built-in definitions carry the layer-model semantics; dateSets is the open namespace. undefined means “not defined” — distinct from an explicit empty list (the statement that there are no such days). Only an undefined workweek means the default (Mon–Fri) instead.
YrnkCalendarWord
Section titled “YrnkCalendarWord”type YrnkCalendarWord = | 'weekday' | 'weekend' | 'holiday' | 'business_day' | 'business_holiday';The five layer-model words. weekday / weekend ask the fixed calendar and consult no definition; holiday asks the holidays list alone; business_day / business_holiday are questions to the stacked conclusion of the layers.
YrnkDateSet
Section titled “YrnkDateSet”type YrnkDateSet = readonly string[] | string;A date-list position of the calendar: the list of date literals the document contains, or the name of what resolves it. The two forms are told apart by type, exactly as the DSL tells them apart by shape.
YrnkDayAtom
Section titled “YrnkDayAtom”type YrnkDayAtom = | YrnkDayCondition | { readonly kind: 'day-cycle'; readonly interval: number };An atom of the days enumeration.
YrnkDayCondition
Section titled “YrnkDayCondition”type YrnkDayCondition = | { readonly kind: 'month-day'; readonly day: number } | { readonly kind: 'weekday'; readonly day: YrnkDayName } | { readonly kind: 'ordinal-weekday'; readonly ordinal: YrnkOrdinal; readonly day: YrnkDayName } | { readonly kind: 'last-day-of-month' } | { readonly kind: 'calendar-word'; readonly word: YrnkCalendarWord } | { readonly kind: 'name'; readonly name: string };A day atom legal as a shift landing condition or an if condition — every atom except the day cycle, which counts from the schedule’s from and is allowed only in the days enumeration.
YrnkDayName
Section titled “YrnkDayName”type YrnkDayName = 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';YrnkDirection
Section titled “YrnkDirection”type YrnkDirection = 'prev' | 'next';YrnkDocument
Section titled “YrnkDocument”type YrnkDocument = { readonly version: string; readonly timezone: string; /** The names this document leaves to its host; empty when none */ readonly resolvers: readonly string[]; readonly calendar: YrnkCalendar; readonly schedules: readonly YrnkSchedule[]; readonly label?: string; readonly description?: string; readonly [parsedDocument]: true;};A parsed Yrnk document. The brand marks that the value went through parse — validated, normalized, and with its resolver bindings registered — telling it apart from raw JSON of the same shape at the type level.
YrnkErrorCode
Section titled “YrnkErrorCode”type YrnkErrorCode = /** The structure or a value of a Yrnk document violates the language */ | 'invalid-document' /** The document declares a spec version this implementation does not know */ | 'unsupported-version' /** A name collides with a reserved word or looks like a literal */ | 'reserved-name' /** A name is neither a date_sets entry nor declared under resolvers */ | 'undefined-name' /** A declared name has no resolver bound to it */ | 'unregistered-resolver' /** A value handed to the API violates its contract */ | 'invalid-value' /** What a resolver returned violates its contract */ | 'invalid-calendar-data' /** A calendar definition required by the vocabulary in use is missing */ | 'missing-calendar-data';What went wrong, as a closed set of kinds. One error class with a code rather than a class per kind: instanceof is brittle across realms and duplicated installs, so the discriminant is data and the class is only the catch-all handle.
YrnkIf
Section titled “YrnkIf”type YrnkIf = { readonly direction: YrnkDirection | null; readonly negated: boolean; readonly condition: YrnkDayCondition;};The if modifier — filtering by the base day itself or a neighbour. shift moves the day; if filters without moving. A null direction means “the day itself”.
YrnkInstant
Section titled “YrnkInstant”type YrnkInstant = Temporal.Instant | Temporal.ZonedDateTime | Date | string;An instant a query accepts: a Temporal value, a Date, or an ISO 8601 string with a UTC offset. The wire carries moments — a zone-name-only string names no moment and is rejected.
YrnkOccurrence
Section titled “YrnkOccurrence”type YrnkOccurrence = Temporal.PlainDate | Temporal.ZonedDateTime;An occurrence as a query answers it: a whole day (all-day) or an instant on the document timezone’s clock (timed). The two kinds never merge — a day and a timed point at its 00:00 are distinct occurrences, and the types carry that distinction.
YrnkOrdinal
Section titled “YrnkOrdinal”type YrnkOrdinal = '1st' | '2nd' | '3rd' | '4th' | '5th' | 'last';YrnkParseOptions
Section titled “YrnkParseOptions”type YrnkParseOptions = { /** What the host binds the document's declared resolver names to */ readonly resolvers?: Readonly<Record<string, YrnkResolver>>;};YrnkResolver
Section titled “YrnkResolver”type YrnkResolver = (range: { readonly from: Temporal.PlainDate; readonly through: Temporal.PlainDate;}) => readonly string[];What the host binds a declared name to. Asked with the date range the answer has to cover; dates outside it are ignored, and dates missing inside it read as “not in this set”. The contract is synchronous on purpose: an async source is pre-fetched by the caller and wrapped as a resolver returning a static list.
YrnkSchedule
Section titled “YrnkSchedule”type YrnkSchedule = { readonly label?: string; readonly description?: string; readonly from?: string; readonly until?: string; readonly years?: readonly number[]; readonly months?: readonly number[]; readonly days?: readonly YrnkDayAtom[]; readonly shift?: YrnkShift; readonly if?: YrnkIf; readonly time: YrnkTimeSpec;};One element of the DSL’s schedules[]. The date axes (years / months / days) combine with AND; an absent axis means no restriction. from / until is the validity range — a boundary clipping the schedule’s set of points to [from, until), spelled “YYYY-MM-DD HH:MM” on the document timezone’s clock.
YrnkShift
Section titled “YrnkShift”type YrnkShift = { readonly direction: YrnkDirection; readonly orSame: boolean; readonly condition: YrnkDayCondition;};The shift modifier — rounding. Takes each base day selected by the days condition and moves it in a fixed direction until the landing condition holds. orSame is the inclusive / exclusive distinction.
YrnkTimeSpec
Section titled “YrnkTimeSpec”type YrnkTimeSpec = /** An enumeration of fixed times, in written order */ | { readonly kind: 'times'; readonly times: readonly string[] } /** A clock grid; null between means the whole day [00:00, 24:00) */ | { readonly kind: 'grid'; readonly every: readonly [number, YrnkTimeUnit]; readonly between: readonly [string, string] | 'business_hour' | null; } /** A day-level occurrence that carries no time */ | { readonly kind: 'allday' } /** The from-anchored interval sequence, counting across days */ | { readonly kind: 'sequence'; readonly every: readonly [number, YrnkTimeUnit] };The time part of a schedule — exactly one of the three forms the DSL offers, plus the grid’s own shape. Time literals stay as written (zero-padded HH:MM), so round-tripping is the identity.
YrnkTimeUnit
Section titled “YrnkTimeUnit”type YrnkTimeUnit = 'hour' | 'minute' | 'second';