@blazeo.com/calendar-client 1.0.24 → 1.0.27

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 CHANGED
@@ -22,12 +22,26 @@ cd your-app && npm link @blazeo.com/calendar-client
22
22
  Configure once at app startup, then use models and their methods:
23
23
 
24
24
  ```js
25
- import { configure, setBaseUrl, setConsumer, CalendarModel, createRootStore } from '@blazeo.com/calendar-client';
25
+ import {
26
+ configure,
27
+ setBaseUrl,
28
+ setConsumer,
29
+ setApiCredentials,
30
+ fetchAccessToken,
31
+ CalendarModel,
32
+ createRootStore,
33
+ } from '@blazeo.com/calendar-client';
26
34
 
27
35
  configure({ baseUrl: 'https://your-appointment-api.example.com' });
28
36
  // or: setBaseUrl('https://localhost:7051');
29
37
  setConsumer('my-app'); // optional: sent as Consumer header (e.g. for lead source)
30
38
 
39
+ // API JWT (for Authorized routes such as lead export)
40
+ setApiCredentials('your-api-key', 'your-api-secret');
41
+ await fetchAccessToken();
42
+ // Or set a token you obtained elsewhere:
43
+ // setAccessToken('eyJ...', '2026-05-19T12:00:00Z');
44
+
31
45
  // Calendar static methods (no store needed)
32
46
  const timezones = await CalendarModel.getTimeZones();
33
47
  const calendar = await CalendarModel.get('calendar-guid');
@@ -38,12 +52,33 @@ const cal = store.addCalendar({ calendarId: 'my-cal', name: 'My Calendar' });
38
52
  await cal.create(); // POST to backend
39
53
  ```
40
54
 
55
+ ### API JWT authentication
56
+
57
+ The backend issues JWTs via `POST Api/Auth/Token` (`api_key` + `api_secret`). There is no refresh endpoint; when the token expires (~1 hour), call `fetchAccessToken()` again.
58
+
59
+ | Function | Purpose |
60
+ |----------|---------|
61
+ | `setApiCredentials(apiKey, apiSecret)` | Store credentials for token exchange |
62
+ | `fetchAccessToken()` | Get JWT from API and store on config |
63
+ | `setAccessToken(token, expiresAtUtc?)` | Use a token you already have |
64
+ | `ensureValidAccessToken()` | Fetch only if missing or near expiry |
65
+ | `clearAuth()` | Clear token and credentials |
66
+
67
+ All `reqGet` / `reqPost` calls automatically attach `Authorization: Bearer …` and re-issue the token before requests when credentials are configured.
68
+
69
+ ```js
70
+ configure({ baseUrl: 'https://your-api', apiKey: 'key', apiSecret: 'secret' });
71
+ await fetchAccessToken();
72
+ await LeadModel.requestExport('company-key');
73
+ ```
74
+
41
75
  ## API overview
42
76
 
43
77
  - **CalendarModel (static):** `get`, `getByCompany`, `getTimeZones`, `getTimeZone`, `getParticipants`, `getMonth`, `getEvents`, etc.
44
78
  - **EventModel (instance):** `get`, `create`, `cancel`, `getCancellable`, `getAvailability`, `setReminder`
45
79
  - **FlowModel:** Same pattern as Calendar — `FlowModel.create({}, { env })` with no fields; static `get`, `getRaw`, `list`, `createFlow`, `updateFlow`, `delete`, `duplicate`, appearance/embed/public/preview helpers; instance methods mirror those using `flowId` on the snapshot
46
80
  - **LeadModel:** `LeadModel.create({}, { env })`; static `get`, `getRaw`, `getByEmail`, `getByCompany`; instance `get`, `getByEmail`, `getByCompany` (uses `leadId` / `email` / `companyKey` on the snapshot)
81
+ - **ParticipantModel:** static `get`, `getByEmail`, `getByIds`, `getAll`, …; instance `getByEmail` uses `email` + `companyKey` on the snapshot (see `GET /participant/getbyemail`)
47
82
  - **AuthModel (calendar OAuth / Connect Calendar):** `getCalendarProviders`, `getAuthorizationUrl`, `getAuthorizationStatus`, `openOAuthPopup`, `onCalendarAuthMessage` — see [Calendar authorization flow](#calendar-authorization-direct-ui)
48
83
  - **RootStore:** `addCalendar`, `addEvent`
49
84
 
package/dist/index.d.ts CHANGED
@@ -1,9 +1,61 @@
1
1
  /** @blazeo.com/calendar-client - type declarations */
2
2
 
3
- export function configure(env: { baseUrl?: string; consumer?: string; fetch?: typeof fetch; getDefaultOffset?: () => number }): void;
4
- export function getConfig(): { baseUrl?: string; consumer?: string; fetch?: typeof fetch; getDefaultOffset?: () => number } | null;
3
+ export type AccessTokenResult = {
4
+ status: string;
5
+ message?: string;
6
+ accessToken?: string;
7
+ expiresAtUtc?: string | null;
8
+ tokenType?: string;
9
+ };
10
+
11
+ export type AuthState = {
12
+ accessToken?: string;
13
+ tokenExpiresAt?: string;
14
+ apiKey?: string;
15
+ apiSecret?: string;
16
+ hasApiCredentials: boolean;
17
+ };
18
+
19
+ export function configure(env: {
20
+ baseUrl?: string;
21
+ consumer?: string;
22
+ fetch?: typeof fetch;
23
+ getDefaultOffset?: () => number;
24
+ accessToken?: string;
25
+ expiresAtUtc?: string;
26
+ tokenExpiresAt?: string;
27
+ expires_at_utc?: string;
28
+ apiKey?: string;
29
+ api_key?: string;
30
+ apiSecret?: string;
31
+ api_secret?: string;
32
+ }): void;
33
+
34
+ export function getConfig(): {
35
+ baseUrl?: string;
36
+ consumer?: string;
37
+ fetch?: typeof fetch;
38
+ getDefaultOffset?: () => number;
39
+ accessToken?: string;
40
+ tokenExpiresAt?: string;
41
+ hasApiCredentials?: boolean;
42
+ } | null;
43
+
5
44
  export function setBaseUrl(baseUrl: string): void;
6
45
  export function setConsumer(consumer: string): void;
46
+ export function setAccessToken(accessToken: string, expiresAtUtc?: string): void;
47
+ export function clearAccessToken(): void;
48
+ export function setApiCredentials(apiKey: string, apiSecret: string): void;
49
+ export function clearApiCredentials(): void;
50
+ export function clearAuth(): void;
51
+ export function getAuth(): AuthState;
52
+ export function fetchAccessToken(apiKey?: string, apiSecret?: string): Promise<AccessTokenResult>;
53
+ export function ensureValidAccessToken(): Promise<string | undefined>;
54
+ export function requestAccessToken(apiKey: string, apiSecret: string, opts?: { baseUrl?: string; fetch?: typeof fetch }): Promise<AccessTokenResult>;
55
+ export function isAccessTokenExpired(expiresAtUtc?: string | number | Date | null, skewMs?: number): boolean;
56
+ export function buildAuthHeaders(extra?: Record<string, string>): Record<string, string>;
57
+ export const TOKEN_PATH: '/Api/Auth/Token';
58
+ export const DEFAULT_TOKEN_REFRESH_SKEW_MS: number;
7
59
  export function getConfigStore(): unknown;
8
60
 
9
61
  export const ConfigModel: unknown;
@@ -79,6 +131,7 @@ export const CalendarParticipantModel: {
79
131
  };
80
132
  export const ParticipantModel: {
81
133
  get(participantId: string): Promise<unknown>;
134
+ getByEmail(email: string, companyKey: string): Promise<unknown>;
82
135
  getByIds(participantIds: string[] | string): Promise<unknown[] | null>;
83
136
  getAll(companyKey: string): Promise<unknown[] | null>;
84
137
  add(payload: object, calendarId?: string): Promise<unknown>;
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  CompanyModel: () => Company_default,
35
35
  ConfigModel: () => ConfigModel_default,
36
36
  CustomFieldModel: () => CustomField_default,
37
+ DEFAULT_TOKEN_REFRESH_SKEW_MS: () => DEFAULT_TOKEN_REFRESH_SKEW_MS,
37
38
  DayOfWeek: () => DayOfWeek,
38
39
  EmailProvider: () => EmailProvider,
39
40
  EventModel: () => Event_default,
@@ -48,14 +49,27 @@ __export(index_exports, {
48
49
  RecurringFrequency: () => RecurringFrequency,
49
50
  RootStore: () => RootStore,
50
51
  SettingModel: () => Setting_default,
52
+ TOKEN_PATH: () => TOKEN_PATH,
51
53
  TimeFrameModel: () => TimeFrame_default,
52
54
  TimeSlotModel: () => TimeSlot_default,
53
55
  Unit: () => Unit,
56
+ buildAuthHeaders: () => buildAuthHeaders,
57
+ clearAccessToken: () => clearAccessToken,
58
+ clearApiCredentials: () => clearApiCredentials,
59
+ clearAuth: () => clearAuth,
54
60
  configure: () => configure,
55
61
  createRootStore: () => createRootStore,
62
+ ensureValidAccessToken: () => ensureValidAccessToken,
63
+ fetchAccessToken: () => fetchAccessToken2,
64
+ getAuth: () => getAuth,
56
65
  getConfig: () => getConfig,
57
66
  getConfigStore: () => getConfigStore,
58
- setBaseUrl: () => setBaseUrl
67
+ isAccessTokenExpired: () => isAccessTokenExpired,
68
+ requestAccessToken: () => requestAccessToken,
69
+ setAccessToken: () => setAccessToken,
70
+ setApiCredentials: () => setApiCredentials,
71
+ setBaseUrl: () => setBaseUrl,
72
+ setConsumer: () => setConsumer
59
73
  });
60
74
  module.exports = __toCommonJS(index_exports);
61
75
 
@@ -69,6 +83,10 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
69
83
  consumer: import_mobx_state_tree.types.optional(import_mobx_state_tree.types.string, "")
70
84
  }).volatile(() => ({
71
85
  fetch: void 0,
86
+ accessToken: void 0,
87
+ tokenExpiresAt: void 0,
88
+ apiKey: void 0,
89
+ apiSecret: void 0,
72
90
  getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
73
91
  })).actions((self) => ({
74
92
  setBaseUrl(url) {
@@ -83,19 +101,57 @@ var ConfigModel = import_mobx_state_tree.types.model("Config", {
83
101
  setGetDefaultOffset(fn) {
84
102
  self.getDefaultOffset = fn;
85
103
  },
104
+ setAccessToken(token, expiresAtUtc = void 0) {
105
+ self.accessToken = token ? String(token) : void 0;
106
+ self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
107
+ },
108
+ clearAccessToken() {
109
+ self.accessToken = void 0;
110
+ self.tokenExpiresAt = void 0;
111
+ },
112
+ setApiCredentials(apiKey, apiSecret) {
113
+ self.apiKey = apiKey != null ? String(apiKey) : void 0;
114
+ self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
115
+ },
116
+ clearApiCredentials() {
117
+ self.apiKey = void 0;
118
+ self.apiSecret = void 0;
119
+ },
120
+ clearAuth() {
121
+ self.clearAccessToken();
122
+ self.clearApiCredentials();
123
+ },
86
124
  configure(env) {
87
125
  if (env.baseUrl != null) self.baseUrl = env.baseUrl;
88
126
  if (env.consumer != null) self.consumer = env.consumer;
89
127
  if (env.fetch != null) self.fetch = env.fetch;
90
128
  if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
129
+ if (env.accessToken != null) {
130
+ self.setAccessToken(
131
+ env.accessToken,
132
+ env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
133
+ );
134
+ }
135
+ if (env.apiKey != null || env.api_key != null) {
136
+ self.apiKey = String(env.apiKey ?? env.api_key);
137
+ }
138
+ if (env.apiSecret != null || env.api_secret != null) {
139
+ self.apiSecret = String(env.apiSecret ?? env.api_secret);
140
+ }
91
141
  }
92
142
  })).views((self) => ({
143
+ get hasApiCredentials() {
144
+ return Boolean(self.apiKey && self.apiSecret);
145
+ },
93
146
  getEnv() {
94
147
  return {
95
148
  baseUrl: self.baseUrl || void 0,
96
149
  consumer: self.consumer || void 0,
97
150
  fetch: self.fetch,
98
- getDefaultOffset: self.getDefaultOffset
151
+ getDefaultOffset: self.getDefaultOffset,
152
+ accessToken: self.accessToken,
153
+ tokenExpiresAt: self.tokenExpiresAt,
154
+ hasApiCredentials: self.hasApiCredentials
99
155
  };
100
156
  }
101
157
  }));
@@ -106,6 +162,122 @@ function getConfigStore() {
106
162
  }
107
163
  var ConfigModel_default = ConfigModel;
108
164
 
165
+ // src/apiAuth.js
166
+ var TOKEN_PATH = "/Api/Auth/Token";
167
+ var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
168
+ function defaultFetch() {
169
+ if (typeof fetch === "undefined") {
170
+ throw new Error("fetch not available");
171
+ }
172
+ return fetch;
173
+ }
174
+ function pickTokenPayload(data) {
175
+ if (!data || typeof data !== "object") return null;
176
+ const nested = data.data && typeof data.data === "object" ? data.data : data;
177
+ const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
178
+ if (!accessToken) return null;
179
+ const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
180
+ return {
181
+ accessToken: String(accessToken),
182
+ expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
183
+ tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
184
+ };
185
+ }
186
+ function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
187
+ if (expiresAtUtc == null || expiresAtUtc === "") return false;
188
+ const expMs = new Date(expiresAtUtc).getTime();
189
+ if (Number.isNaN(expMs)) return false;
190
+ return Date.now() >= expMs - skewMs;
191
+ }
192
+ function getAuthState() {
193
+ const store = getConfigStore();
194
+ return {
195
+ accessToken: store.accessToken ?? void 0,
196
+ tokenExpiresAt: store.tokenExpiresAt ?? void 0,
197
+ apiKey: store.apiKey ?? void 0,
198
+ apiSecret: store.apiSecret ?? void 0,
199
+ hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
200
+ };
201
+ }
202
+ async function requestAccessToken(apiKey, apiSecret, opts = {}) {
203
+ const store = getConfigStore();
204
+ const baseUrl = opts.baseUrl ?? store.baseUrl;
205
+ if (!baseUrl) {
206
+ return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
207
+ }
208
+ const key = apiKey != null ? String(apiKey).trim() : "";
209
+ const secret = apiSecret != null ? String(apiSecret).trim() : "";
210
+ if (!key || !secret) {
211
+ return { status: "failure", message: "api_key and api_secret are required" };
212
+ }
213
+ const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
214
+ const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
215
+ const httpRes = await fetchFn(url, {
216
+ method: "POST",
217
+ headers: { "Content-Type": "application/json" },
218
+ body: JSON.stringify({ api_key: key, api_secret: secret })
219
+ });
220
+ const text = await httpRes.text();
221
+ let body;
222
+ try {
223
+ body = JSON.parse(text);
224
+ } catch {
225
+ body = { status: "failure", message: text || httpRes.statusText };
226
+ }
227
+ if (!httpRes.ok && body.status !== "failure") {
228
+ body.status = "failure";
229
+ body.message = body.message ?? `HTTP ${httpRes.status}`;
230
+ }
231
+ if (body.status !== "success") {
232
+ return {
233
+ status: "failure",
234
+ message: body.message ?? "Failed to obtain access token"
235
+ };
236
+ }
237
+ const parsed = pickTokenPayload(body);
238
+ if (!parsed) {
239
+ return { status: "failure", message: "Token response missing access_token" };
240
+ }
241
+ return {
242
+ status: "success",
243
+ accessToken: parsed.accessToken,
244
+ expiresAtUtc: parsed.expiresAtUtc,
245
+ tokenType: parsed.tokenType
246
+ };
247
+ }
248
+ async function fetchAccessToken(apiKey, apiSecret) {
249
+ const store = getConfigStore();
250
+ const key = apiKey ?? store.apiKey;
251
+ const secret = apiSecret ?? store.apiSecret;
252
+ const result = await requestAccessToken(key, secret);
253
+ if (result.status === "success") {
254
+ store.setAccessToken(result.accessToken, result.expiresAtUtc);
255
+ }
256
+ return result;
257
+ }
258
+ async function ensureAccessToken() {
259
+ const store = getConfigStore();
260
+ const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
261
+ if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
262
+ return accessToken;
263
+ }
264
+ if (apiKey && apiSecret) {
265
+ const result = await fetchAccessToken(apiKey, apiSecret);
266
+ if (result.status === "success") return result.accessToken;
267
+ }
268
+ return accessToken;
269
+ }
270
+ function buildAuthHeaders(extra = {}) {
271
+ const headers = { ...extra };
272
+ const { consumer } = getConfigStore();
273
+ const { accessToken } = getAuthState();
274
+ if (!headers.Consumer && consumer) headers.Consumer = consumer;
275
+ if (!headers.Authorization && accessToken) {
276
+ headers.Authorization = `Bearer ${accessToken}`;
277
+ }
278
+ return headers;
279
+ }
280
+
109
281
  // src/config.js
110
282
  function configure(env) {
111
283
  const store = getConfigStore();
@@ -119,6 +291,33 @@ function getConfig() {
119
291
  function setBaseUrl(baseUrl) {
120
292
  getConfigStore().setBaseUrl(baseUrl);
121
293
  }
294
+ function setConsumer(consumer) {
295
+ getConfigStore().setConsumer(consumer);
296
+ }
297
+ function setAccessToken(accessToken, expiresAtUtc) {
298
+ getConfigStore().setAccessToken(accessToken, expiresAtUtc);
299
+ }
300
+ function clearAccessToken() {
301
+ getConfigStore().clearAccessToken();
302
+ }
303
+ function setApiCredentials(apiKey, apiSecret) {
304
+ getConfigStore().setApiCredentials(apiKey, apiSecret);
305
+ }
306
+ function clearApiCredentials() {
307
+ getConfigStore().clearApiCredentials();
308
+ }
309
+ function clearAuth() {
310
+ getConfigStore().clearAuth();
311
+ }
312
+ function getAuth() {
313
+ return getAuthState();
314
+ }
315
+ function fetchAccessToken2(apiKey, apiSecret) {
316
+ return fetchAccessToken(apiKey, apiSecret);
317
+ }
318
+ function ensureValidAccessToken() {
319
+ return ensureAccessToken();
320
+ }
122
321
 
123
322
  // src/apiRequest.js
124
323
  function buildQuery(params) {
@@ -135,10 +334,18 @@ function buildQuery(params) {
135
334
  return q ? `?${q}` : "";
136
335
  }
137
336
  async function request(baseUrl, fetchFn, path, options = {}) {
138
- const { method = "GET", headers = {}, body, query, skipContentType } = options;
337
+ const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
139
338
  const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
140
339
  const reqHeaders = { ...headers };
141
340
  if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
341
+ if (!skipAuth) {
342
+ const store = getConfigStore();
343
+ if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
344
+ const { accessToken } = getAuthState();
345
+ if (!reqHeaders["Authorization"] && accessToken) {
346
+ reqHeaders["Authorization"] = `Bearer ${accessToken}`;
347
+ }
348
+ }
142
349
  const res = await fetchFn(url, { method, headers: reqHeaders, body });
143
350
  const text = await res.text();
144
351
  let data;
@@ -151,23 +358,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
151
358
  data.status = "failure";
152
359
  data.message = data.message ?? `HTTP ${res.status}`;
153
360
  }
361
+ data._httpStatus = res.status;
154
362
  return data;
155
363
  }
156
- function mergeConsumerHeader(env, opts) {
364
+ function mergeRequestHeaders(env, opts) {
157
365
  const headers = { ...opts.headers || {} };
158
- if (!headers["Consumer"] && (env == null ? void 0 : env.consumer)) headers["Consumer"] = env.consumer;
366
+ const store = getConfigStore();
367
+ if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
368
+ headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
369
+ }
370
+ const { accessToken } = getAuthState();
371
+ if (!headers["Authorization"] && accessToken) {
372
+ headers["Authorization"] = `Bearer ${accessToken}`;
373
+ }
159
374
  return { ...opts, headers };
160
375
  }
376
+ async function executeRequest(baseUrl, fetchFn, path, opts) {
377
+ if (!opts.skipAuth) await ensureAccessToken();
378
+ return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
379
+ }
161
380
  function createRequestHelpers(self, getEnv12) {
162
381
  const env = () => getEnv12(self);
163
382
  const baseUrl = () => env().baseUrl;
164
- const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
383
+ const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
165
384
  throw new Error("fetch not available");
166
385
  });
167
386
  const req = (path, opts = {}) => {
168
387
  const url = baseUrl();
169
388
  if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
170
- return request(url, fetchFn(), path, mergeConsumerHeader(env(), opts));
389
+ return executeRequest(url, fetchFn(), path, opts);
171
390
  };
172
391
  return {
173
392
  req,
@@ -178,14 +397,14 @@ function createRequestHelpers(self, getEnv12) {
178
397
  function createRequestHelpersFromEnv(env) {
179
398
  const e = env ?? getConfig();
180
399
  if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
181
- const baseUrl = () => e == null ? void 0 : e.baseUrl;
182
- const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
400
+ const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
401
+ const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
183
402
  throw new Error("fetch not available");
184
403
  });
185
404
  const req = (path, opts = {}) => {
186
405
  const url = baseUrl();
187
406
  if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
188
- return request(url, fetchFn(), path, mergeConsumerHeader(e, opts));
407
+ return executeRequest(url, fetchFn(), path, opts);
189
408
  };
190
409
  return {
191
410
  env: e,
@@ -1300,6 +1519,15 @@ var ParticipantModel = import_mobx_state_tree11.types.model("Participant", {
1300
1519
  if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
1301
1520
  return res;
1302
1521
  },
1522
+ /** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
1523
+ async getByEmail() {
1524
+ const res = await reqGet("/participant/getbyemail", {
1525
+ email: self.email,
1526
+ company_key: self.companyKey
1527
+ });
1528
+ if (res.status === "success" && res.data) (0, import_mobx_state_tree11.applySnapshot)(self, mapFromApi2(res.data));
1529
+ return res;
1530
+ },
1303
1531
  /** POST participant/save – save participant (add or update) */
1304
1532
  async save() {
1305
1533
  const payload = toPayload(self);
@@ -1415,6 +1643,14 @@ function mapCalendarFromApi2(d) {
1415
1643
  modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
1416
1644
  };
1417
1645
  }
1646
+ ParticipantModel.getByEmail = async (email, companyKey) => {
1647
+ const { reqGet } = createRequestHelpersFromEnv(getConfig());
1648
+ const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
1649
+ if (res.status === "success" && res.data) {
1650
+ return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
1651
+ }
1652
+ return null;
1653
+ };
1418
1654
  ParticipantModel.get = async (participantId) => {
1419
1655
  const { reqGet } = createRequestHelpersFromEnv(getConfig());
1420
1656
  const res = await reqGet("/participant/get", { participant_id: participantId });
@@ -1593,6 +1829,7 @@ SettingModel.save = async (payload) => {
1593
1829
  SettingModel.uploadLogo = async (calendarId, file) => {
1594
1830
  const cfg = getConfig();
1595
1831
  if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
1832
+ await ensureAccessToken();
1596
1833
  const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
1597
1834
  throw new Error("fetch not available");
1598
1835
  });
@@ -1602,6 +1839,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
1602
1839
  formData.append("file", file);
1603
1840
  const res = await fetchFn(url, {
1604
1841
  method: "POST",
1842
+ headers: buildAuthHeaders(),
1605
1843
  body: formData
1606
1844
  });
1607
1845
  const text = await res.text();
@@ -1724,6 +1962,7 @@ AssetModel.upload = async (file, opts = {}) => {
1724
1962
  if (!file) return { status: "failure", message: "file is required" };
1725
1963
  const category = opts.category != null ? String(opts.category).trim() : "";
1726
1964
  if (!category) return { status: "failure", message: "category is required" };
1965
+ await ensureAccessToken();
1727
1966
  const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
1728
1967
  throw new Error("fetch not available");
1729
1968
  });
@@ -1738,8 +1977,7 @@ AssetModel.upload = async (file, opts = {}) => {
1738
1977
  if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
1739
1978
  formData.append("calendar_id", String(opts.calendarId).trim());
1740
1979
  }
1741
- const headers = {};
1742
- if (cfg.consumer) headers.Consumer = cfg.consumer;
1980
+ const headers = buildAuthHeaders();
1743
1981
  if (opts.consumer != null && String(opts.consumer).trim() !== "") {
1744
1982
  headers.Consumer = String(opts.consumer).trim();
1745
1983
  }
@@ -2782,6 +3020,7 @@ function createRootStore(initialState = {}) {
2782
3020
  CompanyModel,
2783
3021
  ConfigModel,
2784
3022
  CustomFieldModel,
3023
+ DEFAULT_TOKEN_REFRESH_SKEW_MS,
2785
3024
  DayOfWeek,
2786
3025
  EmailProvider,
2787
3026
  EventModel,
@@ -2796,12 +3035,25 @@ function createRootStore(initialState = {}) {
2796
3035
  RecurringFrequency,
2797
3036
  RootStore,
2798
3037
  SettingModel,
3038
+ TOKEN_PATH,
2799
3039
  TimeFrameModel,
2800
3040
  TimeSlotModel,
2801
3041
  Unit,
3042
+ buildAuthHeaders,
3043
+ clearAccessToken,
3044
+ clearApiCredentials,
3045
+ clearAuth,
2802
3046
  configure,
2803
3047
  createRootStore,
3048
+ ensureValidAccessToken,
3049
+ fetchAccessToken,
3050
+ getAuth,
2804
3051
  getConfig,
2805
3052
  getConfigStore,
2806
- setBaseUrl
3053
+ isAccessTokenExpired,
3054
+ requestAccessToken,
3055
+ setAccessToken,
3056
+ setApiCredentials,
3057
+ setBaseUrl,
3058
+ setConsumer
2807
3059
  });
package/dist/index.mjs CHANGED
@@ -8,6 +8,10 @@ var ConfigModel = types.model("Config", {
8
8
  consumer: types.optional(types.string, "")
9
9
  }).volatile(() => ({
10
10
  fetch: void 0,
11
+ accessToken: void 0,
12
+ tokenExpiresAt: void 0,
13
+ apiKey: void 0,
14
+ apiSecret: void 0,
11
15
  getDefaultOffset: () => -(/* @__PURE__ */ new Date()).getTimezoneOffset()
12
16
  })).actions((self) => ({
13
17
  setBaseUrl(url) {
@@ -22,19 +26,57 @@ var ConfigModel = types.model("Config", {
22
26
  setGetDefaultOffset(fn) {
23
27
  self.getDefaultOffset = fn;
24
28
  },
29
+ setAccessToken(token, expiresAtUtc = void 0) {
30
+ self.accessToken = token ? String(token) : void 0;
31
+ self.tokenExpiresAt = expiresAtUtc != null && expiresAtUtc !== "" ? String(expiresAtUtc) : void 0;
32
+ },
33
+ clearAccessToken() {
34
+ self.accessToken = void 0;
35
+ self.tokenExpiresAt = void 0;
36
+ },
37
+ setApiCredentials(apiKey, apiSecret) {
38
+ self.apiKey = apiKey != null ? String(apiKey) : void 0;
39
+ self.apiSecret = apiSecret != null ? String(apiSecret) : void 0;
40
+ },
41
+ clearApiCredentials() {
42
+ self.apiKey = void 0;
43
+ self.apiSecret = void 0;
44
+ },
45
+ clearAuth() {
46
+ self.clearAccessToken();
47
+ self.clearApiCredentials();
48
+ },
25
49
  configure(env) {
26
50
  if (env.baseUrl != null) self.baseUrl = env.baseUrl;
27
51
  if (env.consumer != null) self.consumer = env.consumer;
28
52
  if (env.fetch != null) self.fetch = env.fetch;
29
53
  if (env.getDefaultOffset != null) self.getDefaultOffset = env.getDefaultOffset;
54
+ if (env.accessToken != null) {
55
+ self.setAccessToken(
56
+ env.accessToken,
57
+ env.expiresAtUtc ?? env.tokenExpiresAt ?? env.expires_at_utc
58
+ );
59
+ }
60
+ if (env.apiKey != null || env.api_key != null) {
61
+ self.apiKey = String(env.apiKey ?? env.api_key);
62
+ }
63
+ if (env.apiSecret != null || env.api_secret != null) {
64
+ self.apiSecret = String(env.apiSecret ?? env.api_secret);
65
+ }
30
66
  }
31
67
  })).views((self) => ({
68
+ get hasApiCredentials() {
69
+ return Boolean(self.apiKey && self.apiSecret);
70
+ },
32
71
  getEnv() {
33
72
  return {
34
73
  baseUrl: self.baseUrl || void 0,
35
74
  consumer: self.consumer || void 0,
36
75
  fetch: self.fetch,
37
- getDefaultOffset: self.getDefaultOffset
76
+ getDefaultOffset: self.getDefaultOffset,
77
+ accessToken: self.accessToken,
78
+ tokenExpiresAt: self.tokenExpiresAt,
79
+ hasApiCredentials: self.hasApiCredentials
38
80
  };
39
81
  }
40
82
  }));
@@ -45,6 +87,122 @@ function getConfigStore() {
45
87
  }
46
88
  var ConfigModel_default = ConfigModel;
47
89
 
90
+ // src/apiAuth.js
91
+ var TOKEN_PATH = "/Api/Auth/Token";
92
+ var DEFAULT_TOKEN_REFRESH_SKEW_MS = 6e4;
93
+ function defaultFetch() {
94
+ if (typeof fetch === "undefined") {
95
+ throw new Error("fetch not available");
96
+ }
97
+ return fetch;
98
+ }
99
+ function pickTokenPayload(data) {
100
+ if (!data || typeof data !== "object") return null;
101
+ const nested = data.data && typeof data.data === "object" ? data.data : data;
102
+ const accessToken = nested.access_token ?? nested.accessToken ?? nested.AccessToken ?? null;
103
+ if (!accessToken) return null;
104
+ const expiresAtUtc = nested.expires_at_utc ?? nested.expiresAtUtc ?? nested.ExpiresAtUtc ?? null;
105
+ return {
106
+ accessToken: String(accessToken),
107
+ expiresAtUtc: expiresAtUtc != null ? String(expiresAtUtc) : null,
108
+ tokenType: nested.token_type ?? nested.tokenType ?? "Bearer"
109
+ };
110
+ }
111
+ function isAccessTokenExpired(expiresAtUtc, skewMs = DEFAULT_TOKEN_REFRESH_SKEW_MS) {
112
+ if (expiresAtUtc == null || expiresAtUtc === "") return false;
113
+ const expMs = new Date(expiresAtUtc).getTime();
114
+ if (Number.isNaN(expMs)) return false;
115
+ return Date.now() >= expMs - skewMs;
116
+ }
117
+ function getAuthState() {
118
+ const store = getConfigStore();
119
+ return {
120
+ accessToken: store.accessToken ?? void 0,
121
+ tokenExpiresAt: store.tokenExpiresAt ?? void 0,
122
+ apiKey: store.apiKey ?? void 0,
123
+ apiSecret: store.apiSecret ?? void 0,
124
+ hasApiCredentials: Boolean(store.apiKey && store.apiSecret)
125
+ };
126
+ }
127
+ async function requestAccessToken(apiKey, apiSecret, opts = {}) {
128
+ const store = getConfigStore();
129
+ const baseUrl = opts.baseUrl ?? store.baseUrl;
130
+ if (!baseUrl) {
131
+ return { status: "failure", message: "baseUrl required. Call configure({ baseUrl }) first." };
132
+ }
133
+ const key = apiKey != null ? String(apiKey).trim() : "";
134
+ const secret = apiSecret != null ? String(apiSecret).trim() : "";
135
+ if (!key || !secret) {
136
+ return { status: "failure", message: "api_key and api_secret are required" };
137
+ }
138
+ const fetchFn = opts.fetch ?? store.fetch ?? defaultFetch();
139
+ const url = `${String(baseUrl).replace(/\/+$/, "")}${TOKEN_PATH}`;
140
+ const httpRes = await fetchFn(url, {
141
+ method: "POST",
142
+ headers: { "Content-Type": "application/json" },
143
+ body: JSON.stringify({ api_key: key, api_secret: secret })
144
+ });
145
+ const text = await httpRes.text();
146
+ let body;
147
+ try {
148
+ body = JSON.parse(text);
149
+ } catch {
150
+ body = { status: "failure", message: text || httpRes.statusText };
151
+ }
152
+ if (!httpRes.ok && body.status !== "failure") {
153
+ body.status = "failure";
154
+ body.message = body.message ?? `HTTP ${httpRes.status}`;
155
+ }
156
+ if (body.status !== "success") {
157
+ return {
158
+ status: "failure",
159
+ message: body.message ?? "Failed to obtain access token"
160
+ };
161
+ }
162
+ const parsed = pickTokenPayload(body);
163
+ if (!parsed) {
164
+ return { status: "failure", message: "Token response missing access_token" };
165
+ }
166
+ return {
167
+ status: "success",
168
+ accessToken: parsed.accessToken,
169
+ expiresAtUtc: parsed.expiresAtUtc,
170
+ tokenType: parsed.tokenType
171
+ };
172
+ }
173
+ async function fetchAccessToken(apiKey, apiSecret) {
174
+ const store = getConfigStore();
175
+ const key = apiKey ?? store.apiKey;
176
+ const secret = apiSecret ?? store.apiSecret;
177
+ const result = await requestAccessToken(key, secret);
178
+ if (result.status === "success") {
179
+ store.setAccessToken(result.accessToken, result.expiresAtUtc);
180
+ }
181
+ return result;
182
+ }
183
+ async function ensureAccessToken() {
184
+ const store = getConfigStore();
185
+ const { accessToken, tokenExpiresAt, apiKey, apiSecret } = getAuthState();
186
+ if (accessToken && !isAccessTokenExpired(tokenExpiresAt)) {
187
+ return accessToken;
188
+ }
189
+ if (apiKey && apiSecret) {
190
+ const result = await fetchAccessToken(apiKey, apiSecret);
191
+ if (result.status === "success") return result.accessToken;
192
+ }
193
+ return accessToken;
194
+ }
195
+ function buildAuthHeaders(extra = {}) {
196
+ const headers = { ...extra };
197
+ const { consumer } = getConfigStore();
198
+ const { accessToken } = getAuthState();
199
+ if (!headers.Consumer && consumer) headers.Consumer = consumer;
200
+ if (!headers.Authorization && accessToken) {
201
+ headers.Authorization = `Bearer ${accessToken}`;
202
+ }
203
+ return headers;
204
+ }
205
+
48
206
  // src/config.js
49
207
  function configure(env) {
50
208
  const store = getConfigStore();
@@ -58,6 +216,33 @@ function getConfig() {
58
216
  function setBaseUrl(baseUrl) {
59
217
  getConfigStore().setBaseUrl(baseUrl);
60
218
  }
219
+ function setConsumer(consumer) {
220
+ getConfigStore().setConsumer(consumer);
221
+ }
222
+ function setAccessToken(accessToken, expiresAtUtc) {
223
+ getConfigStore().setAccessToken(accessToken, expiresAtUtc);
224
+ }
225
+ function clearAccessToken() {
226
+ getConfigStore().clearAccessToken();
227
+ }
228
+ function setApiCredentials(apiKey, apiSecret) {
229
+ getConfigStore().setApiCredentials(apiKey, apiSecret);
230
+ }
231
+ function clearApiCredentials() {
232
+ getConfigStore().clearApiCredentials();
233
+ }
234
+ function clearAuth() {
235
+ getConfigStore().clearAuth();
236
+ }
237
+ function getAuth() {
238
+ return getAuthState();
239
+ }
240
+ function fetchAccessToken2(apiKey, apiSecret) {
241
+ return fetchAccessToken(apiKey, apiSecret);
242
+ }
243
+ function ensureValidAccessToken() {
244
+ return ensureAccessToken();
245
+ }
61
246
 
62
247
  // src/apiRequest.js
63
248
  function buildQuery(params) {
@@ -74,10 +259,18 @@ function buildQuery(params) {
74
259
  return q ? `?${q}` : "";
75
260
  }
76
261
  async function request(baseUrl, fetchFn, path, options = {}) {
77
- const { method = "GET", headers = {}, body, query, skipContentType } = options;
262
+ const { method = "GET", headers = {}, body, query, skipContentType, skipAuth } = options;
78
263
  const url = `${String(baseUrl).replace(/\/+$/, "")}${path}${buildQuery(query)}`;
79
264
  const reqHeaders = { ...headers };
80
265
  if (!skipContentType && typeof body === "string") reqHeaders["Content-Type"] = "application/json";
266
+ if (!skipAuth) {
267
+ const store = getConfigStore();
268
+ if (!reqHeaders["Consumer"] && store.consumer) reqHeaders["Consumer"] = store.consumer;
269
+ const { accessToken } = getAuthState();
270
+ if (!reqHeaders["Authorization"] && accessToken) {
271
+ reqHeaders["Authorization"] = `Bearer ${accessToken}`;
272
+ }
273
+ }
81
274
  const res = await fetchFn(url, { method, headers: reqHeaders, body });
82
275
  const text = await res.text();
83
276
  let data;
@@ -90,23 +283,35 @@ async function request(baseUrl, fetchFn, path, options = {}) {
90
283
  data.status = "failure";
91
284
  data.message = data.message ?? `HTTP ${res.status}`;
92
285
  }
286
+ data._httpStatus = res.status;
93
287
  return data;
94
288
  }
95
- function mergeConsumerHeader(env, opts) {
289
+ function mergeRequestHeaders(env, opts) {
96
290
  const headers = { ...opts.headers || {} };
97
- if (!headers["Consumer"] && (env == null ? void 0 : env.consumer)) headers["Consumer"] = env.consumer;
291
+ const store = getConfigStore();
292
+ if (!headers["Consumer"] && ((env == null ? void 0 : env.consumer) || store.consumer)) {
293
+ headers["Consumer"] = (env == null ? void 0 : env.consumer) || store.consumer;
294
+ }
295
+ const { accessToken } = getAuthState();
296
+ if (!headers["Authorization"] && accessToken) {
297
+ headers["Authorization"] = `Bearer ${accessToken}`;
298
+ }
98
299
  return { ...opts, headers };
99
300
  }
301
+ async function executeRequest(baseUrl, fetchFn, path, opts) {
302
+ if (!opts.skipAuth) await ensureAccessToken();
303
+ return request(baseUrl, fetchFn, path, mergeRequestHeaders(null, opts));
304
+ }
100
305
  function createRequestHelpers(self, getEnv12) {
101
306
  const env = () => getEnv12(self);
102
307
  const baseUrl = () => env().baseUrl;
103
- const fetchFn = () => env().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
308
+ const fetchFn = () => env().fetch ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
104
309
  throw new Error("fetch not available");
105
310
  });
106
311
  const req = (path, opts = {}) => {
107
312
  const url = baseUrl();
108
313
  if (!url) throw new Error("Model env requires baseUrl. Call configure({ baseUrl }) at app startup.");
109
- return request(url, fetchFn(), path, mergeConsumerHeader(env(), opts));
314
+ return executeRequest(url, fetchFn(), path, opts);
110
315
  };
111
316
  return {
112
317
  req,
@@ -117,14 +322,14 @@ function createRequestHelpers(self, getEnv12) {
117
322
  function createRequestHelpersFromEnv(env) {
118
323
  const e = env ?? getConfig();
119
324
  if (!e) throw new Error("Env required. Pass env to the method or call configure({ baseUrl }) at app startup.");
120
- const baseUrl = () => e == null ? void 0 : e.baseUrl;
121
- const fetchFn = () => (e == null ? void 0 : e.fetch) ?? (typeof fetch !== "undefined" ? fetch : () => {
325
+ const baseUrl = () => (e == null ? void 0 : e.baseUrl) ?? getConfigStore().baseUrl;
326
+ const fetchFn = () => (e == null ? void 0 : e.fetch) ?? getConfigStore().fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
122
327
  throw new Error("fetch not available");
123
328
  });
124
329
  const req = (path, opts = {}) => {
125
330
  const url = baseUrl();
126
331
  if (!url) throw new Error("Env requires baseUrl. Call configure({ baseUrl }) at app startup.");
127
- return request(url, fetchFn(), path, mergeConsumerHeader(e, opts));
332
+ return executeRequest(url, fetchFn(), path, opts);
128
333
  };
129
334
  return {
130
335
  env: e,
@@ -1239,6 +1444,15 @@ var ParticipantModel = types11.model("Participant", {
1239
1444
  if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
1240
1445
  return res;
1241
1446
  },
1447
+ /** GET participant/getbyemail – fetch by companyKey + email on this snapshot */
1448
+ async getByEmail() {
1449
+ const res = await reqGet("/participant/getbyemail", {
1450
+ email: self.email,
1451
+ company_key: self.companyKey
1452
+ });
1453
+ if (res.status === "success" && res.data) applySnapshot3(self, mapFromApi2(res.data));
1454
+ return res;
1455
+ },
1242
1456
  /** POST participant/save – save participant (add or update) */
1243
1457
  async save() {
1244
1458
  const payload = toPayload(self);
@@ -1354,6 +1568,14 @@ function mapCalendarFromApi2(d) {
1354
1568
  modifiedOn: pick2("modifiedOn", "ModifiedOn", "modified_on") ?? null
1355
1569
  };
1356
1570
  }
1571
+ ParticipantModel.getByEmail = async (email, companyKey) => {
1572
+ const { reqGet } = createRequestHelpersFromEnv(getConfig());
1573
+ const res = await reqGet("/participant/getbyemail", { email, company_key: companyKey });
1574
+ if (res.status === "success" && res.data) {
1575
+ return ParticipantModel.create(mapFromApi2(res.data), { env: getConfig() });
1576
+ }
1577
+ return null;
1578
+ };
1357
1579
  ParticipantModel.get = async (participantId) => {
1358
1580
  const { reqGet } = createRequestHelpersFromEnv(getConfig());
1359
1581
  const res = await reqGet("/participant/get", { participant_id: participantId });
@@ -1532,6 +1754,7 @@ SettingModel.save = async (payload) => {
1532
1754
  SettingModel.uploadLogo = async (calendarId, file) => {
1533
1755
  const cfg = getConfig();
1534
1756
  if (!(cfg == null ? void 0 : cfg.baseUrl)) throw new Error("Configure baseUrl before uploadLogo");
1757
+ await ensureAccessToken();
1535
1758
  const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
1536
1759
  throw new Error("fetch not available");
1537
1760
  });
@@ -1541,6 +1764,7 @@ SettingModel.uploadLogo = async (calendarId, file) => {
1541
1764
  formData.append("file", file);
1542
1765
  const res = await fetchFn(url, {
1543
1766
  method: "POST",
1767
+ headers: buildAuthHeaders(),
1544
1768
  body: formData
1545
1769
  });
1546
1770
  const text = await res.text();
@@ -1663,6 +1887,7 @@ AssetModel.upload = async (file, opts = {}) => {
1663
1887
  if (!file) return { status: "failure", message: "file is required" };
1664
1888
  const category = opts.category != null ? String(opts.category).trim() : "";
1665
1889
  if (!category) return { status: "failure", message: "category is required" };
1890
+ await ensureAccessToken();
1666
1891
  const fetchFn = cfg.fetch ?? (typeof fetch !== "undefined" ? fetch : () => {
1667
1892
  throw new Error("fetch not available");
1668
1893
  });
@@ -1677,8 +1902,7 @@ AssetModel.upload = async (file, opts = {}) => {
1677
1902
  if (opts.calendarId != null && String(opts.calendarId).trim() !== "") {
1678
1903
  formData.append("calendar_id", String(opts.calendarId).trim());
1679
1904
  }
1680
- const headers = {};
1681
- if (cfg.consumer) headers.Consumer = cfg.consumer;
1905
+ const headers = buildAuthHeaders();
1682
1906
  if (opts.consumer != null && String(opts.consumer).trim() !== "") {
1683
1907
  headers.Consumer = String(opts.consumer).trim();
1684
1908
  }
@@ -2720,6 +2944,7 @@ export {
2720
2944
  Company_default as CompanyModel,
2721
2945
  ConfigModel_default as ConfigModel,
2722
2946
  CustomField_default as CustomFieldModel,
2947
+ DEFAULT_TOKEN_REFRESH_SKEW_MS,
2723
2948
  DayOfWeek,
2724
2949
  EmailProvider,
2725
2950
  Event_default as EventModel,
@@ -2734,12 +2959,25 @@ export {
2734
2959
  RecurringFrequency,
2735
2960
  RootStore,
2736
2961
  Setting_default as SettingModel,
2962
+ TOKEN_PATH,
2737
2963
  TimeFrame_default as TimeFrameModel,
2738
2964
  TimeSlot_default as TimeSlotModel,
2739
2965
  Unit,
2966
+ buildAuthHeaders,
2967
+ clearAccessToken,
2968
+ clearApiCredentials,
2969
+ clearAuth,
2740
2970
  configure,
2741
2971
  createRootStore,
2972
+ ensureValidAccessToken,
2973
+ fetchAccessToken2 as fetchAccessToken,
2974
+ getAuth,
2742
2975
  getConfig,
2743
2976
  getConfigStore,
2744
- setBaseUrl
2977
+ isAccessTokenExpired,
2978
+ requestAccessToken,
2979
+ setAccessToken,
2980
+ setApiCredentials,
2981
+ setBaseUrl,
2982
+ setConsumer
2745
2983
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazeo.com/calendar-client",
3
- "version": "1.0.24",
3
+ "version": "1.0.27",
4
4
  "description": "Blazeo Calendar / Appointment API client with MobX State Tree models",
5
5
  "exports": {
6
6
  ".": {