@blazeo.com/appointment-client 1.0.6 → 1.0.8

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 (62) hide show
  1. package/Appointment-Client/.gitattributes +2 -0
  2. package/blazeo.com-appointment-client-1.0.8.tgz +0 -0
  3. package/dist/calendar/buildUnifiedCalendarView.d.ts +8 -0
  4. package/dist/calendar/buildUnifiedCalendarView.js +29 -8
  5. package/dist/calendar/fetchCalendarDetails.d.ts +8 -40
  6. package/dist/calendar/fetchCalendarDetails.js +117 -47
  7. package/dist/calendar/fetchCalendarWithOpeningHours.d.ts +1 -10
  8. package/dist/calendar/fetchCalendarWithOpeningHours.js +34 -15
  9. package/dist/calendar/getAllParticipantOpeningHours.d.ts +4 -1
  10. package/dist/calendar/getAllParticipantOpeningHours.js +6 -1
  11. package/dist/calendar/getOpeningHours.d.ts +4 -1
  12. package/dist/calendar/getOpeningHours.js +2 -2
  13. package/dist/calendar/getParticipantOpeningHours.js +9 -4
  14. package/dist/calendar/getParticipants.d.ts +4 -1
  15. package/dist/calendar/getParticipants.js +6 -1
  16. package/dist/calendar/mapToDesiredResponse.d.ts +70 -0
  17. package/dist/calendar/mapToDesiredResponse.js +99 -0
  18. package/dist/config/applyBlazeoDefaults.js +3 -2
  19. package/dist/config/blazeoClientDefaults.js +2 -2
  20. package/dist/config/ensureBlazeoHttpReady.d.ts +17 -0
  21. package/dist/config/ensureBlazeoHttpReady.js +31 -0
  22. package/dist/config/initializeAppointmentClient.d.ts +4 -0
  23. package/dist/config/initializeAppointmentClient.js +9 -3
  24. package/dist/config/syncBlazeoConnection.d.ts +6 -0
  25. package/dist/config/syncBlazeoConnection.js +18 -0
  26. package/dist/index.d.ts +4 -1
  27. package/dist/index.js +3 -1
  28. package/package.json +1 -1
  29. package/sample/.env.example +5 -0
  30. package/sample/package-lock.json +70 -1
  31. package/sample/package.json +1 -0
  32. package/sample/src/AllParticipantOpeningHoursTab.jsx +13 -4
  33. package/sample/src/App2.jsx +22 -2
  34. package/sample/src/AvailabilityTab.jsx +8 -3
  35. package/sample/src/BlazeoConnectionSettings.jsx +16 -15
  36. package/sample/src/CreateCalendarTab.jsx +23 -6
  37. package/sample/src/EventTab.jsx +31 -8
  38. package/sample/src/FetchCalendarTab.jsx +94 -70
  39. package/sample/src/OpeningHoursTab.jsx +13 -4
  40. package/sample/src/ParticipantInfoTab.jsx +8 -3
  41. package/sample/src/ParticipantOpeningHoursTab.jsx +17 -7
  42. package/sample/src/ParticipantTab.jsx +13 -4
  43. package/sample/src/blazeoBootstrap.js +30 -0
  44. package/sample/src/blazeoDemoError.js +14 -0
  45. package/sample/src/blazeoPushConnection.js +23 -0
  46. package/sample/src/main.jsx +3 -3
  47. package/sample/vite.config.js +19 -5
  48. package/src/calendar/buildUnifiedCalendarView.ts +35 -7
  49. package/src/calendar/fetchCalendarDetails.ts +318 -226
  50. package/src/calendar/fetchCalendarWithOpeningHours.ts +130 -99
  51. package/src/calendar/getAllParticipantOpeningHours.ts +9 -1
  52. package/src/calendar/getOpeningHours.ts +2 -2
  53. package/src/calendar/getParticipantOpeningHours.ts +14 -5
  54. package/src/calendar/getParticipants.ts +9 -1
  55. package/src/calendar/mapToDesiredResponse.ts +104 -0
  56. package/src/config/applyBlazeoDefaults.ts +3 -2
  57. package/src/config/blazeoClientDefaults.ts +2 -2
  58. package/src/config/ensureBlazeoHttpReady.ts +41 -0
  59. package/src/config/initializeAppointmentClient.ts +9 -3
  60. package/src/config/syncBlazeoConnection.ts +19 -0
  61. package/src/index.ts +7 -1
  62. package/blazeo.com-appointment-client-1.0.6.tgz +0 -0
@@ -1,99 +1,130 @@
1
- import { CalendarModel } from "@blazeo.com/calendar-client";
2
- import { getSnapshot } from "mobx-state-tree";
3
-
4
- /**
5
- * Unwrap nested REST shapes: `res.data`, `res.Data`, or `res.data.data`.
6
- */
7
- export function unwrapCalendarGetData(res: any) {
8
- if (res == null || typeof res !== "object") return null;
9
- let d = res.data ?? res.Data;
10
- if (d == null) return null;
11
- if (typeof d === "object" && (d.data != null || d.Data != null)) {
12
- d = d.data ?? d.Data;
13
- }
14
- return d;
15
- }
16
-
17
- /** `openingHours` / `OpeningHours` on the calendar object returned by GET /Calendar/
18
- * Robustly picks opening hours array from a raw calendar payload.
19
- */
20
- export function pickOpeningHoursArrayFromCalendarPayload(data: any) {
21
- if (data == null) return null;
22
- const { list } = normalizeParticipantOpeningHoursResponse(data);
23
- return list;
24
- }
25
-
26
- /**
27
- * Normalize `calendar.getParticipantOpeningHours()` response (`GET /Calendar/Participant/OpeningHours/Get`).
28
- * Supports various Blazeo API shapes including nested data and common property names.
29
- */
30
- export function normalizeParticipantOpeningHoursResponse(res: any) {
31
- if (res == null) return { list: null, raw: res };
32
-
33
- // 1. Check for standard envelope: res.data or res.Data
34
- let d = res.data ?? res.Data ?? res;
35
-
36
- // 2. Handle double-nested data (common in some Blazeo API versions)
37
- if (d && typeof d === "object" && !Array.isArray(d)) {
38
- if (d.data !== undefined) d = d.data;
39
- else if (d.Data !== undefined) d = d.Data;
40
- }
41
-
42
- // 3. If d is now an array, that's our list
43
- if (Array.isArray(d)) return { list: d, raw: res };
44
-
45
- // 4. Otherwise check for known list properties on the object
46
- if (d && typeof d === "object") {
47
- const list =
48
- d.openingHours ??
49
- d.OpeningHours ??
50
- d.participantOpeningHours ??
51
- d.ParticipantOpeningHours ??
52
- d.rows ??
53
- d.Rows ??
54
- d.items ??
55
- d.Items ??
56
- d.list ??
57
- d.List;
58
- if (Array.isArray(list)) return { list, raw: res };
59
- }
60
-
61
- return { list: null, raw: res };
62
- }
63
-
64
- /**
65
- * Loads `CalendarModel` and attaches **`openingHours`** to the MST snapshot:
66
- * 1. Prefer rows embedded on **GET /Calendar/Get** (`CalendarModel.getRaw` payload — `@blazeo.com/calendar-client` MST omits them).
67
- * 2. If missing/empty, calls **`calendar.getParticipantOpeningHours()`** (`GET /Calendar/Participant/OpeningHours/Get`).
68
- */
69
- export async function fetchCalendarWithOpeningHours(calendarId: string, options: any = {}) {
70
- const { includeRawGet = false } = options;
71
- const rawRes: any = await (CalendarModel as any).getRaw(calendarId);
72
- const cal: any = await CalendarModel.get(calendarId);
73
- const payload = unwrapCalendarGetData(rawRes);
74
- const embedded = pickOpeningHoursArrayFromCalendarPayload(payload) ?? [];
75
- let resolved = embedded.length > 0 ? embedded : null;
76
- let participantRes: any = null;
77
-
78
- if ((resolved == null || resolved.length === 0) && cal != null) {
79
- // Pass calendarId explicitly because some server shapes do not populate `self.calendarId` on the MST model.
80
- participantRes = await cal.getParticipantOpeningHours({ calendarId });
81
- const { list } = normalizeParticipantOpeningHoursResponse(participantRes);
82
- if (list != null && list.length > 0) resolved = list;
83
- }
84
-
85
- const openingHours = Array.isArray(resolved) ? resolved : [];
86
- const snap = cal != null ? getSnapshot(cal) : null;
87
- const calendar = snap != null ? { ...(snap as any), openingHours } : null;
88
-
89
- return {
90
- calendar,
91
- cal,
92
- openingHours,
93
- embeddedFromGet: embedded,
94
- fromCalendarGet: embedded.length > 0,
95
- fromParticipantApi: embedded.length === 0 && openingHours.length > 0 && participantRes != null,
96
- participantOpeningHoursResponse: participantRes,
97
- ...(includeRawGet ? { rawGet: rawRes } : {}),
98
- };
99
- }
1
+ import { CalendarModel } from "@blazeo.com/calendar-client";
2
+ import { getSnapshot } from "mobx-state-tree";
3
+ import { resolveBlazeoConnection } from "./createCalendar.js";
4
+ import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
5
+ import { mapToDesiredCalendarResponse } from "./mapToDesiredResponse.js";
6
+
7
+ /**
8
+ * Unwrap nested REST shapes: `res.data`, `res.Data`, or `res.data.data`.
9
+ */
10
+ export function unwrapCalendarGetData(res: any) {
11
+ if (res == null || typeof res !== "object") return null;
12
+ let d = res.data ?? res.Data;
13
+ if (d == null) return null;
14
+ if (typeof d === "object" && (d.data != null || d.Data != null)) {
15
+ d = d.data ?? d.Data;
16
+ }
17
+ return d;
18
+ }
19
+
20
+ /** `openingHours` / `OpeningHours` on the calendar object returned by GET /Calendar/
21
+ * Robustly picks opening hours array from a raw calendar payload.
22
+ */
23
+ export function pickOpeningHoursArrayFromCalendarPayload(data: any) {
24
+ if (data == null) return null;
25
+ const { list } = normalizeParticipantOpeningHoursResponse(data);
26
+ return list;
27
+ }
28
+
29
+ /**
30
+ * Normalize `calendar.getParticipantOpeningHours()` response (`GET /Calendar/Participant/OpeningHours/Get`).
31
+ * Supports various Blazeo API shapes including nested data and common property names.
32
+ */
33
+ export function normalizeParticipantOpeningHoursResponse(res: any) {
34
+ if (res == null) return { list: null, raw: res };
35
+
36
+ // 1. Check for standard envelope: res.data or res.Data
37
+ let d = res.data ?? res.Data ?? res;
38
+
39
+ // 2. Handle double-nested data (common in some Blazeo API versions)
40
+ if (d && typeof d === "object" && !Array.isArray(d)) {
41
+ if (d.data !== undefined) d = d.data;
42
+ else if (d.Data !== undefined) d = d.Data;
43
+ }
44
+
45
+ // 3. If d is now an array, that's our list
46
+ if (Array.isArray(d)) return { list: d, raw: res };
47
+
48
+ // 4. Otherwise check for known list properties on the object
49
+ if (d && typeof d === "object") {
50
+ const list =
51
+ d.openingHours ??
52
+ d.OpeningHours ??
53
+ d.participantOpeningHours ??
54
+ d.ParticipantOpeningHours ??
55
+ d.rows ??
56
+ d.Rows ??
57
+ d.items ??
58
+ d.Items ??
59
+ d.list ??
60
+ d.List;
61
+ if (Array.isArray(list)) return { list, raw: res };
62
+ }
63
+
64
+ return { list: null, raw: res };
65
+ }
66
+
67
+ /**
68
+ * Loads `CalendarModel` and attaches **`openingHours`** to the MST snapshot:
69
+ * 1. Prefer rows embedded on **GET /Calendar/Get** (`CalendarModel.getRaw` payload `@blazeo.com/calendar-client` MST omits them).
70
+ * 2. If missing/empty, calls **`calendar.getParticipantOpeningHours()`** (`GET /Calendar/Participant/OpeningHours/Get`).
71
+ */
72
+ export async function fetchCalendarWithOpeningHours(calendarId: string, options: any = {}) {
73
+ const { includeRawGet = false, baseUrl, consumer } = options;
74
+
75
+ const { baseUrl: resolvedBase, consumer: resolvedConsumer } = resolveBlazeoConnection({ baseUrl, consumer });
76
+ const rawRes = await (CalendarModel as any).getRaw(calendarId);
77
+ const payload = unwrapCalendarGetData(rawRes);
78
+
79
+ let cal: any = null;
80
+ if (payload) {
81
+ // Manually create the model instance to fix a bug in calendar-client static get.
82
+ cal = (CalendarModel as any).create(
83
+ { ...payload, calendarId },
84
+ { baseUrl: resolvedBase, consumer: resolvedConsumer }
85
+ );
86
+ }
87
+
88
+ if (cal == null) {
89
+ return {
90
+ calendar: null,
91
+ openingHours: [],
92
+ raw: rawRes,
93
+ meta: { ok: false as const, reason: "calendar_not_found" },
94
+ };
95
+ }
96
+
97
+ const embedded = pickOpeningHoursArrayFromCalendarPayload(payload) ?? [];
98
+ let resolved = embedded.length > 0 ? embedded : null;
99
+ let participantRes: any = null;
100
+
101
+ if ((resolved == null || resolved.length === 0) && cal != null) {
102
+ // Pass calendarId explicitly because some server shapes do not populate `self.calendarId` on the MST model.
103
+ participantRes = await cal.getParticipantOpeningHours({ calendarId });
104
+ const { list } = normalizeParticipantOpeningHoursResponse(participantRes);
105
+ if (list != null && list.length > 0) resolved = list;
106
+ }
107
+
108
+ const openingHours = Array.isArray(resolved) ? resolved : [];
109
+ const mappedOpeningHours = openingHours.map(oh => ({
110
+ ...oh,
111
+ }));
112
+
113
+ const rawMembers = payload?.members ?? payload?.Members ?? payload?.participants ?? payload?.Participants;
114
+ const mappedMembers = Array.isArray(rawMembers)
115
+ ? rawMembers.map((m: any) => ({ ...m }))
116
+ : [];
117
+
118
+ const calendar = mapToDesiredCalendarResponse(payload, openingHours, mappedMembers);
119
+
120
+ if (!calendar) return null as any;
121
+
122
+ Object.defineProperties(calendar, {
123
+ _cal: { value: cal, enumerable: false },
124
+ _openingHours: { value: openingHours, enumerable: false },
125
+ _embeddedFromGet: { value: embedded, enumerable: false },
126
+ _rawGet: { value: rawRes, enumerable: false },
127
+ });
128
+
129
+ return calendar as any;
130
+ }
@@ -1,11 +1,19 @@
1
1
  import { CalendarModel } from "@blazeo.com/calendar-client";
2
+ import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
2
3
  import { normalizeParticipantOpeningHoursResponse } from "./fetchCalendarWithOpeningHours.js";
3
4
 
4
5
  /**
5
6
  * Fetch all participant opening hours for a calendar.
6
7
  * Uses `GET /Calendar/Participant/OpeningHours/All/Get`.
7
8
  */
8
- export async function getAllParticipantOpeningHours(calendarId: string) {
9
+ export async function getAllParticipantOpeningHours(
10
+ calendarId: string,
11
+ options: { baseUrl?: string; consumer?: string } = {}
12
+ ) {
13
+ const ready = ensureBlazeoHttpReady(options);
14
+ if (!ready.ok) {
15
+ throw new Error(ready.error);
16
+ }
9
17
  const raw: any = await (CalendarModel as any).getAllParticipantOpeningHours(calendarId);
10
18
 
11
19
  // calendar-client static helper returns either `unknown[] | null` or an envelope depending on version/entrypoint.
@@ -4,7 +4,7 @@ import { fetchCalendarWithOpeningHours } from "./fetchCalendarWithOpeningHours.j
4
4
  * Fetches opening hours for a calendar.
5
5
  * Automatically handles embedded calendar-level hours and participant-level fallbacks.
6
6
  */
7
- export async function getOpeningHours(calendarId: string) {
8
- const result = await fetchCalendarWithOpeningHours(calendarId);
7
+ export async function getOpeningHours(calendarId: string, options: { baseUrl?: string; consumer?: string } = {}) {
8
+ const result = await fetchCalendarWithOpeningHours(calendarId, options);
9
9
  return result.openingHours;
10
10
  }
@@ -1,5 +1,6 @@
1
1
  import { CalendarModel } from "@blazeo.com/calendar-client";
2
- import { normalizeParticipantOpeningHoursResponse } from "./fetchCalendarWithOpeningHours.js";
2
+ import { resolveBlazeoConnection } from "./createCalendar.js";
3
+ import { unwrapCalendarGetData, normalizeParticipantOpeningHoursResponse } from "./fetchCalendarWithOpeningHours.js";
3
4
 
4
5
  /**
5
6
  * Direct wrapper around `calendar.getParticipantOpeningHours()` for a calendar id.
@@ -9,8 +10,12 @@ import { normalizeParticipantOpeningHoursResponse } from "./fetchCalendarWithOpe
9
10
  */
10
11
  export async function getParticipantOpeningHours(calendarId: string, options: any = {}) {
11
12
  try {
12
- const cal: any = await CalendarModel.get(calendarId);
13
- if (cal == null) {
13
+ const { baseUrl, consumer, ...passThrough } = options;
14
+ const { baseUrl: resolvedBase, consumer: resolvedConsumer } = resolveBlazeoConnection({ baseUrl, consumer });
15
+ const rawRes = await (CalendarModel as any).getRaw(calendarId);
16
+ const payload = unwrapCalendarGetData(rawRes);
17
+
18
+ if (!payload) {
14
19
  return {
15
20
  openingHours: [] as any[],
16
21
  raw: null as any,
@@ -18,7 +23,12 @@ export async function getParticipantOpeningHours(calendarId: string, options: an
18
23
  };
19
24
  }
20
25
 
21
- const raw = await cal.getParticipantOpeningHours({ calendarId, ...(options ?? {}) });
26
+ const cal = (CalendarModel as any).create(
27
+ { ...payload, calendarId },
28
+ { baseUrl: resolvedBase, consumer: resolvedConsumer }
29
+ );
30
+
31
+ const raw = await cal.getParticipantOpeningHours({ calendarId, ...(passThrough ?? {}) });
22
32
  const { list } = normalizeParticipantOpeningHoursResponse(raw);
23
33
  const openingHours = Array.isArray(list) ? list : [];
24
34
 
@@ -43,4 +53,3 @@ export async function getParticipantOpeningHours(calendarId: string, options: an
43
53
  };
44
54
  }
45
55
  }
46
-
@@ -1,9 +1,17 @@
1
1
  import { CalendarModel } from "@blazeo.com/calendar-client";
2
+ import { ensureBlazeoHttpReady } from "../config/ensureBlazeoHttpReady.js";
2
3
 
3
4
  /**
4
5
  * Fetches participants for a calendar.
5
6
  */
6
- export async function getParticipants(calendarId: string) {
7
+ export async function getParticipants(
8
+ calendarId: string,
9
+ options: { baseUrl?: string; consumer?: string } = {}
10
+ ) {
11
+ const ready = ensureBlazeoHttpReady(options);
12
+ if (!ready.ok) {
13
+ throw new Error(ready.error);
14
+ }
7
15
  const participants = await CalendarModel.getParticipants(calendarId);
8
16
  return Array.isArray(participants) ? participants : [];
9
17
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Maps a raw Blazeo API calendar payload (often PascalCase) to the specific camelCase
3
+ * shape requested by the user, including typenames and normalized fields.
4
+ */
5
+ export function mapToDesiredCalendarResponse(payload: any, openingHours: any[] = [], members: any[] = []) {
6
+ if (!payload) return null;
7
+
8
+ const pick = (obj: any, ...keys: string[]) => {
9
+ for (const k of keys) {
10
+ if (obj[k] !== undefined && obj[k] !== null) return obj[k];
11
+ }
12
+ return undefined;
13
+ };
14
+
15
+ const n = (v: any) => (v != null && v !== "" ? Number(v) : null);
16
+
17
+ // Map members with typename
18
+ const mappedMembers = members.map(m => ({
19
+ id: m.id ?? pick(m, "id", "Id"),
20
+ name: m.name ?? pick(m, "name", "Name", "alias", "Alias"),
21
+ email: m.email ?? pick(m, "email", "Email") ?? null,
22
+ status: m.status ?? pick(m, "status", "Status") ?? 0,
23
+ __typename: "Member"
24
+ }));
25
+
26
+ // Map opening hours with typename and raw fields
27
+ const mappedOpeningHours = openingHours.map(oh => ({
28
+ id: pick(oh, "id", "Id") ?? 0,
29
+ createdOn: pick(oh, "createdOn", "CreatedOn", "created_on") ?? "0001-01-01T00:00:00.000Z",
30
+ modifiedOn: pick(oh, "modifiedOn", "ModifiedOn", "modified_on") ?? "0001-01-01T00:00:00.000Z",
31
+ member: pick(oh, "member", "Member"),
32
+ openingHourId: pick(oh, "openingHourId", "OpeningHourId", "opening_hour_id") ?? "",
33
+ calendarId: pick(oh, "calendarId", "CalendarId", "calendar_id") ?? "",
34
+ participantId: pick(oh, "participantId", "ParticipantId", "participant_id") ?? "",
35
+ days: oh.days ?? [],
36
+ startHour: oh.startHour ?? pick(oh, "startHour", "StartHour") ?? 0,
37
+ startMinute: oh.startMinute ?? pick(oh, "startMinute", "StartMinute") ?? 0,
38
+ endHour: oh.endHour ?? pick(oh, "endHour", "EndHour") ?? 0,
39
+ endMinute: oh.endMinute ?? pick(oh, "endMinute", "EndMinute") ?? 0,
40
+ off: !!(oh.off ?? pick(oh, "off", "Off")),
41
+ __typename: "OpeningHour"
42
+ }));
43
+
44
+ // Map theme
45
+ const rawTheme = pick(payload, "theme", "Theme");
46
+ const theme = rawTheme ? {
47
+ id: pick(rawTheme, "id", "Id"),
48
+ color: pick(rawTheme, "color", "Color"),
49
+ logoUrl: pick(rawTheme, "logoUrl", "LogoUrl") ?? null,
50
+ __typename: "Theme"
51
+ } : null;
52
+
53
+ // Map reminders
54
+ const rawReminders = pick(payload, "reminderChannelStatuses", "ReminderChannelStatuses") ?? [];
55
+ const reminderChannelStatuses = Array.isArray(rawReminders) ? rawReminders.map((r: any) => ({
56
+ id: pick(r, "id", "Id"),
57
+ calendarId: pick(r, "calendarId", "CalendarId"),
58
+ channelId: pick(r, "channelId", "ChannelId"),
59
+ status: !!pick(r, "status", "Status"),
60
+ appointmentReminders: (pick(r, "appointmentReminders", "AppointmentReminders") ?? []).map((ar: any) => ({
61
+ id: pick(ar, "id", "Id"),
62
+ reminderChannelStatusId: pick(ar, "reminderChannelStatusId", "ReminderChannelStatusId"),
63
+ recipientType: pick(ar, "recipientType", "RecipientType"),
64
+ beforeEventTime: pick(ar, "beforeEventTime", "BeforeEventTime"),
65
+ unit: pick(ar, "unit", "Unit"),
66
+ __typename: "AppointmentReminder"
67
+ })),
68
+ __typename: "ReminderChannelStatus"
69
+ })) : [];
70
+
71
+ return {
72
+ id: n(pick(payload, "id", "Id")),
73
+ durationUnit: n(pick(payload, "durationUnit", "DurationUnit")),
74
+ minimumBookingNotice: n(pick(payload, "minimumBookingNotice", "MinimumBookingNotice")),
75
+ minimumBookingNoticeUnit: n(pick(payload, "minimumBookingNoticeUnit", "MinimumBookingNoticeUnit")),
76
+ minimumCancelationNotice: n(pick(payload, "minimumCancelationNotice", "MinimumCancelationNotice")),
77
+ minimumCancelationNoticeUnit: n(pick(payload, "minimumCancelationNoticeUnit", "MinimumCancelationNoticeUnit")),
78
+ futureLimit: n(pick(payload, "futureLimit", "FutureLimit")),
79
+ futureLimitUnit: n(pick(payload, "futureLimitUnit", "FutureLimitUnit")),
80
+ bufferTime: n(pick(payload, "bufferTime", "BufferTime")),
81
+ bufferTimeUnit: n(pick(payload, "bufferTimeUnit", "BufferTimeUnit")),
82
+ calendarLink: pick(payload, "calendarLink", "CalendarLink"),
83
+ uuid: pick(payload, "uuid", "Uuid", "calendarId", "CalendarId"),
84
+ location: pick(payload, "location", "Location") ?? "",
85
+ bookingPageTitle: pick(payload, "bookingPageTitle", "BookingPageTitle") ?? null,
86
+ reminderChannelStatuses,
87
+ members: mappedMembers,
88
+ createdOn: pick(payload, "createdOn", "CreatedOn") ?? "0001-01-01T00:00:00.000Z",
89
+ modifiedOn: pick(payload, "modifiedOn", "ModifiedOn") ?? "0001-01-01T00:00:00.000Z",
90
+ name: pick(payload, "name", "Name"),
91
+ timeZoneId: pick(payload, "timeZoneId", "TimeZoneId"),
92
+ description: pick(payload, "description", "Description") ?? "",
93
+ assignmentType: n(pick(payload, "assignmentType", "AssignmentType", "assignmentMethod", "AssignmentMethod")),
94
+ duration: n(pick(payload, "duration", "Duration")),
95
+ bookingLimit: n(pick(payload, "bookingLimit", "BookingLimit")),
96
+ calendarJson: pick(payload, "calendarJson", "CalendarJson") ?? null,
97
+ isThirdPartySaved: !!pick(payload, "isThirdPartySaved", "IsThirdPartySaved"),
98
+ themeId: n(pick(payload, "themeId", "ThemeId")),
99
+ theme,
100
+ openingHours: mappedOpeningHours,
101
+ appointmentUserDefinedFields: pick(payload, "appointmentUserDefinedFields", "AppointmentUserDefinedFields") ?? [],
102
+ __typename: "Calendar"
103
+ };
104
+ }
@@ -1,12 +1,13 @@
1
- import { configure } from "@blazeo.com/calendar-client";
2
1
  import { blazeoClientConfig } from "./blazeoClientDefaults.js";
2
+ import { syncBlazeoConnection } from "./syncBlazeoConnection.js";
3
3
 
4
4
  /** Push {@link blazeoClientConfig} into `@blazeo.com/calendar-client` (global store). Call explicitly if you use file defaults; otherwise use {@link initializeAppointmentClient}. */
5
5
  export function applyBlazeoClientConfig() {
6
+ debugger;
6
7
  const baseUrl = blazeoClientConfig.baseUrl?.trim().replace(/\/+$/, "");
7
8
  if (!baseUrl) return;
8
9
  const consumer = blazeoClientConfig.consumer?.trim();
9
- configure({
10
+ syncBlazeoConnection({
10
11
  baseUrl,
11
12
  ...(consumer ? { consumer } : {}),
12
13
  });
@@ -6,6 +6,6 @@
6
6
  * @example baseUrl: "https://apptscheduling.azurewebsites.net"
7
7
  */
8
8
  export const blazeoClientConfig = {
9
- baseUrl: "",
10
- consumer: "",
9
+ baseUrl: "https://apptscheduling.azurewebsites.net",
10
+ consumer: "smarthub",
11
11
  };
@@ -0,0 +1,41 @@
1
+ import { configure } from "@blazeo.com/calendar-client";
2
+ import { resolveBlazeoConnection } from "../calendar/createCalendar.js";
3
+
4
+ export type EnsureBlazeoHttpOptions = {
5
+ baseUrl?: string;
6
+ consumer?: string;
7
+ };
8
+
9
+ /**
10
+ * Ensures global Blazeo `configure({ baseUrl })` runs before any `CalendarModel` / `EventModel` HTTP.
11
+ * Uses the same resolution as {@link resolveBlazeoConnection}: explicit args, existing `getConfig()`,
12
+ * then `blazeoClientDefaults` — so file defaults apply even if the host never called `configure`.
13
+ */
14
+ export function ensureBlazeoHttpReady(options: EnsureBlazeoHttpOptions = {}):
15
+ | { ok: true; baseUrl: string; consumer?: string }
16
+ | { ok: false; error: string } {
17
+ // Hard-prefer explicit args (call-site can bypass any module-resolution mismatch).
18
+ const explicitBase = options.baseUrl?.trim().replace(/\/+$/, "");
19
+ const explicitConsumer = options.consumer?.trim() || undefined;
20
+ if (explicitBase) {
21
+ configure({
22
+ baseUrl: explicitBase,
23
+ ...(explicitConsumer ? { consumer: explicitConsumer } : {}),
24
+ });
25
+ return { ok: true, baseUrl: explicitBase, ...(explicitConsumer ? { consumer: explicitConsumer } : {}) };
26
+ }
27
+
28
+ const { baseUrl, consumer } = resolveBlazeoConnection(options);
29
+ if (!baseUrl) {
30
+ return {
31
+ ok: false,
32
+ error:
33
+ "Blazeo base URL is not set. Call initializeAppointmentClient({ baseUrl }) or configure({ baseUrl }) at app startup, set blazeoClientConfig.baseUrl, or pass baseUrl when calling fetch APIs.",
34
+ };
35
+ }
36
+ configure({
37
+ baseUrl,
38
+ ...(consumer ? { consumer } : {}),
39
+ });
40
+ return { ok: true, baseUrl, ...(consumer ? { consumer } : {}) };
41
+ }
@@ -1,4 +1,5 @@
1
- import { configure, getConfig } from "@blazeo.com/calendar-client";
1
+ import { getConfig } from "@blazeo.com/calendar-client";
2
+ import { syncBlazeoConnection } from "./syncBlazeoConnection.js";
2
3
 
3
4
  export interface AppointmentClientConfig {
4
5
  baseUrl: string;
@@ -8,9 +9,14 @@ export interface AppointmentClientConfig {
8
9
 
9
10
  let isConfigured = false;
10
11
 
12
+ /**
13
+ * Applies Blazeo connection (same as {@link syncBlazeoConnection}) and marks the client as configured
14
+ * when a non-empty `baseUrl` was written to `@blazeo.com/calendar-client`.
15
+ */
11
16
  export function initializeAppointmentClient(config: AppointmentClientConfig) {
12
- configure(config);
13
- isConfigured = true;
17
+ if (syncBlazeoConnection(config)) {
18
+ isConfigured = true;
19
+ }
14
20
  }
15
21
 
16
22
  export function isAppointmentClientConfigured() {
@@ -0,0 +1,19 @@
1
+ import { configure } from "@blazeo.com/calendar-client";
2
+ import type { AppointmentClientConfig } from "./initializeAppointmentClient.js";
3
+
4
+ /**
5
+ * Writes `baseUrl` / `consumer` into the global `@blazeo.com/calendar-client` `configure()` store.
6
+ * Returns whether anything was applied (skipped when `baseUrl` is empty after trim).
7
+ */
8
+ export function syncBlazeoConnection(config: AppointmentClientConfig): boolean {
9
+ const baseUrl = config.baseUrl?.trim().replace(/\/+$/, "");
10
+ if (!baseUrl) return false;
11
+ configure({
12
+ baseUrl,
13
+ ...(config.consumer != null && String(config.consumer).trim() !== ""
14
+ ? { consumer: String(config.consumer).trim() }
15
+ : {}),
16
+ ...(config.fetch ? { fetch: config.fetch } : {}),
17
+ });
18
+ return true;
19
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { getExampleSlots } from "./exampleData.js";
2
2
 
3
- export { initializeAppointmentClient, isAppointmentClientConfigured } from "./config/initializeAppointmentClient.js";
3
+ export {
4
+ initializeAppointmentClient,
5
+ isAppointmentClientConfigured,
6
+ } from "./config/initializeAppointmentClient.js";
7
+ export { syncBlazeoConnection } from "./config/syncBlazeoConnection.js";
8
+ export { ensureBlazeoHttpReady } from "./config/ensureBlazeoHttpReady.js";
9
+ export type { EnsureBlazeoHttpOptions } from "./config/ensureBlazeoHttpReady.js";
4
10
  export { blazeoClientConfig } from "./config/blazeoClientDefaults.js";
5
11
  export { applyBlazeoClientConfig } from "./config/applyBlazeoDefaults.js";
6
12
  export { createCalendarRoot, CalendarRootModel, CalendarSlotModel, EventModel, ParticipantModel } from "./models/CalendarRootModel.js";