@aranova/tracking-next 1.1.0 → 1.2.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.
package/dist/index.d.mts CHANGED
@@ -6,9 +6,9 @@ export { A as ALL_AUTOMATIC_EVENT_NAMES, a as ALL_LEAD_EVENT_NAMES, b as ALL_MAN
6
6
  export { A as AranovaApiError, f as formatDateInTz } from './date-BHetnL7s.mjs';
7
7
  export { AdPlatformTracking, AdPlatformTrackingProps, ConsentBanner, ConsentBannerProps, CreateTrackingOptions, CreateTrackingResult, GoogleAdsTracking, GoogleAdsTrackingProps, PhoneField, PhoneFieldApi, PhoneFieldProps, PhoneInputProps, TrackingProviderProps, UseConsentResult, UseCookiePreferencesOptions, UseCookiePreferencesResult, UsePhoneFieldOptions, createTracking, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams } from '@aranova/tracking-next/client';
8
8
  import { CalendarReadClient, ComputeSlotsOptions, OpeningHours, Slot } from './calendar.mjs';
9
- export { BusyQuery, CalendarReadClientConfig, ComputeSlotsResult, OpeningInterval, computeSlots, createCalendarReadClient, durationMinutes, endOfDayInTz, formatSlotRange, groupSlotsByDay, instantFromWallClock, isCalendarError, localDateKey, offsetMinutesAt, resolveBrowserTimeZone, startOfDayInTz } from './calendar.mjs';
10
- import { a as Booking, B as BookingCreateInput, d as BusyInterval } from './transport-I_cMSFQ6.mjs';
11
- export { e as BOOKING_SOURCES, f as BOOKING_STATUSES, g as BookingAttendee, c as BookingCancelInput, h as BookingCustomer, i as BookingLocation, j as BookingMetadata, k as BookingReminders, b as BookingRescheduleInput, l as BookingStatus, m as BusyResponse, n as CALENDAR_ERROR_CODES, o as CALENDAR_METADATA_SCHEMA_VERSION, p as CALENDAR_SYNC_STATUSES, q as CalendarErrorCode, r as CalendarSummary, s as CalendarSyncStatus, C as CalendarTransportConfig, G as GOOGLE_SYNC_STATES, t as GoogleSyncState, J as JsonScalar, M as MAX_ATTENDEES, u as MAX_BOOKING_MINUTES, v as MAX_REMINDER_MINUTES, w as MAX_REMINDER_OVERRIDES, x as bookingAttendeeSchema, y as bookingCancelSchema, z as bookingCreateSchema, A as bookingCustomerSchema, D as bookingLocationNullableSchema, E as bookingLocationSchema, F as bookingMetadataSchema, H as bookingReminderSchema, I as bookingRemindersSchema, K as bookingRescheduleSchema } from './transport-I_cMSFQ6.mjs';
9
+ export { BookingAttribution, BusyQuery, CalendarReadClientConfig, ComputeSlotsResult, OpeningInterval, computeSlots, createCalendarReadClient, durationMinutes, endOfDayInTz, formatSlotRange, getBookingAttribution, groupSlotsByDay, instantFromWallClock, isCalendarError, localDateKey, offsetMinutesAt, resolveBrowserTimeZone, startOfDayInTz } from './calendar.mjs';
10
+ import { a as Booking, B as BookingCreateInput, d as BusyInterval } from './transport-BlXQZs9Z.mjs';
11
+ export { e as BOOKING_SOURCES, f as BOOKING_STATUSES, g as BookingAttendee, c as BookingCancelInput, h as BookingCustomer, i as BookingLocation, j as BookingMetadata, k as BookingReminders, b as BookingRescheduleInput, l as BookingStatus, m as BusyResponse, n as CALENDAR_ERROR_CODES, o as CALENDAR_METADATA_SCHEMA_VERSION, p as CALENDAR_SYNC_STATUSES, q as CalendarErrorCode, r as CalendarSummary, s as CalendarSyncStatus, C as CalendarTransportConfig, G as GOOGLE_SYNC_STATES, t as GoogleSyncState, J as JsonScalar, M as MAX_ATTENDEES, u as MAX_BOOKING_MINUTES, v as MAX_REMINDER_MINUTES, w as MAX_REMINDER_OVERRIDES, x as bookingAttendeeSchema, y as bookingCancelSchema, z as bookingCreateSchema, A as bookingCustomerSchema, D as bookingLocationNullableSchema, E as bookingLocationSchema, F as bookingMetadataSchema, H as bookingReminderSchema, I as bookingRemindersSchema, K as bookingRescheduleSchema } from './transport-BlXQZs9Z.mjs';
12
12
  import { ReactNode } from 'react';
13
13
  export { CountryCode } from 'libphonenumber-js';
14
14
  import 'zod/mini';
@@ -154,7 +154,8 @@ interface UseBookingFormResult {
154
154
  * Submit state for a booking.
155
155
  *
156
156
  * Posts to the SITE's own route handler, not to us: a booking needs a secret
157
- * key, which must never reach a browser bundle.
157
+ * key, which must never reach a browser bundle. The visit's attribution fills any
158
+ * `metadata` key the caller left unset, so the booking links to its journey.
158
159
  */
159
160
  declare function useBookingForm(options?: UseBookingFormOptions): UseBookingFormResult;
160
161
 
package/dist/index.d.ts CHANGED
@@ -6,9 +6,9 @@ export { A as ALL_AUTOMATIC_EVENT_NAMES, a as ALL_LEAD_EVENT_NAMES, b as ALL_MAN
6
6
  export { A as AranovaApiError, f as formatDateInTz } from './date-BHetnL7s.js';
7
7
  export { AdPlatformTracking, AdPlatformTrackingProps, ConsentBanner, ConsentBannerProps, CreateTrackingOptions, CreateTrackingResult, GoogleAdsTracking, GoogleAdsTrackingProps, PhoneField, PhoneFieldApi, PhoneFieldProps, PhoneInputProps, TrackingProviderProps, UseConsentResult, UseCookiePreferencesOptions, UseCookiePreferencesResult, UsePhoneFieldOptions, createTracking, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams } from '@aranova/tracking-next/client';
8
8
  import { CalendarReadClient, ComputeSlotsOptions, OpeningHours, Slot } from './calendar.js';
9
- export { BusyQuery, CalendarReadClientConfig, ComputeSlotsResult, OpeningInterval, computeSlots, createCalendarReadClient, durationMinutes, endOfDayInTz, formatSlotRange, groupSlotsByDay, instantFromWallClock, isCalendarError, localDateKey, offsetMinutesAt, resolveBrowserTimeZone, startOfDayInTz } from './calendar.js';
10
- import { a as Booking, B as BookingCreateInput, d as BusyInterval } from './transport-CDmGOiCq.js';
11
- export { e as BOOKING_SOURCES, f as BOOKING_STATUSES, g as BookingAttendee, c as BookingCancelInput, h as BookingCustomer, i as BookingLocation, j as BookingMetadata, k as BookingReminders, b as BookingRescheduleInput, l as BookingStatus, m as BusyResponse, n as CALENDAR_ERROR_CODES, o as CALENDAR_METADATA_SCHEMA_VERSION, p as CALENDAR_SYNC_STATUSES, q as CalendarErrorCode, r as CalendarSummary, s as CalendarSyncStatus, C as CalendarTransportConfig, G as GOOGLE_SYNC_STATES, t as GoogleSyncState, J as JsonScalar, M as MAX_ATTENDEES, u as MAX_BOOKING_MINUTES, v as MAX_REMINDER_MINUTES, w as MAX_REMINDER_OVERRIDES, x as bookingAttendeeSchema, y as bookingCancelSchema, z as bookingCreateSchema, A as bookingCustomerSchema, D as bookingLocationNullableSchema, E as bookingLocationSchema, F as bookingMetadataSchema, H as bookingReminderSchema, I as bookingRemindersSchema, K as bookingRescheduleSchema } from './transport-CDmGOiCq.js';
9
+ export { BookingAttribution, BusyQuery, CalendarReadClientConfig, ComputeSlotsResult, OpeningInterval, computeSlots, createCalendarReadClient, durationMinutes, endOfDayInTz, formatSlotRange, getBookingAttribution, groupSlotsByDay, instantFromWallClock, isCalendarError, localDateKey, offsetMinutesAt, resolveBrowserTimeZone, startOfDayInTz } from './calendar.js';
10
+ import { a as Booking, B as BookingCreateInput, d as BusyInterval } from './transport-CAZYrZbB.js';
11
+ export { e as BOOKING_SOURCES, f as BOOKING_STATUSES, g as BookingAttendee, c as BookingCancelInput, h as BookingCustomer, i as BookingLocation, j as BookingMetadata, k as BookingReminders, b as BookingRescheduleInput, l as BookingStatus, m as BusyResponse, n as CALENDAR_ERROR_CODES, o as CALENDAR_METADATA_SCHEMA_VERSION, p as CALENDAR_SYNC_STATUSES, q as CalendarErrorCode, r as CalendarSummary, s as CalendarSyncStatus, C as CalendarTransportConfig, G as GOOGLE_SYNC_STATES, t as GoogleSyncState, J as JsonScalar, M as MAX_ATTENDEES, u as MAX_BOOKING_MINUTES, v as MAX_REMINDER_MINUTES, w as MAX_REMINDER_OVERRIDES, x as bookingAttendeeSchema, y as bookingCancelSchema, z as bookingCreateSchema, A as bookingCustomerSchema, D as bookingLocationNullableSchema, E as bookingLocationSchema, F as bookingMetadataSchema, H as bookingReminderSchema, I as bookingRemindersSchema, K as bookingRescheduleSchema } from './transport-CAZYrZbB.js';
12
12
  import { ReactNode } from 'react';
13
13
  export { CountryCode } from 'libphonenumber-js';
14
14
  import 'zod/mini';
@@ -154,7 +154,8 @@ interface UseBookingFormResult {
154
154
  * Submit state for a booking.
155
155
  *
156
156
  * Posts to the SITE's own route handler, not to us: a booking needs a secret
157
- * key, which must never reach a browser bundle.
157
+ * key, which must never reach a browser bundle. The visit's attribution fills any
158
+ * `metadata` key the caller left unset, so the booking links to its journey.
158
159
  */
159
160
  declare function useBookingForm(options?: UseBookingFormOptions): UseBookingFormResult;
160
161
 
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ __export(src_exports, {
82
82
  formatPhoneAsTyped: () => formatPhoneAsTyped,
83
83
  formatSlotRange: () => formatSlotRange,
84
84
  fromMinor: () => fromMinor,
85
+ getBookingAttribution: () => getBookingAttribution,
85
86
  getConsentChoice: () => getConsentChoice,
86
87
  getConsentState: () => getConsentState,
87
88
  getEventSemantics: () => getEventSemantics,
@@ -613,6 +614,35 @@ function buildLandingPayloadFields(sessionId, override) {
613
614
  };
614
615
  }
615
616
 
617
+ // ../tracking-core/src/session.ts
618
+ var VISITOR_STORAGE_KEY = "aranova_tracking_visitor";
619
+ var SESSION_STORAGE_KEY = "aranova_tracking_session";
620
+ var SESSION_IDLE_MS = 30 * 60 * 1e3;
621
+ function readLocalStorage(key) {
622
+ try {
623
+ return window.localStorage.getItem(key);
624
+ } catch {
625
+ return null;
626
+ }
627
+ }
628
+ function peekVisitorId() {
629
+ if (typeof window === "undefined") return null;
630
+ const existing = readLocalStorage(VISITOR_STORAGE_KEY);
631
+ return existing && existing.length > 0 ? existing : null;
632
+ }
633
+ function peekSessionId(now = Date.now()) {
634
+ if (typeof window === "undefined") return null;
635
+ const raw = readLocalStorage(SESSION_STORAGE_KEY);
636
+ if (!raw) return null;
637
+ try {
638
+ const parsed = JSON.parse(raw);
639
+ if (typeof parsed.id !== "string" || typeof parsed.last_event_at !== "number") return null;
640
+ return now - parsed.last_event_at <= SESSION_IDLE_MS ? parsed.id : null;
641
+ } catch {
642
+ return null;
643
+ }
644
+ }
645
+
616
646
  // ../tracking-core/src/cross-site-link.ts
617
647
  var LINK_PARAM = "_arv";
618
648
  var LINK_TOKEN_TTL_MS = 5 * 60 * 1e3;
@@ -1775,9 +1805,19 @@ var zErrorCode = z2.enum([
1775
1805
  "bad_request",
1776
1806
  "not_found",
1777
1807
  "conflict",
1808
+ "gone",
1778
1809
  "incompatible_payload",
1779
1810
  "repeat_confirmation_required",
1780
1811
  "idempotency_conflict",
1812
+ "slot_unavailable",
1813
+ "calendar_not_connected",
1814
+ "calendar_disconnected",
1815
+ "booking_not_found",
1816
+ "booking_not_cancellable",
1817
+ "booking_not_reschedulable",
1818
+ "manage_token_invalid",
1819
+ "google_error",
1820
+ "calendar_not_owned",
1781
1821
  "validation_error",
1782
1822
  "unknown_service",
1783
1823
  "invalid_sale_properties",
@@ -1841,6 +1881,7 @@ var zSmsWithheldReason = z2.enum([
1841
1881
  "no_handler",
1842
1882
  "dispatch_already_recorded",
1843
1883
  "text_back_disabled",
1884
+ "number_text_back_disabled",
1844
1885
  "number_not_active",
1845
1886
  "a2p_registration_required",
1846
1887
  "alert_recipient_cannot_receive_sms",
@@ -2047,6 +2088,40 @@ function phoneField(name, raw, country) {
2047
2088
  // src/index.ts
2048
2089
  var import_client = require("@aranova/tracking-next/client");
2049
2090
 
2091
+ // ../tracking-core/src/resources/calendar/attribution.ts
2092
+ var LIMITS = {
2093
+ session_id: 64,
2094
+ visitor_id: 64,
2095
+ gclid: 256,
2096
+ fbclid: 256,
2097
+ utm_source: 128,
2098
+ utm_medium: 128,
2099
+ utm_campaign: 128,
2100
+ utm_term: 128,
2101
+ utm_content: 128
2102
+ };
2103
+ function getBookingAttribution() {
2104
+ if (typeof window === "undefined") return {};
2105
+ const params = getTrackingParamsFromCookieReader(getCookieValueFromDocument);
2106
+ const values = {
2107
+ session_id: peekSessionId(),
2108
+ visitor_id: peekVisitorId(),
2109
+ gclid: params.gclid,
2110
+ fbclid: params.fbclid,
2111
+ utm_source: params.utm_source,
2112
+ utm_medium: params.utm_medium,
2113
+ utm_campaign: params.utm_campaign,
2114
+ utm_term: params.utm_term,
2115
+ utm_content: params.utm_content
2116
+ };
2117
+ const attribution = {};
2118
+ for (const key of Object.keys(LIMITS)) {
2119
+ const value = values[key];
2120
+ if (value) attribution[key] = value.slice(0, LIMITS[key]);
2121
+ }
2122
+ return attribution;
2123
+ }
2124
+
2050
2125
  // ../tracking-core/src/resources/calendar/transport.ts
2051
2126
  async function calendarRequest(config, method, path, body) {
2052
2127
  return apiRequest(config, method, `/calendar${path}`, body);
@@ -2179,22 +2254,27 @@ var zBookingRescheduleSchema = z4.strictObject({
2179
2254
  time_zone: z4.string().check(z4.minLength(1), z4.maxLength(64), z4.regex(/^[A-Za-z0-9_+\-\/]+$/))
2180
2255
  });
2181
2256
  var zBookingResponse = z4.strictObject({
2257
+ all_day: z4.boolean(),
2182
2258
  appointment_type: z4.nullable(z4.string()),
2183
2259
  calendar_key: z4.string(),
2260
+ created_at: z4.iso.datetime({ offset: true }),
2184
2261
  customer: zBookingCustomerOutput,
2185
2262
  end_at: z4.iso.datetime({ offset: true }),
2263
+ google_event_id: z4.nullable(z4.string()),
2186
2264
  google_html_link: z4.nullable(z4.string()),
2187
- google_sync_state: z4.string(),
2265
+ google_sync_state: z4.enum(["queued", "synced", "uncertain", "failed", "drifted"]),
2266
+ ical_uid: z4.nullable(z4.string()),
2188
2267
  id: z4.string(),
2189
2268
  manage_token: z4.nullable(z4.string()),
2190
2269
  meet_url: z4.nullable(z4.string()),
2270
+ organizer_email: z4.nullable(z4.string()),
2191
2271
  start_at: z4.iso.datetime({ offset: true }),
2192
- status: z4.string(),
2272
+ status: z4.enum(["pending", "confirmed", "cancelled", "no_show"]),
2193
2273
  time_zone: z4.string(),
2194
2274
  title: z4.string()
2195
2275
  });
2196
2276
  var zBookingSource = z4.enum(["website", "phone", "walk_in", "import", "operator"]);
2197
- var zBookingMetadata = z4.strictObject({
2277
+ var zBookingMetadataInput = z4.strictObject({
2198
2278
  custom: z4.nullish(
2199
2279
  z4.record(z4.string(), z4.nullable(z4.union([z4.string(), z4.int(), z4.number(), z4.boolean()])))
2200
2280
  ),
@@ -2214,6 +2294,40 @@ var zBookingMetadata = z4.strictObject({
2214
2294
  utm_term: z4.nullish(z4.string().check(z4.maxLength(128))),
2215
2295
  visitor_id: z4.nullish(z4.string().check(z4.maxLength(64)))
2216
2296
  });
2297
+ var zBusySource = z4.enum(["google", "booking"]);
2298
+ var zBusyInterval = z4.strictObject({
2299
+ end_at: z4.iso.datetime({ offset: true }),
2300
+ source: zBusySource,
2301
+ start_at: z4.iso.datetime({ offset: true })
2302
+ });
2303
+ var zBusyWindow = z4.strictObject({
2304
+ since: z4.iso.datetime({ offset: true }),
2305
+ until: z4.iso.datetime({ offset: true })
2306
+ });
2307
+ var zBusyResponse = z4.strictObject({
2308
+ busy: z4.array(zBusyInterval),
2309
+ calendar_key: z4.string(),
2310
+ generated_at: z4.iso.datetime({ offset: true }),
2311
+ time_zone: z4.string(),
2312
+ window: zBusyWindow
2313
+ });
2314
+ var zCalendarSummary = z4.strictObject({
2315
+ allow_overlaps: z4.boolean(),
2316
+ calendar_key: z4.string(),
2317
+ connected_at: z4.nullable(z4.iso.datetime({ offset: true })),
2318
+ google_account_email: z4.nullable(z4.string()),
2319
+ granted_scopes: z4.nullable(z4.array(z4.string())),
2320
+ label: z4.string(),
2321
+ status: z4.enum([
2322
+ "pending_invite",
2323
+ "awaiting_calendar_selection",
2324
+ "connected",
2325
+ "needs_reconnect",
2326
+ "revoked",
2327
+ "disabled"
2328
+ ]),
2329
+ time_zone: z4.string()
2330
+ });
2217
2331
  var zCustomLocation = z4.strictObject({
2218
2332
  kind: z4.literal("custom"),
2219
2333
  text: z4.string().check(z4.maxLength(1024))
@@ -2233,9 +2347,19 @@ var zErrorCode2 = z4.enum([
2233
2347
  "bad_request",
2234
2348
  "not_found",
2235
2349
  "conflict",
2350
+ "gone",
2236
2351
  "incompatible_payload",
2237
2352
  "repeat_confirmation_required",
2238
2353
  "idempotency_conflict",
2354
+ "slot_unavailable",
2355
+ "calendar_not_connected",
2356
+ "calendar_disconnected",
2357
+ "booking_not_found",
2358
+ "booking_not_cancellable",
2359
+ "booking_not_reschedulable",
2360
+ "manage_token_invalid",
2361
+ "google_error",
2362
+ "calendar_not_owned",
2239
2363
  "validation_error",
2240
2364
  "unknown_service",
2241
2365
  "invalid_sale_properties",
@@ -2270,15 +2394,16 @@ var zBookingCreateSchema = z4.strictObject({
2270
2394
  buffer_before_minutes: z4._default(z4.optional(z4.int().check(z4.gte(0), z4.lte(1440))), 0),
2271
2395
  calendar_key: z4.string().check(z4.minLength(1), z4.maxLength(64)),
2272
2396
  customer: zBookingCustomerInput,
2397
+ date: z4.nullish(z4.iso.date()),
2273
2398
  description: z4.nullish(z4.string().check(z4.maxLength(8e3))),
2274
2399
  duration_minutes: z4.nullish(z4.int().check(z4.gt(0), z4.lte(1440))),
2275
2400
  end_at: z4.nullish(z4.iso.datetime({ offset: true })),
2276
2401
  idempotency_key: z4.nullish(z4.string().check(z4.maxLength(128))),
2277
2402
  location: z4.nullish(z4.union([zMeetLocation, zPhysicalLocation, zPhoneLocation, zCustomLocation])),
2278
- metadata: z4.nullish(zBookingMetadata),
2403
+ metadata: z4.nullish(zBookingMetadataInput),
2279
2404
  reminders: z4.nullish(zBookingReminders),
2280
2405
  send_invites: z4._default(z4.optional(z4.boolean()), true),
2281
- start_at: z4.iso.datetime({ offset: true }),
2406
+ start_at: z4.nullish(z4.iso.datetime({ offset: true })),
2282
2407
  time_zone: z4.string().check(z4.minLength(1), z4.maxLength(64), z4.regex(/^[A-Za-z0-9_+\-\/]+$/)),
2283
2408
  title: z4.nullish(z4.string().check(z4.maxLength(300)))
2284
2409
  });
@@ -2312,22 +2437,17 @@ var MAX_REMINDER_OVERRIDES = 5;
2312
2437
  var MAX_REMINDER_MINUTES = 40320;
2313
2438
  var MAX_BOOKING_MINUTES = 1440;
2314
2439
  var BOOKING_SOURCES = ["website", "phone", "walk_in", "import", "operator"];
2315
- var BOOKING_STATUSES = ["pending", "confirmed", "cancelled", "no_show"];
2316
- var GOOGLE_SYNC_STATES = ["queued", "synced", "uncertain", "failed", "drifted"];
2317
- var CALENDAR_SYNC_STATUSES = [
2318
- "pending_invite",
2319
- "awaiting_calendar_selection",
2320
- "connected",
2321
- "needs_reconnect",
2322
- "revoked",
2323
- "disabled"
2324
- ];
2440
+ var BOOKING_STATUSES = zBookingResponse.shape.status.options;
2441
+ var GOOGLE_SYNC_STATES = zBookingResponse.shape.google_sync_state.options;
2442
+ var CALENDAR_SYNC_STATUSES = zCalendarSummary.shape.status.options;
2325
2443
  var CALENDAR_ERROR_CODES = [
2326
2444
  "slot_unavailable",
2327
2445
  "calendar_not_connected",
2328
2446
  "calendar_disconnected",
2329
2447
  "booking_not_found",
2330
- "outside_booking_window",
2448
+ "booking_not_cancellable",
2449
+ "booking_not_reschedulable",
2450
+ "manage_token_invalid",
2331
2451
  "rate_limited",
2332
2452
  "google_error"
2333
2453
  ];
@@ -2348,7 +2468,7 @@ var ianaTz = z5.string().check(
2348
2468
  var bookingAttendeeSchema = zBookingAttendee;
2349
2469
  var bookingReminderSchema = zBookingReminder;
2350
2470
  var bookingCustomerSchema = zBookingCustomerInput;
2351
- var bookingMetadataSchema = z5.extend(zBookingMetadata, {
2471
+ var bookingMetadataSchema = z5.extend(zBookingMetadataInput, {
2352
2472
  custom: z5.nullish(
2353
2473
  z5.record(z5.string(), z5.union([z5.string(), z5.int(), z5.number(), z5.boolean(), z5.null()]))
2354
2474
  )
@@ -2377,6 +2497,24 @@ function refineEndXor(value, ctx) {
2377
2497
  addIssue2(ctx, ["end_at"], "end_at must be after start_at");
2378
2498
  }
2379
2499
  }
2500
+ function refineStartXor(value, ctx) {
2501
+ const hasStart = value.start_at !== void 0 && value.start_at !== null;
2502
+ const hasDate = value.date !== void 0 && value.date !== null;
2503
+ if (hasStart === hasDate) {
2504
+ addIssue2(ctx, ["start_at"], "provide exactly one of start_at or date");
2505
+ return;
2506
+ }
2507
+ if (hasStart) {
2508
+ refineEndXor(value, ctx);
2509
+ return;
2510
+ }
2511
+ if (value.end_at != null || value.duration_minutes != null) {
2512
+ addIssue2(ctx, ["date"], "an all-day booking (date) takes no end_at or duration_minutes");
2513
+ }
2514
+ if (value.buffer_before_minutes || value.buffer_after_minutes) {
2515
+ addIssue2(ctx, ["date"], "an all-day booking (date) takes no buffers");
2516
+ }
2517
+ }
2380
2518
  var bookingRemindersSchema = z5.extend(zBookingReminders, {
2381
2519
  overrides: z5._default(
2382
2520
  z5.array(bookingReminderSchema).check(z5.maxLength(MAX_REMINDER_OVERRIDES)),
@@ -2395,7 +2533,7 @@ var bookingCreateSchema = z5.extend(zBookingCreateSchema, {
2395
2533
  // our compositions into its own output.
2396
2534
  reminders: z5.nullish(bookingRemindersSchema),
2397
2535
  metadata: z5.nullish(bookingMetadataSchema)
2398
- }).check((ctx) => refineEndXor(ctx.value, ctx));
2536
+ }).check((ctx) => refineStartXor(ctx.value, ctx));
2399
2537
  var bookingRescheduleSchema = z5.extend(zBookingRescheduleSchema, { time_zone: ianaTz }).check((ctx) => refineEndXor(ctx.value, ctx));
2400
2538
  var bookingCancelSchema = zBookingCancelSchema;
2401
2539
 
@@ -2554,7 +2692,10 @@ function useBookingForm(options = {}) {
2554
2692
  const response = await fetch(endpoint, {
2555
2693
  method: "POST",
2556
2694
  headers: { "Content-Type": "application/json" },
2557
- body: JSON.stringify(input)
2695
+ body: JSON.stringify({
2696
+ ...input,
2697
+ metadata: { ...getBookingAttribution(), ...input.metadata }
2698
+ })
2558
2699
  });
2559
2700
  const body = await response.json().catch(() => null);
2560
2701
  if (!response.ok) {
@@ -2635,6 +2776,7 @@ var import_client2 = require("@aranova/tracking-next/client");
2635
2776
  formatPhoneAsTyped,
2636
2777
  formatSlotRange,
2637
2778
  fromMinor,
2779
+ getBookingAttribution,
2638
2780
  getConsentChoice,
2639
2781
  getConsentState,
2640
2782
  getEventSemantics,