From 74503ad663019788a4270a7d7de0516c6b058829 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Oct 2026 13:16:27 +0000 Subject: [PATCH] refactor(contracts): share one clock-time schema The coach rhythm and the quiet hours both parse "HH:MM" wall-clock times and each defined its own ClockTimeSchema, which the contracts index cannot export twice. Both now import it from one module. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u --- packages/contracts/src/clock-time.ts | 19 +++++++++++++++++++ packages/contracts/src/coach.ts | 2 +- packages/contracts/src/index.ts | 1 + packages/contracts/src/notifications.ts | 19 +------------------ 4 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 packages/contracts/src/clock-time.ts diff --git a/packages/contracts/src/clock-time.ts b/packages/contracts/src/clock-time.ts new file mode 100644 index 0000000..e8d1464 --- /dev/null +++ b/packages/contracts/src/clock-time.ts @@ -0,0 +1,19 @@ +import * as z from "zod"; + +const CLOCK_TIME = /^([01]\d|2[0-3]):([0-5]\d)$/; + +/** A 24-hour wall-clock time, e.g. "22:30". */ +export const ClockTimeSchema = z.string().regex(CLOCK_TIME, "Use HH:MM"); + +/** Minutes after midnight for a "HH:MM" time. */ +export function clockTimeToMinutes(value: string): number { + const match = CLOCK_TIME.exec(value); + if (!match) throw new Error("Use HH:MM"); + return Number(match[1]) * 60 + Number(match[2]); +} + +/** "HH:MM" for minutes after midnight. */ +export function minutesToClockTime(minutes: number): string { + const pad = (value: number) => String(value).padStart(2, "0"); + return `${pad(Math.floor(minutes / 60))}:${pad(minutes % 60)}`; +} diff --git a/packages/contracts/src/coach.ts b/packages/contracts/src/coach.ts index 011944e..17f777e 100644 --- a/packages/contracts/src/coach.ts +++ b/packages/contracts/src/coach.ts @@ -1,4 +1,5 @@ import * as z from "zod"; +import { ClockTimeSchema } from "./clock-time.js"; import { Id } from "./ids.js"; export const COACH_TITLE_MAX_LENGTH = 200; @@ -38,7 +39,6 @@ export const LocalDateSchema = z .refine(isCalendarDate, "Invalid date"); /** A 24-hour wall-clock time in the user's time zone, e.g. "07:30". */ -export const ClockTimeSchema = z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "Use HH:MM"); /** When the main bot checks in with the user; null turns a check-in off. */ export const CoachRhythmSchema = z.object({ diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index 034dcd5..197dfd5 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -1,6 +1,7 @@ export * from "./ai-consent.js"; export * from "./attachments.js"; export * from "./bot-secrets.js"; +export * from "./clock-time.js"; export * from "./coach.js"; export * from "./desktop.js"; export * from "./domain.js"; diff --git a/packages/contracts/src/notifications.ts b/packages/contracts/src/notifications.ts index 5cb6287..5b09a13 100644 --- a/packages/contracts/src/notifications.ts +++ b/packages/contracts/src/notifications.ts @@ -1,26 +1,9 @@ import * as z from "zod"; +import { ClockTimeSchema } from "./clock-time.js"; /** How many nudges the main agent may send per day in the user's time zone. */ export const NUDGES_PER_DAY = 3; -const CLOCK_TIME = /^([01]\d|2[0-3]):([0-5]\d)$/; - -/** A 24-hour wall-clock time, e.g. "22:30". */ -export const ClockTimeSchema = z.string().regex(CLOCK_TIME, "Use HH:MM"); - -/** Minutes after midnight for a "HH:MM" time. */ -export function clockTimeToMinutes(value: string): number { - const match = CLOCK_TIME.exec(value); - if (!match) throw new Error("Use HH:MM"); - return Number(match[1]) * 60 + Number(match[2]); -} - -/** "HH:MM" for minutes after midnight. */ -export function minutesToClockTime(minutes: number): string { - const pad = (value: number) => String(value).padStart(2, "0"); - return `${pad(Math.floor(minutes / 60))}:${pad(minutes % 60)}`; -} - /** A daily range in the user's profile time zone; it may cross midnight ("22:00"–"07:00"). */ export const QuietHoursSchema = z .object({ start: ClockTimeSchema, end: ClockTimeSchema })