@bimetal/temporal 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +73 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/constants.d.ts +29 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +29 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/date-strings.d.ts +23 -0
  9. package/dist/date-strings.d.ts.map +1 -0
  10. package/dist/date-strings.js +25 -0
  11. package/dist/date-strings.js.map +1 -0
  12. package/dist/datetime.d.ts +100 -0
  13. package/dist/datetime.d.ts.map +1 -0
  14. package/dist/datetime.js +289 -0
  15. package/dist/datetime.js.map +1 -0
  16. package/dist/duration.d.ts +25 -0
  17. package/dist/duration.d.ts.map +1 -0
  18. package/dist/duration.js +43 -0
  19. package/dist/duration.js.map +1 -0
  20. package/dist/errors.d.ts +22 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +19 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/format.d.ts +13 -0
  25. package/dist/format.d.ts.map +1 -0
  26. package/dist/format.js +51 -0
  27. package/dist/format.js.map +1 -0
  28. package/dist/granularity.d.ts +13 -0
  29. package/dist/granularity.d.ts.map +1 -0
  30. package/dist/granularity.js +70 -0
  31. package/dist/granularity.js.map +1 -0
  32. package/dist/index.d.ts +30 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +37 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/time-range.d.ts +38 -0
  37. package/dist/time-range.d.ts.map +1 -0
  38. package/dist/time-range.js +67 -0
  39. package/dist/time-range.js.map +1 -0
  40. package/dist/types.d.ts +50 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +9 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/vocabulary.d.ts +40 -0
  45. package/dist/vocabulary.d.ts.map +1 -0
  46. package/dist/vocabulary.js +38 -0
  47. package/dist/vocabulary.js.map +1 -0
  48. package/package.json +45 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @module @bimetal/temporal
3
+ *
4
+ * Domain-neutral temporal kernel: time model, durations, grid resolution,
5
+ * formatting. Stateless, deterministic, pure functions. Shared by every
6
+ * BIMETAL component family, not just the calendar.
7
+ * No DOM, no framework dependencies. ZERO external npm dependencies.
8
+ *
9
+ * Surface (Surface-Charge B / D3): this barrel exposes only the temporal base
10
+ * that is actually consumed. The unconsumed half of the duration algebra
11
+ * (`minutes`/`hours`/`days`/`between`/`duration`/`to*`), the unused unit
12
+ * constants, and a handful of unused helpers were retracted before the 1.0
13
+ * freeze — they live in their modules and return additively post-1.0 when a
14
+ * consumer needs them.
15
+ */
16
+ export type { CalendarDateTime, TimeRange, Granularity, TimeSlot, Clock, FormatOptions, SnapStrategy, } from './types.js';
17
+ export { Month, monthIndex, Weekday, weekdayIndex } from './vocabulary.js';
18
+ /** Forward-API (P1, post-1.0): the typed-error union is advertised but not yet
19
+ * constructed/handled by any consumer. Kept public as the documented error
20
+ * contract; the concrete throwers land post-1.0. */
21
+ export type { CalendarError } from './errors.js';
22
+ export { CalendarValidationError } from './errors.js';
23
+ export { MINUTES_PER_HOUR, HOURS_PER_DAY, DAYS_PER_WEEK, MS_PER_EXACT_DAY, } from './constants.js';
24
+ export { createDateTime, fromEpochMs, now, addDays, addWeeks, addMonths, addYears, addHours, addMinutes, startOfDay, endOfDay, getYear, getMonth, getDayOfMonth, getDayOfWeek, getHours, getMinutes, toMinutesOfDay, isSameDay, isBefore, isAfter, isEqual, daysBetween, } from './datetime.js';
25
+ export { toDateStr, parseDateStr } from './date-strings.js';
26
+ export { createTimeRange, overlaps, clipTo, shiftRange, } from './time-range.js';
27
+ export { durationInMinutes, } from './duration.js';
28
+ export { snap, generateSlots, } from './granularity.js';
29
+ export { formatDateTime, formatDayLabel, } from './format.js';
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,QAAQ,EACR,KAAK,EAGL,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG3E;;qDAEqD;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,cAAc,EAEd,WAAW,EACX,GAAG,EACH,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,UAAU,EACV,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,cAAc,EACd,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EACL,eAAe,EACf,QAAQ,EACR,MAAM,EACN,UAAU,GACX,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,IAAI,EACJ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @module @bimetal/temporal
3
+ *
4
+ * Domain-neutral temporal kernel: time model, durations, grid resolution,
5
+ * formatting. Stateless, deterministic, pure functions. Shared by every
6
+ * BIMETAL component family, not just the calendar.
7
+ * No DOM, no framework dependencies. ZERO external npm dependencies.
8
+ *
9
+ * Surface (Surface-Charge B / D3): this barrel exposes only the temporal base
10
+ * that is actually consumed. The unconsumed half of the duration algebra
11
+ * (`minutes`/`hours`/`days`/`between`/`duration`/`to*`), the unused unit
12
+ * constants, and a handful of unused helpers were retracted before the 1.0
13
+ * freeze — they live in their modules and return additively post-1.0 when a
14
+ * consumer needs them.
15
+ */
16
+ // ── Branded vocabulary (Month / Weekday) — see vocabulary.ts for the freeze rule.
17
+ // `Month`/`Weekday` are each a value (the constants) AND a type (the brand) via declaration
18
+ // merging, so this one export line carries both. ──
19
+ export { Month, monthIndex, Weekday, weekdayIndex } from './vocabulary.js';
20
+ export { CalendarValidationError } from './errors.js';
21
+ // ── Time-Unit Constants ─────────────────────────────────────────
22
+ export { MINUTES_PER_HOUR, HOURS_PER_DAY, DAYS_PER_WEEK, MS_PER_EXACT_DAY, } from './constants.js';
23
+ // ── CalendarDateTime ────────────────────────────────────────────
24
+ export { createDateTime,
25
+ // Documented migration bridge from Date/ISO/epochMs (migration guides) — public API.
26
+ fromEpochMs, now, addDays, addWeeks, addMonths, addYears, addHours, addMinutes, startOfDay, endOfDay, getYear, getMonth, getDayOfMonth, getDayOfWeek, getHours, getMinutes, toMinutesOfDay, isSameDay, isBefore, isAfter, isEqual, daysBetween, } from './datetime.js';
27
+ // ── Date strings (timezone-aware CalendarDateTime ↔ "YYYY-MM-DD") ─
28
+ export { toDateStr, parseDateStr } from './date-strings.js';
29
+ // ── TimeRange ───────────────────────────────────────────────────
30
+ export { createTimeRange, overlaps, clipTo, shiftRange, } from './time-range.js';
31
+ // ── Duration ────────────────────────────────────────────────────
32
+ export { durationInMinutes, } from './duration.js';
33
+ // ── Granularity ─────────────────────────────────────────────────
34
+ export { snap, generateSlots, } from './granularity.js';
35
+ // ── Formatting ──────────────────────────────────────────────────
36
+ export { formatDateTime, formatDayLabel, } from './format.js';
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAeH,mFAAmF;AACnF,+FAA+F;AAC/F,uDAAuD;AACvD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAO3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,mEAAmE;AACnE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,mEAAmE;AACnE,OAAO,EACL,cAAc;AACd,qFAAqF;AACrF,WAAW,EACX,GAAG,EACH,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,UAAU,EACV,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,cAAc,EACd,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,qEAAqE;AACrE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE5D,mEAAmE;AACnE,OAAO,EACL,eAAe,EACf,QAAQ,EACR,MAAM,EACN,UAAU,GACX,MAAM,iBAAiB,CAAC;AAEzB,mEAAmE;AACnE,OAAO,EACL,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,mEAAmE;AACnE,OAAO,EACL,IAAI,EACJ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { CalendarDateTime, TimeRange } from './types.js';
2
+ /** Create a TimeRange. Throws if start >= end. */
3
+ export declare function createTimeRange(start: CalendarDateTime, end: CalendarDateTime): TimeRange;
4
+ /**
5
+ * Build a TimeRange for an all-day event with user-facing **inclusive
6
+ * end** semantics.
7
+ *
8
+ * BIMETAL stores all-day events with **exclusive end** internally:
9
+ * `end = midnight of the day AFTER the last visible day`. Custom dialogs
10
+ * and importers that hand-build TimeRanges historically tripped over
11
+ * this — naive `{ start: 2026-04-15T00:00, end: 2026-04-15T23:59 }` looks
12
+ * right in storage but breaks drag math (degenerate ranges, off-by-one
13
+ * sums). This helper takes the *inclusive* form (the user picks "from
14
+ * April 15 to April 17") and normalises both ends:
15
+ *
16
+ * - `start` → `startOfDay(start)` — strips any time-of-day component
17
+ * - `end` → `addDays(startOfDay(end), 1)` — flips inclusive → exclusive
18
+ *
19
+ * @example Single-day event ("just today"):
20
+ * createAllDayTimeRange(today, today)
21
+ * // → { start: today 00:00, end: tomorrow 00:00 }
22
+ *
23
+ * @example Multi-day event ("April 15 through April 17 inclusive"):
24
+ * createAllDayTimeRange(apr15, apr17)
25
+ * // → { start: apr15 00:00, end: apr18 00:00 }
26
+ */
27
+ export declare function createAllDayTimeRange(startDate: CalendarDateTime, endDate: CalendarDateTime): TimeRange;
28
+ /** Intersection of two ranges. Returns null if they don't overlap. */
29
+ export declare function intersect(a: TimeRange, b: TimeRange): TimeRange | null;
30
+ /** Check if a range contains a point in time. */
31
+ export declare function contains(range: TimeRange, dt: CalendarDateTime): boolean;
32
+ /** Check if two ranges overlap (touching is not overlapping). */
33
+ export declare function overlaps(a: TimeRange, b: TimeRange): boolean;
34
+ /** Clip a range to fit within bounds. Returns null if no overlap. */
35
+ export declare function clipTo(range: TimeRange, bounds: TimeRange): TimeRange | null;
36
+ /** Shift a range to a new start, preserving duration. */
37
+ export declare function shiftRange(range: TimeRange, newStart: CalendarDateTime): TimeRange;
38
+ //# sourceMappingURL=time-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-range.d.ts","sourceRoot":"","sources":["../src/time-range.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI9D,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,gBAAgB,GAAG,SAAS,CAQzF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,gBAAgB,GACxB,SAAS,CAKX;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAQtE;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAExE;AAED,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAE5D;AAED,qEAAqE;AACrE,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAE5E;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CAMlF"}
@@ -0,0 +1,67 @@
1
+ import { CalendarValidationError } from './errors.js';
2
+ import { addDays, startOfDay } from './datetime.js';
3
+ /** Create a TimeRange. Throws if start >= end. */
4
+ export function createTimeRange(start, end) {
5
+ if (start.epochMs >= end.epochMs) {
6
+ throw new CalendarValidationError({
7
+ type: 'INVALID_TIME_RANGE',
8
+ message: `start (${start.epochMs}) must be before end (${end.epochMs})`,
9
+ });
10
+ }
11
+ return { start, end };
12
+ }
13
+ /**
14
+ * Build a TimeRange for an all-day event with user-facing **inclusive
15
+ * end** semantics.
16
+ *
17
+ * BIMETAL stores all-day events with **exclusive end** internally:
18
+ * `end = midnight of the day AFTER the last visible day`. Custom dialogs
19
+ * and importers that hand-build TimeRanges historically tripped over
20
+ * this — naive `{ start: 2026-04-15T00:00, end: 2026-04-15T23:59 }` looks
21
+ * right in storage but breaks drag math (degenerate ranges, off-by-one
22
+ * sums). This helper takes the *inclusive* form (the user picks "from
23
+ * April 15 to April 17") and normalises both ends:
24
+ *
25
+ * - `start` → `startOfDay(start)` — strips any time-of-day component
26
+ * - `end` → `addDays(startOfDay(end), 1)` — flips inclusive → exclusive
27
+ *
28
+ * @example Single-day event ("just today"):
29
+ * createAllDayTimeRange(today, today)
30
+ * // → { start: today 00:00, end: tomorrow 00:00 }
31
+ *
32
+ * @example Multi-day event ("April 15 through April 17 inclusive"):
33
+ * createAllDayTimeRange(apr15, apr17)
34
+ * // → { start: apr15 00:00, end: apr18 00:00 }
35
+ */
36
+ export function createAllDayTimeRange(startDate, endDate) {
37
+ return createTimeRange(startOfDay(startDate), addDays(startOfDay(endDate), 1));
38
+ }
39
+ /** Intersection of two ranges. Returns null if they don't overlap. */
40
+ export function intersect(a, b) {
41
+ const start = Math.max(a.start.epochMs, b.start.epochMs);
42
+ const end = Math.min(a.end.epochMs, b.end.epochMs);
43
+ if (start >= end)
44
+ return null;
45
+ return {
46
+ start: { epochMs: start, timezone: a.start.timezone },
47
+ end: { epochMs: end, timezone: a.start.timezone },
48
+ };
49
+ }
50
+ /** Check if a range contains a point in time. */
51
+ export function contains(range, dt) {
52
+ return dt.epochMs >= range.start.epochMs && dt.epochMs < range.end.epochMs;
53
+ }
54
+ /** Check if two ranges overlap (touching is not overlapping). */
55
+ export function overlaps(a, b) {
56
+ return a.start.epochMs < b.end.epochMs && b.start.epochMs < a.end.epochMs;
57
+ }
58
+ /** Clip a range to fit within bounds. Returns null if no overlap. */
59
+ export function clipTo(range, bounds) {
60
+ return intersect(range, bounds);
61
+ }
62
+ /** Shift a range to a new start, preserving duration. */
63
+ export function shiftRange(range, newStart) {
64
+ const durationMs = range.end.epochMs - range.start.epochMs;
65
+ return createTimeRange(newStart, { epochMs: newStart.epochMs + durationMs, timezone: newStart.timezone });
66
+ }
67
+ //# sourceMappingURL=time-range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-range.js","sourceRoot":"","sources":["../src/time-range.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEpD,kDAAkD;AAClD,MAAM,UAAU,eAAe,CAAC,KAAuB,EAAE,GAAqB;IAC5E,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,uBAAuB,CAAC;YAChC,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,UAAU,KAAK,CAAC,OAAO,yBAAyB,GAAG,CAAC,OAAO,GAAG;SACxE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAA2B,EAC3B,OAAyB;IAEzB,OAAO,eAAe,CACpB,UAAU,CAAC,SAAS,CAAC,EACrB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAChC,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,CAAY,EAAE,CAAY;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,KAAK,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO;QACL,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;QACrD,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;KAClD,CAAC;AACJ,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,QAAQ,CAAC,KAAgB,EAAE,EAAoB;IAC7D,OAAO,EAAE,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;AAC7E,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,QAAQ,CAAC,CAAY,EAAE,CAAY;IACjD,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;AAC5E,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,MAAM,CAAC,KAAgB,EAAE,MAAiB;IACxD,OAAO,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,UAAU,CAAC,KAAgB,EAAE,QAA0B;IACrE,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;IAC3D,OAAO,eAAe,CACpB,QAAQ,EACR,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,GAAG,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CACxE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Core — Temporal Type Definitions
3
+ *
4
+ * Domain-neutral time model: points in time, ranges, durations, grid
5
+ * resolution, formatting. All types are immutable (readonly properties).
6
+ * No framework dependencies. No DOM. No calendar-domain concepts.
7
+ */
8
+ /** A point in time with timezone context. */
9
+ export interface CalendarDateTime {
10
+ readonly epochMs: number;
11
+ readonly timezone: string;
12
+ }
13
+ /** A span between two points in time. Invariant: start < end. */
14
+ export interface TimeRange {
15
+ readonly start: CalendarDateTime;
16
+ readonly end: CalendarDateTime;
17
+ }
18
+ /** A duration in milliseconds. */
19
+ export interface Duration {
20
+ readonly ms: number;
21
+ }
22
+ /** Time grid resolution. */
23
+ export interface Granularity {
24
+ readonly minutes: number;
25
+ }
26
+ /** A discrete slot in a time grid. */
27
+ export interface TimeSlot {
28
+ readonly range: TimeRange;
29
+ readonly index: number;
30
+ }
31
+ /**
32
+ * Injectable time source. Used for "now" semantics and by event-sourced
33
+ * stores for event timestamps.
34
+ *
35
+ * Defined locally — structurally identical to `@bimetal/event-sourcing`'s
36
+ * `Clock`. TypeScript's structural typing keeps the two interchangeable
37
+ * without coupling `@bimetal/temporal` to the event-sourcing layer.
38
+ */
39
+ export interface Clock {
40
+ now(): number;
41
+ }
42
+ /** Options for Intl-based formatting. */
43
+ export interface FormatOptions {
44
+ readonly locale: string;
45
+ readonly dateStyle?: 'full' | 'long' | 'medium' | 'short';
46
+ readonly timeStyle?: 'full' | 'long' | 'medium' | 'short';
47
+ }
48
+ /** Snap rounding strategy. */
49
+ export type SnapStrategy = 'floor' | 'ceil' | 'round';
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC;CAChC;AAED,kCAAkC;AAClC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAOD;;;;;;;GAOG;AACH,MAAM,WAAW,KAAK;IACpB,GAAG,IAAI,MAAM,CAAC;CACf;AAID,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CAC3D;AAID,8BAA8B;AAC9B,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Core — Temporal Type Definitions
3
+ *
4
+ * Domain-neutral time model: points in time, ranges, durations, grid
5
+ * resolution, formatting. All types are immutable (readonly properties).
6
+ * No framework dependencies. No DOM. No calendar-domain concepts.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,40 @@
1
+ declare const MonthBrand: unique symbol;
2
+ /** Calendar month, 0-based (January = 0 … December = 11). House convention. */
3
+ export type Month = number & {
4
+ readonly [MonthBrand]: 'Month';
5
+ };
6
+ export declare const Month: {
7
+ readonly January: Month;
8
+ readonly February: Month;
9
+ readonly March: Month;
10
+ readonly April: Month;
11
+ readonly May: Month;
12
+ readonly June: Month;
13
+ readonly July: Month;
14
+ readonly August: Month;
15
+ readonly September: Month;
16
+ readonly October: Month;
17
+ readonly November: Month;
18
+ readonly December: Month;
19
+ };
20
+ /** Brand a COMPUTED month value. Validates integer 0..11 (January=0 … December=11); throws otherwise. */
21
+ export declare function monthIndex(n: number): Month;
22
+ declare const WeekdayBrand: unique symbol;
23
+ /** Day of week, 0-based Monday (Monday = 0 … Sunday = 6). A HOUSE convention — NOT ISO-8601 (1–7)
24
+ * and NOT JS `Date.getDay()` (0 = Sunday). */
25
+ export type Weekday = number & {
26
+ readonly [WeekdayBrand]: 'Weekday';
27
+ };
28
+ export declare const Weekday: {
29
+ readonly Monday: Weekday;
30
+ readonly Tuesday: Weekday;
31
+ readonly Wednesday: Weekday;
32
+ readonly Thursday: Weekday;
33
+ readonly Friday: Weekday;
34
+ readonly Saturday: Weekday;
35
+ readonly Sunday: Weekday;
36
+ };
37
+ /** Brand a COMPUTED weekday value. Validates integer 0..6 (Monday=0 … Sunday=6); throws otherwise. */
38
+ export declare function weekdayIndex(n: number): Weekday;
39
+ export {};
40
+ //# sourceMappingURL=vocabulary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabulary.d.ts","sourceRoot":"","sources":["../src/vocabulary.ts"],"names":[],"mappings":"AAeA,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AACxC,+EAA+E;AAC/E,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAChE,eAAO,MAAM,KAAK;sBACF,KAAK;uBAAiB,KAAK;oBAAc,KAAK;oBAAc,KAAK;kBACrE,KAAK;mBAAa,KAAK;mBAAa,KAAK;qBAAe,KAAK;wBACvD,KAAK;sBAAgB,KAAK;uBAAkB,KAAK;uBAAkB,KAAK;CAChF,CAAC;AAEX,yGAAyG;AACzG,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAK3C;AAED,OAAO,CAAC,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;AAC1C;+CAC+C;AAC/C,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AACtE,eAAO,MAAM,OAAO;qBACL,OAAO;sBAAgB,OAAO;wBAAkB,OAAO;uBAAiB,OAAO;qBAC/E,OAAO;uBAAiB,OAAO;qBAAe,OAAO;CAC1D,CAAC;AAEX,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAK/C"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Branded calendar vocabulary — `Month` and `Weekday`.
3
+ *
4
+ * WHY BRANDED (freeze rule, V1_PUBLIC_CONTRACT): a numeric parameter gets a branded vocabulary
5
+ * exactly when several LIVE conventions compete for the same number. `month` (0- vs 1-based) and
6
+ * `weekday` (0=Mon vs 0=Sun vs ISO 1–7) both qualify; `day` (always 1-based) and `hour`/`minute`
7
+ * (clock convention) do NOT. The brand makes a naked number a COMPILE error where a Month/Weekday is
8
+ * expected — a numeric enum/union would silently accept in-range literals and NOT close the trap.
9
+ *
10
+ * The internal representation is UNCHANGED (Month 0-based, Weekday 0=Monday) — this is a vocabulary,
11
+ * not a semantic flip. Use `Month.January`…`Month.December` / `Weekday.Monday`…`Weekday.Sunday` for
12
+ * literals, or `monthIndex(n)` / `weekdayIndex(n)` for COMPUTED values (they validate + brand).
13
+ */
14
+ import { CalendarValidationError } from './errors.js';
15
+ export const Month = {
16
+ January: 0, February: 1, March: 2, April: 3,
17
+ May: 4, June: 5, July: 6, August: 7,
18
+ September: 8, October: 9, November: 10, December: 11,
19
+ };
20
+ /** Brand a COMPUTED month value. Validates integer 0..11 (January=0 … December=11); throws otherwise. */
21
+ export function monthIndex(n) {
22
+ if (!Number.isInteger(n) || n < 0 || n > 11) {
23
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'month', message: `month must be an integer 0-11 (January=0 … December=11), got ${n}` });
24
+ }
25
+ return n;
26
+ }
27
+ export const Weekday = {
28
+ Monday: 0, Tuesday: 1, Wednesday: 2, Thursday: 3,
29
+ Friday: 4, Saturday: 5, Sunday: 6,
30
+ };
31
+ /** Brand a COMPUTED weekday value. Validates integer 0..6 (Monday=0 … Sunday=6); throws otherwise. */
32
+ export function weekdayIndex(n) {
33
+ if (!Number.isInteger(n) || n < 0 || n > 6) {
34
+ throw new CalendarValidationError({ type: 'INVALID_EVENT', field: 'weekday', message: `weekday must be an integer 0-6 (Monday=0 … Sunday=6), got ${n}` });
35
+ }
36
+ return n;
37
+ }
38
+ //# sourceMappingURL=vocabulary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabulary.js","sourceRoot":"","sources":["../src/vocabulary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAKtD,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,OAAO,EAAE,CAAU,EAAE,QAAQ,EAAE,CAAU,EAAE,KAAK,EAAE,CAAU,EAAE,KAAK,EAAE,CAAU;IAC/E,GAAG,EAAE,CAAU,EAAE,IAAI,EAAE,CAAU,EAAE,IAAI,EAAE,CAAU,EAAE,MAAM,EAAE,CAAU;IACvE,SAAS,EAAE,CAAU,EAAE,OAAO,EAAE,CAAU,EAAE,QAAQ,EAAE,EAAW,EAAE,QAAQ,EAAE,EAAW;CAChF,CAAC;AAEX,yGAAyG;AACzG,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,uBAAuB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gEAAgE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7J,CAAC;IACD,OAAO,CAAU,CAAC;AACpB,CAAC;AAMD,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,MAAM,EAAE,CAAY,EAAE,OAAO,EAAE,CAAY,EAAE,SAAS,EAAE,CAAY,EAAE,QAAQ,EAAE,CAAY;IAC5F,MAAM,EAAE,CAAY,EAAE,QAAQ,EAAE,CAAY,EAAE,MAAM,EAAE,CAAY;CAC1D,CAAC;AAEX,sGAAsG;AACtG,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,uBAAuB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,6DAA6D,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5J,CAAC;IACD,OAAO,CAAY,CAAC;AACtB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@bimetal/temporal",
3
+ "version": "0.35.0",
4
+ "description": "Domain-neutral temporal kernel: time model, durations, grid resolution, formatting. Shared across all BIMETAL component families.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -b",
21
+ "dev": "tsc -b --watch",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "devDependencies": {
28
+ "vitest": "^3.2.1"
29
+ },
30
+ "sideEffects": false,
31
+ "license": "Apache-2.0",
32
+ "author": "Andreas Biederbeck",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/bimetal/bimetal.git",
36
+ "directory": "packages/engine/temporal"
37
+ },
38
+ "keywords": [
39
+ "datetime",
40
+ "timezone",
41
+ "duration",
42
+ "temporal",
43
+ "headless"
44
+ ]
45
+ }