@agent-native/scheduling 0.2.1 → 0.2.2-nightly-20260927013707

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 (279) hide show
  1. package/dist/actions/_helpers.d.ts +0 -13
  2. package/dist/actions/_helpers.d.ts.map +1 -1
  3. package/dist/actions/_helpers.js +0 -16
  4. package/dist/actions/_helpers.js.map +1 -1
  5. package/dist/actions/create-team.js +0 -1
  6. package/dist/actions/create-team.js.map +1 -1
  7. package/dist/actions/list-bookings.js +0 -4
  8. package/dist/actions/list-bookings.js.map +1 -1
  9. package/dist/actions/refresh-busy-times.js +0 -1
  10. package/dist/actions/refresh-busy-times.js.map +1 -1
  11. package/dist/actions/revoke-private-link.js +0 -2
  12. package/dist/actions/revoke-private-link.js.map +1 -1
  13. package/dist/actions/update-profile.js +0 -5
  14. package/dist/actions/update-profile.js.map +1 -1
  15. package/dist/core/buffers.d.ts +0 -18
  16. package/dist/core/buffers.d.ts.map +1 -1
  17. package/dist/core/buffers.js +0 -6
  18. package/dist/core/buffers.js.map +1 -1
  19. package/dist/core/conflicts.d.ts +0 -4
  20. package/dist/core/conflicts.d.ts.map +1 -1
  21. package/dist/core/conflicts.js +0 -1
  22. package/dist/core/conflicts.js.map +1 -1
  23. package/dist/core/limits.d.ts +0 -8
  24. package/dist/core/limits.d.ts.map +1 -1
  25. package/dist/core/limits.js.map +1 -1
  26. package/dist/core/round-robin.d.ts +0 -17
  27. package/dist/core/round-robin.d.ts.map +1 -1
  28. package/dist/core/round-robin.js.map +1 -1
  29. package/dist/core/rrule.d.ts.map +1 -1
  30. package/dist/core/rrule.js +0 -7
  31. package/dist/core/rrule.js.map +1 -1
  32. package/dist/core/rules.d.ts +0 -17
  33. package/dist/core/rules.d.ts.map +1 -1
  34. package/dist/core/rules.js +0 -8
  35. package/dist/core/rules.js.map +1 -1
  36. package/dist/core/slots.d.ts +0 -31
  37. package/dist/core/slots.d.ts.map +1 -1
  38. package/dist/core/slots.js +0 -9
  39. package/dist/core/slots.js.map +1 -1
  40. package/dist/core/time.d.ts +0 -24
  41. package/dist/core/time.d.ts.map +1 -1
  42. package/dist/core/time.js +0 -37
  43. package/dist/core/time.js.map +1 -1
  44. package/dist/index.d.ts +0 -10
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +0 -10
  47. package/dist/index.js.map +1 -1
  48. package/dist/manifest.d.ts +0 -9
  49. package/dist/manifest.d.ts.map +1 -1
  50. package/dist/manifest.js +0 -18
  51. package/dist/manifest.js.map +1 -1
  52. package/dist/react/components/SlotPicker.d.ts +0 -8
  53. package/dist/react/components/SlotPicker.d.ts.map +1 -1
  54. package/dist/react/components/SlotPicker.js.map +1 -1
  55. package/dist/react/components/TimezoneSelect.d.ts.map +1 -1
  56. package/dist/react/components/TimezoneSelect.js +0 -2
  57. package/dist/react/components/TimezoneSelect.js.map +1 -1
  58. package/dist/react/components/booking-links/AvailabilityEditor.d.ts +0 -4
  59. package/dist/react/components/booking-links/AvailabilityEditor.d.ts.map +1 -1
  60. package/dist/react/components/booking-links/AvailabilityEditor.js +0 -15
  61. package/dist/react/components/booking-links/AvailabilityEditor.js.map +1 -1
  62. package/dist/react/components/booking-links/BookingLinkCreateDialog.d.ts +0 -6
  63. package/dist/react/components/booking-links/BookingLinkCreateDialog.d.ts.map +1 -1
  64. package/dist/react/components/booking-links/BookingLinkCreateDialog.js +0 -16
  65. package/dist/react/components/booking-links/BookingLinkCreateDialog.js.map +1 -1
  66. package/dist/react/components/booking-links/ConferencingSelector.d.ts +0 -2
  67. package/dist/react/components/booking-links/ConferencingSelector.d.ts.map +1 -1
  68. package/dist/react/components/booking-links/ConferencingSelector.js +0 -24
  69. package/dist/react/components/booking-links/ConferencingSelector.js.map +1 -1
  70. package/dist/react/components/booking-links/CustomFieldsEditor.d.ts +0 -1
  71. package/dist/react/components/booking-links/CustomFieldsEditor.d.ts.map +1 -1
  72. package/dist/react/components/booking-links/CustomFieldsEditor.js +0 -14
  73. package/dist/react/components/booking-links/CustomFieldsEditor.js.map +1 -1
  74. package/dist/react/components/booking-links/DurationPicker.d.ts +0 -1
  75. package/dist/react/components/booking-links/DurationPicker.d.ts.map +1 -1
  76. package/dist/react/components/booking-links/DurationPicker.js +1 -11
  77. package/dist/react/components/booking-links/DurationPicker.js.map +1 -1
  78. package/dist/react/components/booking-links/SlugEditor.d.ts +0 -3
  79. package/dist/react/components/booking-links/SlugEditor.d.ts.map +1 -1
  80. package/dist/react/components/booking-links/SlugEditor.js +0 -11
  81. package/dist/react/components/booking-links/SlugEditor.js.map +1 -1
  82. package/dist/react/components/booking-links/index.d.ts +0 -9
  83. package/dist/react/components/booking-links/index.d.ts.map +1 -1
  84. package/dist/react/components/booking-links/index.js +0 -9
  85. package/dist/react/components/booking-links/index.js.map +1 -1
  86. package/dist/react/components/index.d.ts +0 -10
  87. package/dist/react/components/index.d.ts.map +1 -1
  88. package/dist/react/components/index.js +0 -10
  89. package/dist/react/components/index.js.map +1 -1
  90. package/dist/react/hooks/useBookingFlow.d.ts.map +1 -1
  91. package/dist/react/hooks/useBookingFlow.js +0 -10
  92. package/dist/react/hooks/useBookingFlow.js.map +1 -1
  93. package/dist/react/hooks/useEventType.d.ts.map +1 -1
  94. package/dist/react/hooks/useEventType.js +0 -4
  95. package/dist/react/hooks/useEventType.js.map +1 -1
  96. package/dist/react/hooks/useReschedule.d.ts.map +1 -1
  97. package/dist/react/hooks/useReschedule.js +0 -4
  98. package/dist/react/hooks/useReschedule.js.map +1 -1
  99. package/dist/react/hooks/useSlots.d.ts +0 -2
  100. package/dist/react/hooks/useSlots.d.ts.map +1 -1
  101. package/dist/react/hooks/useSlots.js +0 -8
  102. package/dist/react/hooks/useSlots.js.map +1 -1
  103. package/dist/react/hooks/useTimezone.d.ts.map +1 -1
  104. package/dist/react/hooks/useTimezone.js +0 -4
  105. package/dist/react/hooks/useTimezone.js.map +1 -1
  106. package/dist/react/index.d.ts +0 -5
  107. package/dist/react/index.d.ts.map +1 -1
  108. package/dist/react/index.js +0 -5
  109. package/dist/react/index.js.map +1 -1
  110. package/dist/schema/bookings.d.ts +0 -7
  111. package/dist/schema/bookings.d.ts.map +1 -1
  112. package/dist/schema/bookings.js +0 -29
  113. package/dist/schema/bookings.js.map +1 -1
  114. package/dist/schema/calendar-cache.d.ts.map +1 -1
  115. package/dist/schema/calendar-cache.js +0 -11
  116. package/dist/schema/calendar-cache.js.map +1 -1
  117. package/dist/schema/credentials.d.ts +0 -8
  118. package/dist/schema/credentials.d.ts.map +1 -1
  119. package/dist/schema/credentials.js +0 -28
  120. package/dist/schema/credentials.js.map +1 -1
  121. package/dist/schema/event-types.d.ts +0 -10
  122. package/dist/schema/event-types.d.ts.map +1 -1
  123. package/dist/schema/event-types.js +0 -42
  124. package/dist/schema/event-types.js.map +1 -1
  125. package/dist/schema/index.d.ts +0 -11
  126. package/dist/schema/index.d.ts.map +1 -1
  127. package/dist/schema/index.js +0 -11
  128. package/dist/schema/index.js.map +1 -1
  129. package/dist/schema/routing-forms.d.ts.map +1 -1
  130. package/dist/schema/routing-forms.js +0 -17
  131. package/dist/schema/routing-forms.js.map +1 -1
  132. package/dist/schema/schedules.d.ts +0 -12
  133. package/dist/schema/schedules.d.ts.map +1 -1
  134. package/dist/schema/schedules.js +0 -25
  135. package/dist/schema/schedules.js.map +1 -1
  136. package/dist/schema/teams.d.ts.map +1 -1
  137. package/dist/schema/teams.js +0 -7
  138. package/dist/schema/teams.js.map +1 -1
  139. package/dist/schema/workflows.d.ts +0 -7
  140. package/dist/schema/workflows.d.ts.map +1 -1
  141. package/dist/schema/workflows.js +0 -19
  142. package/dist/schema/workflows.js.map +1 -1
  143. package/dist/server/availability-engine.d.ts +0 -12
  144. package/dist/server/availability-engine.d.ts.map +1 -1
  145. package/dist/server/availability-engine.js +0 -20
  146. package/dist/server/availability-engine.js.map +1 -1
  147. package/dist/server/booking-service.d.ts +0 -14
  148. package/dist/server/booking-service.d.ts.map +1 -1
  149. package/dist/server/booking-service.js +0 -8
  150. package/dist/server/booking-service.js.map +1 -1
  151. package/dist/server/bookings-repo.d.ts +0 -7
  152. package/dist/server/bookings-repo.d.ts.map +1 -1
  153. package/dist/server/bookings-repo.js +0 -3
  154. package/dist/server/bookings-repo.js.map +1 -1
  155. package/dist/server/context.d.ts +0 -16
  156. package/dist/server/context.d.ts.map +1 -1
  157. package/dist/server/context.js.map +1 -1
  158. package/dist/server/db-types.d.ts +0 -11
  159. package/dist/server/db-types.d.ts.map +1 -1
  160. package/dist/server/event-types-repo.d.ts +0 -10
  161. package/dist/server/event-types-repo.d.ts.map +1 -1
  162. package/dist/server/event-types-repo.js +0 -21
  163. package/dist/server/event-types-repo.js.map +1 -1
  164. package/dist/server/hooks.d.ts.map +1 -1
  165. package/dist/server/hooks.js +0 -11
  166. package/dist/server/hooks.js.map +1 -1
  167. package/dist/server/ics.d.ts +0 -7
  168. package/dist/server/ics.d.ts.map +1 -1
  169. package/dist/server/ics.js.map +1 -1
  170. package/dist/server/providers/builtin-video.d.ts +0 -5
  171. package/dist/server/providers/builtin-video.d.ts.map +1 -1
  172. package/dist/server/providers/builtin-video.js.map +1 -1
  173. package/dist/server/providers/google-calendar.d.ts +0 -3
  174. package/dist/server/providers/google-calendar.d.ts.map +1 -1
  175. package/dist/server/providers/google-calendar.js +0 -2
  176. package/dist/server/providers/google-calendar.js.map +1 -1
  177. package/dist/server/providers/google-meet.d.ts +0 -7
  178. package/dist/server/providers/google-meet.d.ts.map +1 -1
  179. package/dist/server/providers/google-meet.js.map +1 -1
  180. package/dist/server/providers/office365.d.ts +0 -6
  181. package/dist/server/providers/office365.d.ts.map +1 -1
  182. package/dist/server/providers/office365.js.map +1 -1
  183. package/dist/server/providers/registry.d.ts +0 -4
  184. package/dist/server/providers/registry.d.ts.map +1 -1
  185. package/dist/server/providers/registry.js.map +1 -1
  186. package/dist/server/providers/teams.d.ts +0 -6
  187. package/dist/server/providers/teams.d.ts.map +1 -1
  188. package/dist/server/providers/teams.js +0 -2
  189. package/dist/server/providers/teams.js.map +1 -1
  190. package/dist/server/providers/types.d.ts +0 -27
  191. package/dist/server/providers/types.d.ts.map +1 -1
  192. package/dist/server/providers/zoom.d.ts +0 -19
  193. package/dist/server/providers/zoom.d.ts.map +1 -1
  194. package/dist/server/providers/zoom.js +0 -2
  195. package/dist/server/providers/zoom.js.map +1 -1
  196. package/dist/server/schedules-repo.d.ts.map +1 -1
  197. package/dist/server/schedules-repo.js +0 -4
  198. package/dist/server/schedules-repo.js.map +1 -1
  199. package/dist/server/video-oauth-state.d.ts +0 -5
  200. package/dist/server/video-oauth-state.d.ts.map +1 -1
  201. package/dist/server/video-oauth-state.js +1 -9
  202. package/dist/server/video-oauth-state.js.map +1 -1
  203. package/dist/server/video-oauth.d.ts +0 -4
  204. package/dist/server/video-oauth.d.ts.map +1 -1
  205. package/dist/server/video-oauth.js +0 -23
  206. package/dist/server/video-oauth.js.map +1 -1
  207. package/dist/shared/index.d.ts +0 -37
  208. package/dist/shared/index.d.ts.map +1 -1
  209. package/dist/shared/index.js +0 -4
  210. package/dist/shared/index.js.map +1 -1
  211. package/package.json +4 -4
  212. package/src/actions/_helpers.ts +0 -16
  213. package/src/actions/booking-authz.spec.ts +0 -6
  214. package/src/actions/create-team.ts +0 -1
  215. package/src/actions/event-type-authz.spec.ts +0 -11
  216. package/src/actions/list-bookings.ts +0 -4
  217. package/src/actions/refresh-busy-times.ts +0 -1
  218. package/src/actions/revoke-private-link.ts +0 -2
  219. package/src/actions/update-profile.ts +0 -5
  220. package/src/core/buffers.ts +0 -18
  221. package/src/core/conflicts.ts +0 -4
  222. package/src/core/limits.ts +0 -8
  223. package/src/core/round-robin.test.ts +0 -1
  224. package/src/core/round-robin.ts +0 -17
  225. package/src/core/rrule.ts +0 -7
  226. package/src/core/rules.ts +0 -17
  227. package/src/core/slots.test.ts +1 -11
  228. package/src/core/slots.ts +0 -40
  229. package/src/core/time.ts +0 -37
  230. package/src/index.ts +0 -11
  231. package/src/manifest.ts +0 -19
  232. package/src/react/components/SlotPicker.tsx +0 -8
  233. package/src/react/components/TimezoneSelect.tsx +0 -2
  234. package/src/react/components/_shadcn-shims.d.ts +0 -35
  235. package/src/react/components/booking-links/AvailabilityEditor.tsx +0 -15
  236. package/src/react/components/booking-links/BookingLinkCreateDialog.tsx +0 -22
  237. package/src/react/components/booking-links/ConferencingSelector.tsx +0 -26
  238. package/src/react/components/booking-links/CustomFieldsEditor.tsx +0 -15
  239. package/src/react/components/booking-links/DurationPicker.tsx +1 -12
  240. package/src/react/components/booking-links/SlugEditor.tsx +0 -14
  241. package/src/react/components/booking-links/index.ts +0 -9
  242. package/src/react/components/index.ts +0 -10
  243. package/src/react/hooks/useBookingFlow.ts +0 -10
  244. package/src/react/hooks/useEventType.ts +0 -4
  245. package/src/react/hooks/useReschedule.ts +0 -4
  246. package/src/react/hooks/useSlots.ts +0 -10
  247. package/src/react/hooks/useTimezone.ts +0 -4
  248. package/src/react/index.ts +0 -5
  249. package/src/schema/bookings.ts +0 -29
  250. package/src/schema/calendar-cache.ts +0 -11
  251. package/src/schema/credentials.ts +0 -28
  252. package/src/schema/event-types.ts +0 -42
  253. package/src/schema/index.ts +0 -11
  254. package/src/schema/routing-forms.ts +0 -17
  255. package/src/schema/schedules.ts +0 -25
  256. package/src/schema/teams.ts +0 -7
  257. package/src/schema/workflows.ts +0 -19
  258. package/src/server/availability-engine.test.ts +0 -1
  259. package/src/server/availability-engine.ts +0 -22
  260. package/src/server/booking-service.spec.ts +0 -15
  261. package/src/server/booking-service.ts +0 -22
  262. package/src/server/bookings-repo.ts +0 -10
  263. package/src/server/context.ts +0 -16
  264. package/src/server/db-types.ts +0 -11
  265. package/src/server/event-types-repo.ts +0 -31
  266. package/src/server/hooks.ts +0 -11
  267. package/src/server/ics.ts +0 -7
  268. package/src/server/providers/builtin-video.ts +0 -5
  269. package/src/server/providers/google-calendar.ts +0 -5
  270. package/src/server/providers/google-meet.ts +0 -7
  271. package/src/server/providers/office365.ts +0 -6
  272. package/src/server/providers/registry.ts +0 -4
  273. package/src/server/providers/teams.ts +0 -8
  274. package/src/server/providers/types.ts +0 -27
  275. package/src/server/providers/zoom.ts +0 -21
  276. package/src/server/schedules-repo.ts +0 -4
  277. package/src/server/video-oauth-state.ts +1 -10
  278. package/src/server/video-oauth.ts +0 -23
  279. package/src/shared/index.ts +0 -38
@@ -1,6 +1,3 @@
1
- /**
2
- * Shared helpers for actions in this package.
3
- */
4
1
  import { and, eq } from "drizzle-orm";
5
2
 
6
3
  import { getSchedulingContext } from "../server/context.js";
@@ -15,13 +12,6 @@ export function currentUserEmailOrNull(): string | null {
15
12
  return getSchedulingContext().getCurrentUserEmail() ?? null;
16
13
  }
17
14
 
18
- /**
19
- * Verify the current user is the host of a booking. Throws "Not authorized to
20
- * <action>" if not. Mirrors the `isHost` half of the `isHost || hasToken`
21
- * guard used by cancel-booking/reschedule-booking, for booking actions that
22
- * don't accept a public capability token (attendee/note/status mutations —
23
- * host identity is the only legitimate caller for these).
24
- */
25
15
  export function assertBookingHost(
26
16
  booking: { hostEmail: string },
27
17
  action: string,
@@ -56,12 +46,6 @@ export async function assertTeamAdmin(teamId: string): Promise<void> {
56
46
  }
57
47
  }
58
48
 
59
- /**
60
- * Verify the current user is a member of the team (any role). Read-only
61
- * team resource listings should gate on this so that team IDs cannot be
62
- * enumerated by guessing. Throws "Not authenticated" if there is no
63
- * current user, and "Forbidden" if the user is not a member.
64
- */
65
49
  export async function assertTeamMember(teamId: string): Promise<void> {
66
50
  const { getDb, schema } = getSchedulingContext();
67
51
  const email = currentUserEmail();
@@ -1,9 +1,3 @@
1
- /**
2
- * Cross-tenant write (IDOR) regression tests for booking actions that
3
- * mutate — or disclose a capability token for — a booking that belongs to
4
- * another host. Mirrors the `isHost` guard already proven in
5
- * `cancel-booking.ts` / `reschedule-booking.ts`.
6
- */
7
1
  import { mkdtempSync, rmSync } from "node:fs";
8
2
  import { tmpdir } from "node:os";
9
3
  import { join } from "node:path";
@@ -30,7 +30,6 @@ export default defineAction({
30
30
  ownerEmail,
31
31
  orgId: currentOrgId() ?? null,
32
32
  });
33
- // Owner is automatically a member with owner role
34
33
  await getDb().insert(schema.teamMembers).values({
35
34
  id: nanoid(),
36
35
  teamId: id,
@@ -1,10 +1,3 @@
1
- /**
2
- * Cross-tenant write (IDOR) regression tests for event-type-scoped actions
3
- * that mutate a resource owned by another tenant: revoking a private hashed
4
- * link and duplicating an event type. Mirrors the `assertAccess("event-type",
5
- * ..., <role>)` guard already used by `add-private-link.ts` /
6
- * `delete-event-type.ts`.
7
- */
8
1
  import { mkdtempSync, rmSync } from "node:fs";
9
2
  import { tmpdir } from "node:os";
10
3
  import { join } from "node:path";
@@ -164,10 +157,6 @@ beforeEach(async () => {
164
157
  setSchedulingContext({
165
158
  getDb: () => db,
166
159
  schema,
167
- // Mirrors real app wiring (templates/scheduling's server plugin): the
168
- // scheduling package's own "current user" and the framework's
169
- // request-context ALS (used by `assertAccess`) both resolve to the same
170
- // identity.
171
160
  getCurrentUserEmail: () => getRequestUserEmail(),
172
161
  });
173
162
  registerShareableResource({
@@ -17,10 +17,6 @@ export default defineAction({
17
17
  from: z.string().optional(),
18
18
  to: z.string().optional(),
19
19
  limit: z.number().optional(),
20
- /**
21
- * If true, narrow to bookings where you are the host. Defaults to false
22
- * so org-shared bookings + bookings shared with you appear too.
23
- */
24
20
  onlyMine: z.boolean().optional().default(false),
25
21
  }),
26
22
  run: async (args) => {
@@ -12,7 +12,6 @@ export default defineAction({
12
12
  run: async (args) => {
13
13
  const { getDb, schema } = getSchedulingContext();
14
14
  const email = args.userEmail ?? currentUserEmail();
15
- // Delete cache rows for all of this user's credentials
16
15
  const creds = await getDb()
17
16
  .select({ id: schema.schedulingCredentials.id })
18
17
  .from(schema.schedulingCredentials)
@@ -20,8 +20,6 @@ export default defineAction({
20
20
  try {
21
21
  await assertAccess("event-type", link.eventTypeId, "editor");
22
22
  } catch {
23
- // Keep private-link hashes unprobeable: callers without access receive
24
- // the same idempotent result as callers presenting an unknown hash.
25
23
  return { ok: true };
26
24
  }
27
25
  await db
@@ -3,8 +3,6 @@ import { z } from "zod";
3
3
 
4
4
  import { currentUserEmail } from "./_helpers.js";
5
5
 
6
- // Profile data lives in consumer-provided settings (not the scheduling schema);
7
- // this action delegates to the core `settings` subsystem via a well-known key.
8
6
  export default defineAction({
9
7
  description:
10
8
  "Update the current user's profile (name, bio, timezone, brand colors, etc.)",
@@ -20,9 +18,6 @@ export default defineAction({
20
18
  hideBranding: z.boolean().optional(),
21
19
  }),
22
20
  run: async (args) => {
23
- // The consumer template wires a `profile-settings` settings key; we just
24
- // read/merge/write it. Accessed via dynamic import to avoid a hard dep on
25
- // the core settings API here (different consumers may use alternate stores).
26
21
  const core: any = await import("@agent-native/core");
27
22
  if (core.writeSetting) {
28
23
  const existing = (await core.readSetting?.("profile-settings")) ?? {};
@@ -1,19 +1,6 @@
1
- /**
2
- * Buffer application.
3
- *
4
- * Buffers pad a slot with prep/cool-down time that the host needs but the
5
- * attendee shouldn't see as part of the meeting. A before-event buffer
6
- * means the slot reserves extra time before its displayed start; the slot
7
- * itself is still shown at the true meeting time.
8
- *
9
- * The practical effect: a slot [10:00–10:30] with before=10 means the host
10
- * is considered "busy" from 09:50 to 10:30, so it conflicts with a 09:00–09:50
11
- * meeting only if THAT meeting also has an after-buffer.
12
- */
13
1
  import type { BusyInterval } from "../shared/index.js";
14
2
  import { addMinutes } from "./time.js";
15
3
 
16
- /** Expand a busy interval to include before/after buffers. */
17
4
  export function applyBuffers(
18
5
  busy: BusyInterval,
19
6
  beforeMinutes: number,
@@ -28,11 +15,6 @@ export function applyBuffers(
28
15
  };
29
16
  }
30
17
 
31
- /**
32
- * Expand a proposed slot window (start→end) to include the event type's
33
- * before/after buffers. The expanded window is what collision detection
34
- * uses; the displayed slot remains the unexpanded window.
35
- */
36
18
  export function expandSlotForConflictCheck(
37
19
  start: Date,
38
20
  end: Date,
@@ -1,6 +1,3 @@
1
- /**
2
- * Conflict detection — does a proposed interval overlap any busy interval?
3
- */
4
1
  import type { BusyInterval } from "../shared/index.js";
5
2
  import { overlaps } from "./time.js";
6
3
 
@@ -21,7 +18,6 @@ export function hasConflict(
21
18
  return false;
22
19
  }
23
20
 
24
- /** Merge overlapping busy intervals into a canonical, non-overlapping set. */
25
21
  export function mergeBusy(busy: BusyInterval[]): BusyInterval[] {
26
22
  if (busy.length === 0) return [];
27
23
  const sorted = busy
@@ -1,14 +1,6 @@
1
1
  import { TZDate } from "@date-fns/tz";
2
2
  import { startOfWeek, startOfMonth, startOfYear, format } from "date-fns";
3
3
 
4
- /**
5
- * Booking limits enforcement.
6
- *
7
- * Limits constrain how many bookings an event type can receive in a time
8
- * window (day / week / month / year). We pass in existing booking counts
9
- * per bucket and return a function that tells us whether a proposed start
10
- * time would exceed any limit.
11
- */
12
4
  import type { BookingLimits } from "../shared/index.js";
13
5
 
14
6
  export interface BookingCounts {
@@ -53,7 +53,6 @@ describe("assignRoundRobin", () => {
53
53
  metrics,
54
54
  strategy: "lowest-recent-bookings",
55
55
  });
56
- // priority 1 > priority 2; within priority 1, weight 5 > weight 1
57
56
  expect(result?.userEmail).toBe("c@x.com");
58
57
  });
59
58
 
@@ -1,23 +1,7 @@
1
- /**
2
- * Round-robin host assignment.
3
- *
4
- * For a given event type, pick the host who should take the next booking.
5
- *
6
- * Strategies:
7
- * - "lowest-recent-bookings" — host with fewest bookings in the rolling
8
- * window (default 30 days) wins; tie-break by priority then weight.
9
- * - "weighted" — distribute by relative weight; random weighted draw that's
10
- * deterministic-per-timestamp (seeded for reproducibility in tests).
11
- * - "calibrated" — weighted but scaled by no-show rate; hosts with high
12
- * no-show rates receive fewer bookings until their rate recovers.
13
- *
14
- * Out-of-office hosts are filtered out before selection.
15
- */
16
1
  import type { Host, RoundRobinStrategy } from "../shared/index.js";
17
2
 
18
3
  export interface HostMetrics {
19
4
  recentBookingCount: number;
20
- /** 0–1; fraction of recent bookings that were no-shows */
21
5
  noShowRate: number;
22
6
  }
23
7
 
@@ -26,7 +10,6 @@ export interface AssignRoundRobinInput {
26
10
  metrics: Map<string, HostMetrics>;
27
11
  excludeEmails?: Set<string>;
28
12
  strategy: RoundRobinStrategy;
29
- /** Deterministic seed (e.g. booking timestamp) for reproducible weighted picks */
30
13
  seed?: number;
31
14
  }
32
15
 
package/src/core/rrule.ts CHANGED
@@ -1,9 +1,3 @@
1
- /**
2
- * Recurring event expansion.
3
- *
4
- * Given an RRULE string and a time range, return the list of occurrence
5
- * start times. Uses `rrule` npm package under the hood.
6
- */
7
1
  import { RRule, rrulestr } from "rrule";
8
2
 
9
3
  export function expandRecurring(
@@ -13,7 +7,6 @@ export function expandRecurring(
13
7
  rangeEnd: Date,
14
8
  maxOccurrences = 10,
15
9
  ): Date[] {
16
- // rrulestr parses RRULE:... with or without the DTSTART prefix.
17
10
  const rule = rrulestr(rruleString, { dtstart }) as RRule;
18
11
  const all = rule.between(rangeStart, rangeEnd, true);
19
12
  return all.slice(0, maxOccurrences);
package/src/core/rules.ts CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Availability rule evaluation.
3
- *
4
- * Given a Schedule (weekly rules + date overrides) and a local date in the
5
- * schedule's timezone, produce the set of available intervals on that day.
6
- *
7
- * Date overrides win over weekly rules: an empty-intervals override blocks
8
- * the day entirely; a non-empty override replaces the weekly rules for the day.
9
- */
10
1
  import type {
11
2
  AvailabilityInterval,
12
3
  DateOverride,
@@ -19,10 +10,6 @@ export interface ScheduleInput {
19
10
  dateOverrides: DateOverride[];
20
11
  }
21
12
 
22
- /**
23
- * Return the intervals available for a given local date.
24
- * Intervals are in the schedule's local HH:MM, not UTC.
25
- */
26
13
  export function evaluateAvailabilityForDate(
27
14
  schedule: ScheduleInput,
28
15
  localDate: string,
@@ -34,10 +21,6 @@ export function evaluateAvailabilityForDate(
34
21
  return weekly?.intervals ?? [];
35
22
  }
36
23
 
37
- /**
38
- * Normalize an interval list: sort by start, merge overlapping/adjacent,
39
- * drop zero-length.
40
- */
41
24
  export function normalizeIntervals(
42
25
  intervals: AvailabilityInterval[],
43
26
  ): AvailabilityInterval[] {
@@ -17,7 +17,6 @@ const utc: ScheduleInput = {
17
17
 
18
18
  describe("computeAvailableSlots", () => {
19
19
  it("enumerates 30-minute slots across a single day", () => {
20
- // Mon 2026-04-06 09:00Z → 17:00Z → 16 slots of 30 min
21
20
  const slots = computeAvailableSlots({
22
21
  duration: 30,
23
22
  minimumBookingNotice: 0,
@@ -50,7 +49,6 @@ describe("computeAvailableSlots", () => {
50
49
  rangeEnd: new Date("2026-04-07T00:00:00Z"),
51
50
  now: new Date("2026-04-06T10:00:00Z"),
52
51
  });
53
- // now=10:00, +3h → earliest bookable is 13:00
54
52
  expect(slots[0].start).toBe("2026-04-06T13:00:00.000Z");
55
53
  });
56
54
 
@@ -74,7 +72,6 @@ describe("computeAvailableSlots", () => {
74
72
  s.start < "2026-04-06T11:30:00.000Z",
75
73
  );
76
74
  expect(busy.length).toBe(0);
77
- // 11:30 slot should still be available (not overlapping)
78
75
  expect(
79
76
  slots.find((s) => s.start === "2026-04-06T11:30:00.000Z"),
80
77
  ).toBeTruthy();
@@ -94,15 +91,12 @@ describe("computeAvailableSlots", () => {
94
91
  rangeEnd: new Date("2026-04-07T00:00:00Z"),
95
92
  now: new Date("2026-04-06T00:00:00Z"),
96
93
  });
97
- // 10:30 slot + afterBuffer=15 → conflict with 11:00 busy
98
94
  expect(
99
95
  slots.find((s) => s.start === "2026-04-06T10:30:00.000Z"),
100
96
  ).toBeFalsy();
101
- // 11:30 slot - beforeBuffer=15 → 11:15, conflicts with busy end at 11:30
102
97
  expect(
103
98
  slots.find((s) => s.start === "2026-04-06T11:30:00.000Z"),
104
99
  ).toBeFalsy();
105
- // 12:00 slot - 15 → 11:45, fully clear of busy interval [11:00, 11:30)
106
100
  expect(
107
101
  slots.find((s) => s.start === "2026-04-06T12:00:00.000Z"),
108
102
  ).toBeTruthy();
@@ -118,7 +112,6 @@ describe("computeAvailableSlots", () => {
118
112
  periodType: "unlimited",
119
113
  schedule: utc,
120
114
  busy: [],
121
- // 2026-04-04 is a Saturday
122
115
  rangeStart: new Date("2026-04-04T00:00:00Z"),
123
116
  rangeEnd: new Date("2026-04-05T00:00:00Z"),
124
117
  now: new Date("2026-04-04T00:00:00Z"),
@@ -141,7 +134,6 @@ describe("computeAvailableSlots", () => {
141
134
  rangeEnd: new Date("2026-04-10T00:00:00Z"),
142
135
  now: new Date("2026-04-06T08:00:00Z"),
143
136
  });
144
- // All slots must fall within [now, now+1d]
145
137
  for (const s of slots) {
146
138
  expect(new Date(s.end).getTime()).toBeLessThanOrEqual(
147
139
  new Date("2026-04-07T08:00:00Z").getTime(),
@@ -150,7 +142,6 @@ describe("computeAvailableSlots", () => {
150
142
  });
151
143
 
152
144
  it("handles DST transition (spring forward) in America/Los_Angeles", () => {
153
- // 2026-03-08 is the spring-forward date in US; 2am→3am.
154
145
  const tz: ScheduleInput = {
155
146
  timezone: "America/Los_Angeles",
156
147
  weeklyAvailability: [
@@ -171,9 +162,8 @@ describe("computeAvailableSlots", () => {
171
162
  rangeEnd: new Date("2026-03-09T00:00:00Z"),
172
163
  now: new Date("2026-03-08T00:00:00Z"),
173
164
  });
174
- // 9-17 PDT on 2026-03-08 = 16:00 UTC to 00:00 UTC next day → 16 30-min slots
175
165
  expect(slots.length).toBeGreaterThanOrEqual(15);
176
- expect(slots[0].start).toBe("2026-03-08T16:00:00.000Z"); // 9am PDT = 16:00 UTC
166
+ expect(slots[0].start).toBe("2026-03-08T16:00:00.000Z");
177
167
  });
178
168
 
179
169
  it("respects seats-per-time-slot", () => {
package/src/core/slots.ts CHANGED
@@ -1,21 +1,3 @@
1
- /**
2
- * Slot computation — the heart of the scheduling package.
3
- *
4
- * Input: an event type's config (duration, buffers, limits, period),
5
- * a schedule (weekly hours + date overrides),
6
- * a set of busy intervals (from external calendars + existing bookings),
7
- * a time range to compute over.
8
- * Output: an array of Slot objects (UTC start/end) that are available
9
- * and pass every constraint.
10
- *
11
- * Correctness priorities, in order:
12
- * 1. Never return a slot in the past (respect `now + minimumBookingNotice`)
13
- * 2. Never return a slot that overlaps a busy interval (with buffers)
14
- * 3. Never return a slot outside the schedule's available windows
15
- * 4. Never exceed booking limits (per-day/week/month/year)
16
- * 5. Respect the event type's period (rolling / range / unlimited)
17
- * 6. Correctness across DST transitions
18
- */
19
1
  import type {
20
2
  Slot,
21
3
  BusyInterval,
@@ -34,38 +16,25 @@ import {
34
16
  } from "./time.js";
35
17
 
36
18
  export interface ComputeSlotsInput {
37
- /** Event type duration, minutes */
38
19
  duration: number;
39
- /** Minutes before event start where the event type becomes unbookable; 0 = up to now */
40
20
  minimumBookingNotice: number;
41
- /** Buffers in minutes applied to any existing busy interval and the candidate slot */
42
21
  beforeEventBuffer: number;
43
22
  afterEventBuffer: number;
44
- /** Null = increment by duration */
45
23
  slotInterval: number | null;
46
24
  periodType: PeriodType;
47
25
  periodDays?: number;
48
26
  periodStartDate?: string;
49
27
  periodEndDate?: string;
50
28
  bookingLimits?: BookingLimits;
51
- /** Schedule — intervals in schedule's timezone */
52
29
  schedule: ScheduleInput;
53
- /** Busy intervals in UTC (already aggregated from all sources) */
54
30
  busy: BusyInterval[];
55
- /** Existing bookings grouped by bucket key — used for limit enforcement */
56
31
  bookingCounts?: BookingCounts;
57
- /** Week start for limit-bucketing */
58
32
  weekStartsOn?: 0 | 1;
59
- /** Range to compute over, UTC */
60
33
  rangeStart: Date;
61
34
  rangeEnd: Date;
62
- /** Current time (UTC). Defaults to now. */
63
35
  now?: Date;
64
- /** Seats per slot — if > 1, slot stays available until seats exhausted */
65
36
  seatsPerTimeSlot?: number;
66
- /** Seat count already reserved, keyed by ISO start */
67
37
  seatsTaken?: Map<string, number>;
68
- /** Timezone in which the Booker displays the slots (for limit buckets) */
69
38
  viewerTimezone?: string;
70
39
  }
71
40
 
@@ -82,7 +51,6 @@ export function computeAvailableSlots(input: ComputeSlotsInput): Slot[] {
82
51
  const viewerTz = input.viewerTimezone ?? input.schedule.timezone;
83
52
  const slots: Slot[] = [];
84
53
 
85
- // Enumerate each local day in the schedule's timezone that the range touches.
86
54
  const dates = localDatesInRange(
87
55
  rangeStart,
88
56
  rangeEnd,
@@ -114,19 +82,16 @@ export function computeAvailableSlots(input: ComputeSlotsInput): Slot[] {
114
82
  while (addMinutes(slotStart, input.duration) <= dayEndUtc) {
115
83
  const slotEnd = addMinutes(slotStart, input.duration);
116
84
 
117
- // Cap to range
118
85
  if (slotStart < rangeStart || slotEnd > rangeEnd) {
119
86
  slotStart = addMinutes(slotStart, interval);
120
87
  continue;
121
88
  }
122
89
 
123
- // Respect minimum notice
124
90
  if (slotStart < minBookableTime) {
125
91
  slotStart = addMinutes(slotStart, interval);
126
92
  continue;
127
93
  }
128
94
 
129
- // Conflict check (expanded with buffers)
130
95
  const expanded = expandSlotForConflictCheck(
131
96
  slotStart,
132
97
  slotEnd,
@@ -138,7 +103,6 @@ export function computeAvailableSlots(input: ComputeSlotsInput): Slot[] {
138
103
  continue;
139
104
  }
140
105
 
141
- // Booking limits (in viewer's timezone for perDay buckets)
142
106
  if (
143
107
  input.bookingCounts &&
144
108
  exceedsLimits(
@@ -153,7 +117,6 @@ export function computeAvailableSlots(input: ComputeSlotsInput): Slot[] {
153
117
  continue;
154
118
  }
155
119
 
156
- // Seats
157
120
  const seatsTaken = input.seatsTaken?.get(slotStart.toISOString()) ?? 0;
158
121
  const seatsRemaining =
159
122
  input.seatsPerTimeSlot != null
@@ -189,7 +152,6 @@ function capToPeriodStart(
189
152
  const start = new Date(input.periodStartDate);
190
153
  return rangeStart > start ? rangeStart : start;
191
154
  }
192
- // Rolling + unlimited don't restrict start
193
155
  return rangeStart > now ? rangeStart : now;
194
156
  }
195
157
 
@@ -210,8 +172,6 @@ function capToPeriodEnd(
210
172
  }
211
173
 
212
174
  function dayOfWeekForLocalDate(localDate: string, timezone: string): number {
213
- // Construct a UTC date that corresponds to noon on that local date in the
214
- // target timezone, then ask what day-of-week it is in that timezone.
215
175
  const noon = zonedTimeToUtc(localDate, "12:00", timezone);
216
176
  return getDayOfWeek(noon, timezone);
217
177
  }
package/src/core/time.ts CHANGED
@@ -1,43 +1,22 @@
1
- /**
2
- * DST-safe time math.
3
- *
4
- * Rules:
5
- * - Never use raw `Date` arithmetic for anything timezone-aware.
6
- * - Always work in UTC internally; convert at the edges.
7
- * - Date strings are ISO-8601 (UTC). Local-day strings are "YYYY-MM-DD".
8
- * - Times of day are "HH:MM" (24h, no seconds).
9
- * - Timezones are IANA identifiers like "America/Los_Angeles".
10
- */
11
1
  import { TZDate } from "@date-fns/tz";
12
2
  import { addMinutes as _addMinutes, differenceInMinutes } from "date-fns";
13
3
 
14
- /** Parse an ISO 8601 string to a UTC Date. */
15
4
  export function parseISO(iso: string): Date {
16
5
  return new Date(iso);
17
6
  }
18
7
 
19
- /** Format a Date to an ISO 8601 UTC string ending in "Z". */
20
8
  export function toISO(d: Date): string {
21
9
  return d.toISOString();
22
10
  }
23
11
 
24
- /** Add minutes to a Date (UTC-safe; DST-safe because we stay in UTC). */
25
12
  export function addMinutes(d: Date, minutes: number): Date {
26
13
  return _addMinutes(d, minutes);
27
14
  }
28
15
 
29
- /** Minutes between two Dates (end - start). Can be negative. */
30
16
  export function minutesBetween(start: Date, end: Date): number {
31
17
  return differenceInMinutes(end, start);
32
18
  }
33
19
 
34
- /**
35
- * Build a UTC Date from a local date + time of day + timezone.
36
- *
37
- * Example:
38
- * zonedTimeToUtc("2026-04-04", "09:00", "America/Los_Angeles")
39
- * → Date where UTC hour depends on whether PDT or PST is active.
40
- */
41
20
  export function zonedTimeToUtc(
42
21
  localDate: string,
43
22
  localTime: string,
@@ -45,13 +24,10 @@ export function zonedTimeToUtc(
45
24
  ): Date {
46
25
  const [y, m, d] = localDate.split("-").map(Number);
47
26
  const [h, mm] = localTime.split(":").map(Number);
48
- // TZDate constructor takes local-in-timezone components and produces a
49
- // Date whose UTC value is correct for that wall-clock time in `timezone`.
50
27
  const tz = new TZDate(y, (m ?? 1) - 1, d ?? 1, h ?? 0, mm ?? 0, 0, timezone);
51
28
  return new Date(tz.getTime());
52
29
  }
53
30
 
54
- /** Format a UTC Date into a "YYYY-MM-DD" string in the target timezone. */
55
31
  export function formatLocalDate(d: Date, timezone: string): string {
56
32
  const tz = new TZDate(d.getTime(), timezone);
57
33
  const yyyy = tz.getFullYear();
@@ -60,7 +36,6 @@ export function formatLocalDate(d: Date, timezone: string): string {
60
36
  return `${yyyy}-${mm}-${dd}`;
61
37
  }
62
38
 
63
- /** Format a UTC Date into a "HH:MM" string in the target timezone. */
64
39
  export function formatLocalTime(d: Date, timezone: string): string {
65
40
  const tz = new TZDate(d.getTime(), timezone);
66
41
  const hh = String(tz.getHours()).padStart(2, "0");
@@ -68,13 +43,11 @@ export function formatLocalTime(d: Date, timezone: string): string {
68
43
  return `${hh}:${mm}`;
69
44
  }
70
45
 
71
- /** Day of week (0=Sun, 6=Sat) for a UTC Date in the target timezone. */
72
46
  export function getDayOfWeek(d: Date, timezone: string): number {
73
47
  const tz = new TZDate(d.getTime(), timezone);
74
48
  return tz.getDay();
75
49
  }
76
50
 
77
- /** True if two intervals overlap (end-exclusive). */
78
51
  export function overlaps(
79
52
  a: { start: Date; end: Date },
80
53
  b: { start: Date; end: Date },
@@ -82,7 +55,6 @@ export function overlaps(
82
55
  return a.start < b.end && b.start < a.end;
83
56
  }
84
57
 
85
- /** Clamp a date range to a window. Returns null if they don't intersect. */
86
58
  export function clampRange(
87
59
  range: { start: Date; end: Date },
88
60
  window: { start: Date; end: Date },
@@ -93,10 +65,6 @@ export function clampRange(
93
65
  return { start, end };
94
66
  }
95
67
 
96
- /**
97
- * Iterate UTC Date objects from start (inclusive) to end (exclusive) by
98
- * stepping `stepMinutes`. Safe across DST because we never touch local time.
99
- */
100
68
  export function* steppedDates(
101
69
  start: Date,
102
70
  end: Date,
@@ -109,10 +77,6 @@ export function* steppedDates(
109
77
  }
110
78
  }
111
79
 
112
- /**
113
- * Enumerate "YYYY-MM-DD" local dates in `timezone` from `startUtc` to `endUtc`.
114
- * Useful for iterating per-day availability over a booking window.
115
- */
116
80
  export function localDatesInRange(
117
81
  startUtc: Date,
118
82
  endUtc: Date,
@@ -129,7 +93,6 @@ export function localDatesInRange(
129
93
  }
130
94
  cursor = addMinutes(cursor, 60);
131
95
  }
132
- // Include final local day even if loop stopped exactly at midnight boundary
133
96
  const endLocal = formatLocalDate(new Date(endUtc.getTime() - 1), timezone);
134
97
  if (out[out.length - 1] !== endLocal) out.push(endLocal);
135
98
  return out;
package/src/index.ts CHANGED
@@ -1,14 +1,3 @@
1
- /**
2
- * @agent-native/scheduling
3
- *
4
- * Root entry — re-exports shared types and constants only.
5
- * Subpath imports are the primary API surface:
6
- *
7
- * import { eventTypes, bookings } from "@agent-native/scheduling/schema";
8
- * import { computeAvailableSlots } from "@agent-native/scheduling/core";
9
- * import { useSlots } from "@agent-native/scheduling/react";
10
- */
11
-
12
1
  export * from "./shared/index.js";
13
2
  export { MANIFEST } from "./manifest.js";
14
3
  export { VERSION } from "./version.js";