@aranova/tracking-next 0.20.2 → 0.21.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/README.md +80 -1
- package/dist/calendar-server.d.mts +53 -0
- package/dist/calendar-server.d.ts +53 -0
- package/dist/calendar-server.js +206 -0
- package/dist/calendar-server.js.map +1 -0
- package/dist/calendar-server.mjs +180 -0
- package/dist/calendar-server.mjs.map +1 -0
- package/dist/calendar.d.mts +112 -0
- package/dist/calendar.d.ts +112 -0
- package/dist/calendar.js +517 -0
- package/dist/calendar.js.map +1 -0
- package/dist/calendar.mjs +457 -0
- package/dist/calendar.mjs.map +1 -0
- package/dist/date-BHetnL7s.d.mts +26 -0
- package/dist/date-BHetnL7s.d.ts +26 -0
- package/dist/index.d.mts +60 -3
- package/dist/index.d.ts +60 -3
- package/dist/index.js +511 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +474 -18
- package/dist/index.mjs.map +1 -1
- package/dist/request-Doq36Rt0.d.mts +19 -0
- package/dist/request-Doq36Rt0.d.ts +19 -0
- package/dist/{sales-DrH6dY5F.d.mts → sales-B3BnYncU.d.mts} +5 -44
- package/dist/{sales-DrH6dY5F.d.ts → sales-CDjXRdmd.d.ts} +5 -44
- package/dist/sales.d.mts +3 -1
- package/dist/sales.d.ts +3 -1
- package/dist/sales.js +22 -17
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +22 -17
- package/dist/sales.mjs.map +1 -1
- package/dist/transport-Bku8zftU.d.mts +721 -0
- package/dist/transport-DsOY6kcW.d.ts +721 -0
- package/package.json +21 -1
package/dist/calendar.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/calendar.ts
|
|
21
|
+
var calendar_exports = {};
|
|
22
|
+
__export(calendar_exports, {
|
|
23
|
+
AranovaApiError: () => AranovaApiError,
|
|
24
|
+
BOOKING_SOURCES: () => BOOKING_SOURCES,
|
|
25
|
+
BOOKING_STATUSES: () => BOOKING_STATUSES,
|
|
26
|
+
CALENDAR_ERROR_CODES: () => CALENDAR_ERROR_CODES,
|
|
27
|
+
CALENDAR_METADATA_SCHEMA_VERSION: () => CALENDAR_METADATA_SCHEMA_VERSION,
|
|
28
|
+
CALENDAR_SYNC_STATUSES: () => CALENDAR_SYNC_STATUSES,
|
|
29
|
+
GOOGLE_SYNC_STATES: () => GOOGLE_SYNC_STATES,
|
|
30
|
+
MAX_ATTENDEES: () => MAX_ATTENDEES,
|
|
31
|
+
MAX_BOOKING_MINUTES: () => MAX_BOOKING_MINUTES,
|
|
32
|
+
MAX_REMINDER_MINUTES: () => MAX_REMINDER_MINUTES,
|
|
33
|
+
MAX_REMINDER_OVERRIDES: () => MAX_REMINDER_OVERRIDES,
|
|
34
|
+
bookingAttendeeSchema: () => bookingAttendeeSchema,
|
|
35
|
+
bookingCancelSchema: () => bookingCancelSchema,
|
|
36
|
+
bookingCreateSchema: () => bookingCreateSchema,
|
|
37
|
+
bookingCustomerSchema: () => bookingCustomerSchema,
|
|
38
|
+
bookingLocationNullableSchema: () => bookingLocationNullableSchema,
|
|
39
|
+
bookingLocationSchema: () => bookingLocationSchema,
|
|
40
|
+
bookingMetadataSchema: () => bookingMetadataSchema,
|
|
41
|
+
bookingReminderSchema: () => bookingReminderSchema,
|
|
42
|
+
bookingRemindersSchema: () => bookingRemindersSchema,
|
|
43
|
+
bookingRescheduleSchema: () => bookingRescheduleSchema,
|
|
44
|
+
computeSlots: () => computeSlots,
|
|
45
|
+
createCalendarReadClient: () => createCalendarReadClient,
|
|
46
|
+
durationMinutes: () => durationMinutes,
|
|
47
|
+
endOfDayInTz: () => endOfDayInTz,
|
|
48
|
+
formatDateInTz: () => formatDateInTz,
|
|
49
|
+
formatSlotRange: () => formatSlotRange,
|
|
50
|
+
groupSlotsByDay: () => groupSlotsByDay,
|
|
51
|
+
instantFromWallClock: () => instantFromWallClock,
|
|
52
|
+
isCalendarError: () => isCalendarError,
|
|
53
|
+
localDateKey: () => localDateKey,
|
|
54
|
+
offsetMinutesAt: () => offsetMinutesAt,
|
|
55
|
+
resolveBrowserTimeZone: () => resolveBrowserTimeZone,
|
|
56
|
+
startOfDayInTz: () => startOfDayInTz
|
|
57
|
+
});
|
|
58
|
+
module.exports = __toCommonJS(calendar_exports);
|
|
59
|
+
|
|
60
|
+
// ../tracking-core/src/resources/http/errors.ts
|
|
61
|
+
var AranovaApiError = class extends Error {
|
|
62
|
+
constructor(message, options) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "AranovaApiError";
|
|
65
|
+
this.status = options.status;
|
|
66
|
+
this.code = options.code;
|
|
67
|
+
this.requestId = options.requestId;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// ../tracking-core/src/phone.ts
|
|
72
|
+
var import_libphonenumber_js = require("libphonenumber-js");
|
|
73
|
+
|
|
74
|
+
// ../tracking-core/src/events/page-view.ts
|
|
75
|
+
var import_zod = require("zod");
|
|
76
|
+
var pageViewMetadataSchema = import_zod.z.object({
|
|
77
|
+
page: import_zod.z.object({
|
|
78
|
+
title: import_zod.z.string().nullable(),
|
|
79
|
+
path: import_zod.z.string(),
|
|
80
|
+
search: import_zod.z.string(),
|
|
81
|
+
hash: import_zod.z.string()
|
|
82
|
+
}).strict(),
|
|
83
|
+
referrer: import_zod.z.string().nullable(),
|
|
84
|
+
// `.nullable().optional()` — absent (undefined) OR explicit null OR a
|
|
85
|
+
// real viewport object. Mirrors Pydantic's `_Viewport | None = None`
|
|
86
|
+
// on the backend side so the drift test stays clean.
|
|
87
|
+
viewport: import_zod.z.object({
|
|
88
|
+
w: import_zod.z.number(),
|
|
89
|
+
h: import_zod.z.number()
|
|
90
|
+
}).strict().nullable().optional()
|
|
91
|
+
}).strict();
|
|
92
|
+
var pageViewConfigSchema = import_zod.z.object({}).strict();
|
|
93
|
+
|
|
94
|
+
// ../tracking-core/src/session.ts
|
|
95
|
+
var SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
96
|
+
|
|
97
|
+
// ../tracking-core/src/ingest.ts
|
|
98
|
+
var API_KEY_HEADER = "X-Aranova-Api-Key";
|
|
99
|
+
var SDK_VERSION_HEADER = "X-Aranova-Sdk-Version";
|
|
100
|
+
var SDK_PACKAGE_HEADER = "X-Aranova-Sdk-Package";
|
|
101
|
+
var SDK_SURFACE_HEADER = "X-Aranova-Sdk-Surface";
|
|
102
|
+
var SDK_ENVIRONMENT_HEADER = "X-Aranova-Sdk-Environment";
|
|
103
|
+
|
|
104
|
+
// ../tracking-core/src/resources/http/request.ts
|
|
105
|
+
function identityHeaders(config) {
|
|
106
|
+
const headers = { [API_KEY_HEADER]: config.apiKey };
|
|
107
|
+
if (config.sdkVersion) headers[SDK_VERSION_HEADER] = config.sdkVersion;
|
|
108
|
+
if (config.packageName) headers[SDK_PACKAGE_HEADER] = config.packageName;
|
|
109
|
+
if (config.surface) headers[SDK_SURFACE_HEADER] = config.surface;
|
|
110
|
+
if (config.environment) headers[SDK_ENVIRONMENT_HEADER] = config.environment;
|
|
111
|
+
return headers;
|
|
112
|
+
}
|
|
113
|
+
function joinUrl(endpoint, path) {
|
|
114
|
+
return `${endpoint.replace(/\/$/, "")}${path}`;
|
|
115
|
+
}
|
|
116
|
+
async function apiRequest(config, method, path, body, extraHeaders) {
|
|
117
|
+
const headers = { ...identityHeaders(config), ...extraHeaders };
|
|
118
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
119
|
+
const response = await fetch(joinUrl(config.endpoint, path), {
|
|
120
|
+
method,
|
|
121
|
+
headers,
|
|
122
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
123
|
+
});
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
let detail;
|
|
126
|
+
let code;
|
|
127
|
+
try {
|
|
128
|
+
const parsed = await response.json();
|
|
129
|
+
if (parsed && typeof parsed === "object") {
|
|
130
|
+
const record = parsed;
|
|
131
|
+
if (typeof record.detail === "string") detail = record.detail;
|
|
132
|
+
if (typeof record.code === "string") code = record.code;
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
throw new AranovaApiError(detail ?? response.statusText ?? "Request failed", {
|
|
137
|
+
status: response.status,
|
|
138
|
+
code,
|
|
139
|
+
requestId: response.headers.get("x-request-id") ?? void 0
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (response.status === 204) return void 0;
|
|
143
|
+
return await response.json();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ../tracking-core/src/resources/calendar/transport.ts
|
|
147
|
+
async function calendarRequest(config, method, path, body) {
|
|
148
|
+
return apiRequest(config, method, `/calendar${path}`, body);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ../tracking-core/src/resources/intl/date.ts
|
|
152
|
+
function formatDateInTz(iso, timeZone, opts, locale) {
|
|
153
|
+
const date = new Date(iso);
|
|
154
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
155
|
+
return new Intl.DateTimeFormat(locale, {
|
|
156
|
+
year: "numeric",
|
|
157
|
+
month: "short",
|
|
158
|
+
day: "2-digit",
|
|
159
|
+
hour: "2-digit",
|
|
160
|
+
minute: "2-digit",
|
|
161
|
+
...opts,
|
|
162
|
+
timeZone
|
|
163
|
+
}).format(date);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ../tracking-core/src/resources/calendar/time.ts
|
|
167
|
+
function resolveBrowserTimeZone() {
|
|
168
|
+
try {
|
|
169
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
170
|
+
} catch {
|
|
171
|
+
return "UTC";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function partsInTz(date, timeZone) {
|
|
175
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
176
|
+
timeZone,
|
|
177
|
+
hour12: false,
|
|
178
|
+
year: "numeric",
|
|
179
|
+
month: "2-digit",
|
|
180
|
+
day: "2-digit",
|
|
181
|
+
hour: "2-digit",
|
|
182
|
+
minute: "2-digit",
|
|
183
|
+
second: "2-digit"
|
|
184
|
+
});
|
|
185
|
+
const out = {};
|
|
186
|
+
for (const part of formatter.formatToParts(date)) {
|
|
187
|
+
if (part.type !== "literal") out[part.type] = Number(part.value);
|
|
188
|
+
}
|
|
189
|
+
if (out.hour === 24) out.hour = 0;
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
function offsetMinutesAt(date, timeZone) {
|
|
193
|
+
const p = partsInTz(date, timeZone);
|
|
194
|
+
const asUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
195
|
+
return (asUtc - Math.floor(date.getTime() / 1e3) * 1e3) / 6e4;
|
|
196
|
+
}
|
|
197
|
+
function instantFromWallClock(timeZone, year, month, day, hour = 0, minute = 0) {
|
|
198
|
+
const naive = Date.UTC(year, month - 1, day, hour, minute);
|
|
199
|
+
const firstGuess = new Date(naive - offsetMinutesAt(new Date(naive), timeZone) * 6e4);
|
|
200
|
+
const corrected = naive - offsetMinutesAt(firstGuess, timeZone) * 6e4;
|
|
201
|
+
return new Date(corrected);
|
|
202
|
+
}
|
|
203
|
+
function localDateKey(iso, timeZone) {
|
|
204
|
+
const p = partsInTz(new Date(iso), timeZone);
|
|
205
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
206
|
+
return `${p.year}-${pad(p.month)}-${pad(p.day)}`;
|
|
207
|
+
}
|
|
208
|
+
function startOfDayInTz(dateKey, timeZone) {
|
|
209
|
+
const [year, month, day] = dateKey.split("-").map(Number);
|
|
210
|
+
return instantFromWallClock(timeZone, year, month, day, 0, 0);
|
|
211
|
+
}
|
|
212
|
+
function endOfDayInTz(dateKey, timeZone) {
|
|
213
|
+
const start = startOfDayInTz(dateKey, timeZone);
|
|
214
|
+
const p = partsInTz(start, timeZone);
|
|
215
|
+
return instantFromWallClock(timeZone, p.year, p.month, p.day + 1, 0, 0);
|
|
216
|
+
}
|
|
217
|
+
function durationMinutes(startIso, endIso) {
|
|
218
|
+
return Math.round((new Date(endIso).getTime() - new Date(startIso).getTime()) / 6e4);
|
|
219
|
+
}
|
|
220
|
+
function formatSlotRange(startIso, endIso, timeZone, locale) {
|
|
221
|
+
const opts = { hour: "2-digit", minute: "2-digit", timeZone };
|
|
222
|
+
const fmt = new Intl.DateTimeFormat(locale, opts);
|
|
223
|
+
return `${fmt.format(new Date(startIso))} \u2013 ${fmt.format(new Date(endIso))}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ../tracking-core/src/resources/calendar/read-client.ts
|
|
227
|
+
function withBusiness(config, path) {
|
|
228
|
+
if (!config.businessId) return path;
|
|
229
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
230
|
+
return `${path}${separator}business_id=${encodeURIComponent(config.businessId)}`;
|
|
231
|
+
}
|
|
232
|
+
function createCalendarReadClient(config) {
|
|
233
|
+
return {
|
|
234
|
+
async listCalendars() {
|
|
235
|
+
return calendarRequest(config, "GET", withBusiness(config, "/calendars"));
|
|
236
|
+
},
|
|
237
|
+
async busy(calendarKey, query) {
|
|
238
|
+
const params = new URLSearchParams({
|
|
239
|
+
since: query.since,
|
|
240
|
+
until: query.until
|
|
241
|
+
});
|
|
242
|
+
const timeZone = query.timeZone ?? config.defaultTimeZone ?? resolveBrowserTimeZone();
|
|
243
|
+
params.set("time_zone", timeZone);
|
|
244
|
+
return calendarRequest(
|
|
245
|
+
config,
|
|
246
|
+
"GET",
|
|
247
|
+
withBusiness(config, `/${encodeURIComponent(calendarKey)}/busy?${params.toString()}`)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function isCalendarError(error, code) {
|
|
253
|
+
if (!(error instanceof AranovaApiError)) return false;
|
|
254
|
+
return code === void 0 || error.code === code;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ../tracking-core/src/resources/calendar/schema.ts
|
|
258
|
+
var import_zod2 = require("zod");
|
|
259
|
+
var CALENDAR_METADATA_SCHEMA_VERSION = 1;
|
|
260
|
+
var MAX_ATTENDEES = 20;
|
|
261
|
+
var MAX_REMINDER_OVERRIDES = 5;
|
|
262
|
+
var MAX_REMINDER_MINUTES = 40320;
|
|
263
|
+
var MAX_BOOKING_MINUTES = 1440;
|
|
264
|
+
var BOOKING_SOURCES = ["website", "phone", "walk_in", "import", "operator"];
|
|
265
|
+
var BOOKING_STATUSES = ["pending", "confirmed", "cancelled", "no_show"];
|
|
266
|
+
var GOOGLE_SYNC_STATES = ["queued", "synced", "uncertain", "failed", "drifted"];
|
|
267
|
+
var CALENDAR_SYNC_STATUSES = [
|
|
268
|
+
"pending_invite",
|
|
269
|
+
"awaiting_calendar_selection",
|
|
270
|
+
"connected",
|
|
271
|
+
"needs_reconnect",
|
|
272
|
+
"revoked",
|
|
273
|
+
"disabled"
|
|
274
|
+
];
|
|
275
|
+
var CALENDAR_ERROR_CODES = [
|
|
276
|
+
"slot_unavailable",
|
|
277
|
+
"calendar_not_connected",
|
|
278
|
+
"calendar_disconnected",
|
|
279
|
+
"booking_not_found",
|
|
280
|
+
"outside_booking_window",
|
|
281
|
+
"rate_limited",
|
|
282
|
+
"google_error"
|
|
283
|
+
];
|
|
284
|
+
var isoInstant = import_zod2.z.string().datetime({ offset: true });
|
|
285
|
+
function isKnownTimeZone(timeZone) {
|
|
286
|
+
try {
|
|
287
|
+
new Intl.DateTimeFormat(void 0, { timeZone });
|
|
288
|
+
return true;
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
var ianaTz = import_zod2.z.string().min(1).max(64).regex(/^[A-Za-z0-9_+\-/]+$/).refine(isKnownTimeZone, { message: "unknown IANA time zone" });
|
|
294
|
+
var bookingAttendeeSchema = import_zod2.z.object({
|
|
295
|
+
email: import_zod2.z.string().max(320).email(),
|
|
296
|
+
display_name: import_zod2.z.string().max(200).nullable().optional(),
|
|
297
|
+
optional: import_zod2.z.boolean().default(false)
|
|
298
|
+
}).strict();
|
|
299
|
+
var bookingReminderSchema = import_zod2.z.object({
|
|
300
|
+
method: import_zod2.z.enum(["email", "popup"]),
|
|
301
|
+
minutes_before: import_zod2.z.number().int().min(0).max(MAX_REMINDER_MINUTES)
|
|
302
|
+
}).strict();
|
|
303
|
+
var bookingRemindersSchema = import_zod2.z.object({
|
|
304
|
+
use_calendar_defaults: import_zod2.z.boolean().default(true),
|
|
305
|
+
overrides: import_zod2.z.array(bookingReminderSchema).max(MAX_REMINDER_OVERRIDES).default([])
|
|
306
|
+
}).strict();
|
|
307
|
+
var bookingLocationSchema = import_zod2.z.union([
|
|
308
|
+
import_zod2.z.object({ kind: import_zod2.z.literal("google_meet") }).strict(),
|
|
309
|
+
import_zod2.z.object({ kind: import_zod2.z.literal("physical"), address: import_zod2.z.string().max(1024) }).strict(),
|
|
310
|
+
import_zod2.z.object({ kind: import_zod2.z.literal("phone"), phone: import_zod2.z.string().max(64) }).strict(),
|
|
311
|
+
import_zod2.z.object({ kind: import_zod2.z.literal("custom"), text: import_zod2.z.string().max(1024) }).strict()
|
|
312
|
+
]);
|
|
313
|
+
var bookingLocationNullableSchema = import_zod2.z.union([...bookingLocationSchema.options, import_zod2.z.null()]);
|
|
314
|
+
var bookingCustomerSchema = import_zod2.z.object({
|
|
315
|
+
// Required: Google needs an address to send the invite that confirms the
|
|
316
|
+
// booking, and that invite is the whole confirmation mechanism.
|
|
317
|
+
email: import_zod2.z.string().max(320).email(),
|
|
318
|
+
name: import_zod2.z.string().max(200).nullable().optional(),
|
|
319
|
+
phone: import_zod2.z.string().max(64).nullable().optional(),
|
|
320
|
+
sms_consent: import_zod2.z.boolean().default(false)
|
|
321
|
+
}).strict();
|
|
322
|
+
var jsonScalar = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.boolean(), import_zod2.z.null()]);
|
|
323
|
+
var bookingMetadataSchema = import_zod2.z.object({
|
|
324
|
+
schema_version: import_zod2.z.number().int().default(CALENDAR_METADATA_SCHEMA_VERSION),
|
|
325
|
+
source: import_zod2.z.enum(BOOKING_SOURCES).nullable().optional(),
|
|
326
|
+
service: import_zod2.z.string().max(64).nullable().optional(),
|
|
327
|
+
visitor_id: import_zod2.z.string().max(64).nullable().optional(),
|
|
328
|
+
session_id: import_zod2.z.string().max(64).nullable().optional(),
|
|
329
|
+
gclid: import_zod2.z.string().max(256).nullable().optional(),
|
|
330
|
+
fbclid: import_zod2.z.string().max(256).nullable().optional(),
|
|
331
|
+
utm_source: import_zod2.z.string().max(128).nullable().optional(),
|
|
332
|
+
utm_medium: import_zod2.z.string().max(128).nullable().optional(),
|
|
333
|
+
utm_campaign: import_zod2.z.string().max(128).nullable().optional(),
|
|
334
|
+
utm_term: import_zod2.z.string().max(128).nullable().optional(),
|
|
335
|
+
utm_content: import_zod2.z.string().max(128).nullable().optional(),
|
|
336
|
+
landing_page: import_zod2.z.string().max(2048).nullable().optional(),
|
|
337
|
+
referrer: import_zod2.z.string().max(2048).nullable().optional(),
|
|
338
|
+
notes: import_zod2.z.string().max(2e3).nullable().optional(),
|
|
339
|
+
custom: import_zod2.z.record(jsonScalar).nullable().optional()
|
|
340
|
+
}).strict();
|
|
341
|
+
function refineEndXor(value, ctx) {
|
|
342
|
+
const hasEnd = value.end_at !== void 0 && value.end_at !== null;
|
|
343
|
+
const hasDuration = value.duration_minutes !== void 0 && value.duration_minutes !== null;
|
|
344
|
+
if (hasEnd === hasDuration) {
|
|
345
|
+
ctx.addIssue({
|
|
346
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
347
|
+
message: "provide exactly one of end_at or duration_minutes",
|
|
348
|
+
path: ["end_at"]
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (hasEnd && new Date(value.end_at) <= new Date(value.start_at)) {
|
|
353
|
+
ctx.addIssue({
|
|
354
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
355
|
+
message: "end_at must be after start_at",
|
|
356
|
+
path: ["end_at"]
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
var bookingCreateSchema = import_zod2.z.object({
|
|
361
|
+
calendar_key: import_zod2.z.string().min(1).max(64),
|
|
362
|
+
// Free-form: appointment types are the client site's own vocabulary, not
|
|
363
|
+
// configuration the dashboard holds.
|
|
364
|
+
appointment_type: import_zod2.z.string().max(64).nullable().optional(),
|
|
365
|
+
start_at: isoInstant,
|
|
366
|
+
end_at: isoInstant.nullable().optional(),
|
|
367
|
+
duration_minutes: import_zod2.z.number().int().positive().max(MAX_BOOKING_MINUTES).nullable().optional(),
|
|
368
|
+
/** The booker's zone — distinct from the business's and the calendar's. */
|
|
369
|
+
time_zone: ianaTz,
|
|
370
|
+
title: import_zod2.z.string().max(300).nullable().optional(),
|
|
371
|
+
description: import_zod2.z.string().max(8e3).nullable().optional(),
|
|
372
|
+
location: bookingLocationNullableSchema.optional(),
|
|
373
|
+
customer: bookingCustomerSchema,
|
|
374
|
+
attendees: import_zod2.z.array(bookingAttendeeSchema).max(MAX_ATTENDEES).default([]),
|
|
375
|
+
send_invites: import_zod2.z.boolean().default(true),
|
|
376
|
+
reminders: bookingRemindersSchema.nullable().optional(),
|
|
377
|
+
metadata: bookingMetadataSchema.nullable().optional(),
|
|
378
|
+
idempotency_key: import_zod2.z.string().max(128).nullable().optional(),
|
|
379
|
+
buffer_before_minutes: import_zod2.z.number().int().min(0).max(MAX_BOOKING_MINUTES).default(0),
|
|
380
|
+
buffer_after_minutes: import_zod2.z.number().int().min(0).max(MAX_BOOKING_MINUTES).default(0)
|
|
381
|
+
}).strict().superRefine(refineEndXor);
|
|
382
|
+
var bookingRescheduleSchema = import_zod2.z.object({
|
|
383
|
+
start_at: isoInstant,
|
|
384
|
+
end_at: isoInstant.nullable().optional(),
|
|
385
|
+
duration_minutes: import_zod2.z.number().int().positive().max(MAX_BOOKING_MINUTES).nullable().optional(),
|
|
386
|
+
time_zone: ianaTz,
|
|
387
|
+
notify_attendees: import_zod2.z.boolean().default(true),
|
|
388
|
+
reason: import_zod2.z.string().max(500).nullable().optional()
|
|
389
|
+
}).strict().superRefine(refineEndXor);
|
|
390
|
+
var bookingCancelSchema = import_zod2.z.object({
|
|
391
|
+
reason: import_zod2.z.string().max(500).nullable().optional(),
|
|
392
|
+
notify_attendees: import_zod2.z.boolean().default(true)
|
|
393
|
+
}).strict();
|
|
394
|
+
|
|
395
|
+
// ../tracking-core/src/resources/calendar/slots.ts
|
|
396
|
+
function parseHhMm(value) {
|
|
397
|
+
const [hour, minute] = value.split(":").map(Number);
|
|
398
|
+
return [hour || 0, minute || 0];
|
|
399
|
+
}
|
|
400
|
+
function dateKeyOf(date, timeZone) {
|
|
401
|
+
const fmt = new Intl.DateTimeFormat("en-CA", {
|
|
402
|
+
timeZone,
|
|
403
|
+
year: "numeric",
|
|
404
|
+
month: "2-digit",
|
|
405
|
+
day: "2-digit"
|
|
406
|
+
});
|
|
407
|
+
return fmt.format(date);
|
|
408
|
+
}
|
|
409
|
+
function weekdayFromDateKey(dateKey) {
|
|
410
|
+
const [year, month, day] = dateKey.split("-").map(Number);
|
|
411
|
+
return new Date(Date.UTC(year, month - 1, day, 12)).getUTCDay();
|
|
412
|
+
}
|
|
413
|
+
function startOfLocalDay(at, timeZone) {
|
|
414
|
+
const [year, month, day] = dateKeyOf(at, timeZone).split("-").map(Number);
|
|
415
|
+
return instantFromWallClock(timeZone, year, month, day, 12, 0);
|
|
416
|
+
}
|
|
417
|
+
function overlaps(aStart, aEnd, bStart, bEnd) {
|
|
418
|
+
return aStart < bEnd && aEnd > bStart;
|
|
419
|
+
}
|
|
420
|
+
function computeSlots(options) {
|
|
421
|
+
const {
|
|
422
|
+
busy,
|
|
423
|
+
openingHours,
|
|
424
|
+
timeZone,
|
|
425
|
+
durationMinutes: durationMinutes2,
|
|
426
|
+
granularityMinutes = 15,
|
|
427
|
+
minNoticeMinutes = 0,
|
|
428
|
+
bufferBeforeMinutes = 0,
|
|
429
|
+
bufferAfterMinutes = 0,
|
|
430
|
+
windowDays = 30,
|
|
431
|
+
now = /* @__PURE__ */ new Date()
|
|
432
|
+
} = options;
|
|
433
|
+
const earliest = now.getTime() + minNoticeMinutes * 6e4;
|
|
434
|
+
const busyRanges = busy.map((interval) => [
|
|
435
|
+
new Date(interval.start_at).getTime(),
|
|
436
|
+
new Date(interval.end_at).getTime()
|
|
437
|
+
]);
|
|
438
|
+
const slots = [];
|
|
439
|
+
let cursor = startOfLocalDay(now, timeZone);
|
|
440
|
+
for (let dayOffset = 0; dayOffset < windowDays; dayOffset += 1) {
|
|
441
|
+
const dateKey = dateKeyOf(cursor, timeZone);
|
|
442
|
+
const [year, month, day] = dateKey.split("-").map(Number);
|
|
443
|
+
cursor = instantFromWallClock(timeZone, year, month, day + 1, 12, 0);
|
|
444
|
+
if (openingHours.closedDates?.includes(dateKey)) continue;
|
|
445
|
+
const intervals = openingHours.overrides?.[dateKey] ?? openingHours.weekly[weekdayFromDateKey(dateKey)] ?? [];
|
|
446
|
+
for (const interval of intervals) {
|
|
447
|
+
const [openHour, openMinute] = parseHhMm(interval.start);
|
|
448
|
+
const [closeHour, closeMinute] = parseHhMm(interval.end);
|
|
449
|
+
const opensAt = instantFromWallClock(timeZone, year, month, day, openHour, openMinute);
|
|
450
|
+
const closesAt = instantFromWallClock(timeZone, year, month, day, closeHour, closeMinute);
|
|
451
|
+
for (let start = opensAt.getTime(); start + durationMinutes2 * 6e4 <= closesAt.getTime(); start += granularityMinutes * 6e4) {
|
|
452
|
+
const end = start + durationMinutes2 * 6e4;
|
|
453
|
+
if (start < earliest) continue;
|
|
454
|
+
const blockedStart = start - bufferBeforeMinutes * 6e4;
|
|
455
|
+
const blockedEnd = end + bufferAfterMinutes * 6e4;
|
|
456
|
+
const taken = busyRanges.some(
|
|
457
|
+
([busyStart, busyEnd]) => overlaps(blockedStart, blockedEnd, busyStart, busyEnd)
|
|
458
|
+
);
|
|
459
|
+
if (taken) continue;
|
|
460
|
+
slots.push({
|
|
461
|
+
start_at: new Date(start).toISOString(),
|
|
462
|
+
end_at: new Date(end).toISOString()
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
slots.sort((a, b) => a.start_at.localeCompare(b.start_at));
|
|
468
|
+
return { slots, nextAvailableAt: slots[0]?.start_at ?? null };
|
|
469
|
+
}
|
|
470
|
+
function groupSlotsByDay(slots, timeZone) {
|
|
471
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
472
|
+
for (const slot of slots) {
|
|
473
|
+
const key = dateKeyOf(new Date(slot.start_at), timeZone);
|
|
474
|
+
const bucket = byDay.get(key);
|
|
475
|
+
if (bucket) bucket.push(slot);
|
|
476
|
+
else byDay.set(key, [slot]);
|
|
477
|
+
}
|
|
478
|
+
return [...byDay.entries()].map(([date, daySlots]) => ({ date, slots: daySlots })).sort((a, b) => a.date.localeCompare(b.date));
|
|
479
|
+
}
|
|
480
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
481
|
+
0 && (module.exports = {
|
|
482
|
+
AranovaApiError,
|
|
483
|
+
BOOKING_SOURCES,
|
|
484
|
+
BOOKING_STATUSES,
|
|
485
|
+
CALENDAR_ERROR_CODES,
|
|
486
|
+
CALENDAR_METADATA_SCHEMA_VERSION,
|
|
487
|
+
CALENDAR_SYNC_STATUSES,
|
|
488
|
+
GOOGLE_SYNC_STATES,
|
|
489
|
+
MAX_ATTENDEES,
|
|
490
|
+
MAX_BOOKING_MINUTES,
|
|
491
|
+
MAX_REMINDER_MINUTES,
|
|
492
|
+
MAX_REMINDER_OVERRIDES,
|
|
493
|
+
bookingAttendeeSchema,
|
|
494
|
+
bookingCancelSchema,
|
|
495
|
+
bookingCreateSchema,
|
|
496
|
+
bookingCustomerSchema,
|
|
497
|
+
bookingLocationNullableSchema,
|
|
498
|
+
bookingLocationSchema,
|
|
499
|
+
bookingMetadataSchema,
|
|
500
|
+
bookingReminderSchema,
|
|
501
|
+
bookingRemindersSchema,
|
|
502
|
+
bookingRescheduleSchema,
|
|
503
|
+
computeSlots,
|
|
504
|
+
createCalendarReadClient,
|
|
505
|
+
durationMinutes,
|
|
506
|
+
endOfDayInTz,
|
|
507
|
+
formatDateInTz,
|
|
508
|
+
formatSlotRange,
|
|
509
|
+
groupSlotsByDay,
|
|
510
|
+
instantFromWallClock,
|
|
511
|
+
isCalendarError,
|
|
512
|
+
localDateKey,
|
|
513
|
+
offsetMinutesAt,
|
|
514
|
+
resolveBrowserTimeZone,
|
|
515
|
+
startOfDayInTz
|
|
516
|
+
});
|
|
517
|
+
//# sourceMappingURL=calendar.js.map
|