Moltenship Time
API reference

Events and snapshots

Adapters, event ranges, validation, snapshots, and queries.

Exported from @moltenship/calendar and from @moltenship/calendar-core.

Adapter

const eventAdapter = defineEventAdapter<TEvent>()({ getId, getTitle, getInterval, getResourceIds });

defineEventAdapter returns your adapter unchanged. The two calls let TypeScript fix TEvent first and then infer the ID types from getId and getResourceIds.

Prop

Type

createCanonicalEventAdapter() returns an adapter for records already in Time's CalendarEvent shape (id, title, kind, start, end, optional resourceIds, and data).

Ranges

getInterval returns one of three shapes. In every one, start is included and end is excluded, and start must come before end.

type CalendarEventInterval =
  | { kind: "timed"; start: Temporal.Instant; end: Temporal.Instant; sourceTimeZone?: string }
  | { kind: "all-day"; start: Temporal.PlainDate; end: Temporal.PlainDate }
  | { kind: "floating"; start: Temporal.PlainDateTime; end: Temporal.PlainDateTime };

See Time and time zones for when to use each.

Validation helpers

These check values before they reach the calendar. They return a result instead of throwing.

import { isEventId, validateCalendarEvent } from "@moltenship/calendar";

isEventId("meeting-1"); // true

const result = validateCalendarEvent({
  id: "meeting-1",
  title: "Standup",
  kind: "timed",
  start: Temporal.Instant.from("2026-10-08T07:15:00Z"),
  end: Temporal.Instant.from("2026-10-08T07:00:00Z"),
  data: null,
});

result.valid; // false
result.valid || result.issues; // [{ code: "start-not-before-end", kind: "timed" }]
FunctionChecks
isEventId(value), isResourceId(value)A non-empty string or a finite number
validateCalendarEvent(event)A CalendarEvent: its ID, resource IDs, and range
validateCalendarEventInterval(interval, compare)A range, using the comparison functions you pass

Snapshots

A snapshot is a frozen, indexed copy of your events. Root builds one for you and exposes it as meta.eventSnapshot. Outside React, build one yourself:

import { createEventSnapshotCache } from "@moltenship/calendar";

const snapshots = createEventSnapshotCache(eventAdapter, { queryCacheSize: 128 });
const snapshot = snapshots.getSnapshot(events);

snapshots.getSnapshot(events) === snapshot; // true while `events` is the same array

Keep one cache per adapter and reuse it. getSnapshot returns the same snapshot for the same array and builds a new one, reusing what it can, for a new array. For a one-off snapshot, call createEventSnapshot(events, eventAdapter, options).

Prop

Type

Snapshot fields

FieldContents
eventsValid events, normalized, in input order
byIdA read-only Map from ID to normalized event
sizeNumber of valid events
issuesProblems with skipped records (see below)
versionGoes up by one for each new snapshot from the same cache
getQueryCacheStatistics()capacity, size, hits, misses, and evictions

A normalized event has your record as event, plus id, title, resourceIds, isReadOnly, revision, inputIndex, and the range fields (kind, start, end). Inside a calendar, an event with a change that is still saving also has isPending: true and shows its proposed range; see pending changes.

Queries

snapshot.queryTimed(instantInterval(start, end));
snapshot.queryAllDay(plainDateInterval(startDate, endDate));
snapshot.queryFloating(plainDateTimeInterval(startDateTime, endDateTime));

snapshot.queryTimed(range, { resourceIds: ["room-1"] });
snapshot.eventsForResource("room-1");
snapshot.get("meeting-1");
  • Each query returns the events of one kind that overlap the range. Ranges are end-exclusive on both sides, so an event ending exactly at the range start isn't included.
  • Build ranges with instantInterval, plainDateInterval, and plainDateTimeInterval from the Temporal helpers.
  • resourceIds keeps events assigned to any of the listed resources. An empty list matches nothing.
  • Results are frozen and in input order. Repeating a query returns the same array while it's in the cache.

Invalid records

With the default policy, invalid records are left out of the snapshot and listed in snapshot.issues. In React they also appear as invalid-event diagnostics. Each issue has the event, its index in your array, a code, and when known the eventId, the adapter function that failed (accessor), and a message.

codeCause
adapter-errorAn adapter function threw
invalid-event-idgetId returned an empty string, NaN, or another non-ID value
duplicate-event-idAnother record already has this ID
invalid-resource-idgetResourceIds returned a non-ID value
invalid-revisiongetRevision returned something other than a string or number
invalid-event-intervalThe range has an unknown kind or the wrong Temporal type
start-not-before-endThe range is empty or reversed

With invalidEventPolicy: "throw", building the snapshot throws an InvalidCalendarEventSnapshotError with every issue on its issues property instead.

Edit on GitHub

Last updated on

On this page