@bookla-app/react-client-sdk
Version:
React SDK for Bookla Client API
862 lines (845 loc) • 31.3 kB
JavaScript
;
var zod = require('zod');
class InterceptorManager {
constructor() {
this.interceptors = [];
}
use(interceptor) {
this.interceptors.push(interceptor);
return () => {
const index = this.interceptors.indexOf(interceptor);
if (index !== -1) {
this.interceptors.splice(index, 1);
}
};
}
async execute(config) {
let result = config;
for (const interceptor of this.interceptors) {
result = await interceptor(result);
}
return result;
}
}
class BooklaError extends Error {
constructor(message, code, status) {
super(message);
this.code = code;
this.status = status;
this.name = "BooklaError";
}
}
class BooklaValidationError extends BooklaError {
constructor(message, details) {
super(message, "VALIDATION_ERROR");
this.details = details;
this.name = "BooklaValidationError";
}
}
class CancelToken {
constructor() {
this.isCancelled = false;
}
cancel() {
this.isCancelled = true;
}
get cancelled() {
return this.isCancelled;
}
}
const storage = {
setTokens(tokens) {
localStorage.setItem("bookla_access_token", tokens.accessToken);
localStorage.setItem("bookla_refresh_token", tokens.refreshToken);
localStorage.setItem("bookla_expires_at", tokens.expiresAt);
},
getTokens() {
const accessToken = localStorage.getItem("bookla_access_token");
const refreshToken = localStorage.getItem("bookla_refresh_token");
const expiresAt = localStorage.getItem("bookla_expires_at");
if (accessToken && refreshToken && expiresAt) {
return { accessToken, refreshToken, expiresAt };
}
return null;
},
clearTokens() {
localStorage.removeItem("bookla_access_token");
localStorage.removeItem("bookla_refresh_token");
localStorage.removeItem("bookla_expires_at");
},
};
const ENDPOINTS = {
bookings: {
list: { path: "/v1/client/bookings", auth: "bearer" },
get: { path: "/v1/client/bookings/{id}", auth: "bearer" },
create: { path: "/v1/client/bookings", auth: "apiKeyOrBearer" },
cancel: {
path: "/v1/client/bookings/{id}/cancel",
auth: "bearer",
},
},
services: {
list: {
path: "/v1/client/companies/{companyId}/services",
auth: "apiKey",
},
get: {
path: "/v1/client/companies/{companyId}/services/{id}",
auth: "apiKey",
},
getTimes: {
path: "/v1/client/companies/{companyId}/services/{id}/times",
auth: "apiKey",
},
getDates: {
path: "/v1/client/companies/{companyId}/services/{id}/dates",
auth: "apiKey",
},
},
resources: {
list: {
path: "/v1/client/companies/{companyId}/resources",
auth: "apiKey",
},
get: {
path: "/v1/client/companies/{companyId}/resources/{id}",
auth: "apiKey",
},
},
subscriptions: {
cart: {
get: {
path: "/v1/companies/{companyId}/plugins/subscription/client/cart",
auth: "bearer",
},
add: {
path: "/v1/companies/{companyId}/plugins/subscription/client/cart",
auth: "bearer",
},
remove: {
path: "/v1/companies/{companyId}/plugins/subscription/client/cart/{itemId}",
auth: "bearer",
},
checkout: {
path: "/v1/companies/{companyId}/plugins/subscription/client/cart/checkout",
auth: "bearer",
},
},
purchases: {
list: {
path: "/v1/companies/{companyId}/plugins/subscription/client/purchases",
auth: "bearer",
},
get: {
path: "/v1/companies/{companyId}/plugins/subscription/client/purchases/{itemId}",
auth: "bearer",
},
renew: {
path: "/v1/companies/{companyId}/plugins/subscription/client/purchases/renew",
auth: "bearer",
},
create: {
path: "/v1/companies/{companyId}/plugins/subscription/client/purchases",
auth: "apiKeyOrBearer",
},
},
available: {
path: "/v1/companies/{companyId}/plugins/subscription/client/subscriptions",
auth: "apiKey",
},
},
giftCards: {
purchases: {
list: {
path: "/v1/companies/{companyId}/plugins/giftcards/client/orders",
auth: "bearer",
},
get: {
path: "/v1/companies/{companyId}/plugins/giftcards/client/orders/{itemId}",
auth: "bearer",
},
create: {
path: "/v1/companies/{companyId}/plugins/giftcards/client/orders",
auth: "apiKeyOrBearer",
},
},
available: {
path: "/v1/companies/{companyId}/plugins/giftcards/client/giftcards",
auth: "apiKey",
},
},
codes: {
validate: {
path: "/v1/client/codes/{code}/validate",
auth: "apiKeyOrBearer",
},
},
auth: {
refresh: {
path: "/v1/client/auth/refresh",
auth: "bearer",
},
},
addons: {
discover: {
path: "/v1/companies/{companyId}/plugins/addons/client/services/{serviceId}",
auth: "apiKey",
},
validate: {
path: "/v1/companies/{companyId}/plugins/addons/client/services/{serviceId}/validate",
auth: "apiKey",
},
},
};
class HttpClient {
constructor(config) {
this.tokens = null;
// Request deduplication: tracks in-flight requests to prevent duplicates
this.pendingRequests = new Map();
// Development warnings: tracks request patterns to detect potential issues
this.requestHistory = new Map();
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
this.baseURL = config.apiUrl;
this.apiKey = config.apiKey;
this.timeout = config.timeout || 30000;
this.retry = config.retry || {
maxAttempts: 3,
delayMs: 1000,
statusCodesToRetry: [408, 429, 500, 502, 503, 504],
};
this.debug = config.debug || false;
}
setTokens(tokens) {
this.tokens = tokens;
this.saveTokensToStorage(tokens);
}
async isAuthenticated() {
var _a;
if ((_a = this.tokens) === null || _a === void 0 ? void 0 : _a.accessToken) {
const expiresAt = Date.parse(this.tokens.expiresAt);
if (expiresAt < Date.now()) {
const refreshed = await this.refreshToken();
if (!refreshed) {
this.clearAuth();
return { isAuthenticated: false };
}
return {
isAuthenticated: true,
accessToken: this.tokens.accessToken,
expiresAt: Date.parse(this.tokens.expiresAt),
};
}
return {
isAuthenticated: true,
accessToken: this.tokens.accessToken,
expiresAt: Date.parse(this.tokens.expiresAt),
};
}
// Check localStorage as fallback
const storedTokens = this.loadTokensFromStorage();
if (storedTokens) {
this.tokens = storedTokens; // Update current tokens
const expiresAt = Date.parse(storedTokens.expiresAt);
if (expiresAt < Date.now()) {
const refreshed = await this.refreshToken();
if (!refreshed) {
this.clearAuth();
return { isAuthenticated: false };
}
return {
isAuthenticated: true,
accessToken: this.tokens.accessToken,
expiresAt: Date.parse(this.tokens.expiresAt),
};
}
return {
isAuthenticated: true,
accessToken: storedTokens.accessToken,
expiresAt: expiresAt,
};
}
return { isAuthenticated: false };
}
clearAuth() {
this.tokens = null;
storage.clearTokens();
}
saveTokensToStorage(tokens) {
storage.setTokens(tokens);
}
loadTokensFromStorage() {
return storage.getTokens();
}
validateAuth(config) {
var _a;
if (config.auth === "bearer" && !((_a = this.tokens) === null || _a === void 0 ? void 0 : _a.accessToken)) {
throw new BooklaError("Authentication required for this endpoint", "AUTH_REQUIRED");
}
}
/**
* Generates a unique key for request deduplication
* Format: METHOD:URL:BODY_HASH
*/
getRequestKey(config) {
const body = config.data ? JSON.stringify(config.data) : "";
return `${config.method}:${config.url}:${body}`;
}
/**
* Tracks request patterns and warns about potential issues in debug mode
*/
trackRequest(requestKey) {
if (!this.debug)
return;
const now = Date.now();
const history = this.requestHistory.get(requestKey) || {
count: 0,
lastTime: 0,
times: [],
};
history.count++;
history.times.push(now);
history.lastTime = now;
// Keep only last 10 seconds of history
history.times = history.times.filter((time) => now - time < 10000);
this.requestHistory.set(requestKey, history);
// Warning 1: Rapid identical requests (potential infinite loop)
const timeSinceLastRequest = history.lastTime - (history.times[history.times.length - 2] || 0);
if (timeSinceLastRequest < 100 && history.times.length > 1) {
console.warn(`[Bookla SDK] ⚠️ Rapid identical request detected (${timeSinceLastRequest}ms since last)`, `\n Request: ${requestKey.split(":")[0]} ${requestKey.split(":")[1]}`, `\n This might indicate an infinite loop in your React component.`, `\n Check useEffect dependencies or use the SDK's cancelToken feature.`);
}
// Warning 2: High frequency requests (> 10 in 1 second)
const recentRequests = history.times.filter((time) => now - time < 1000);
if (recentRequests.length > 10) {
console.warn(`[Bookla SDK] ⚠️ High request frequency detected: ${recentRequests.length} requests in 1 second`, `\n Request: ${requestKey.split(":")[0]} ${requestKey.split(":")[1]}`, `\n Consider implementing proper request cancellation or debouncing in your application.`);
}
// Warning 3: Many identical requests over time (> 50 in 10 seconds)
if (history.times.length > 50) {
console.warn(`[Bookla SDK] ⚠️ Excessive identical requests: ${history.times.length} requests in 10 seconds`, `\n Request: ${requestKey.split(":")[0]} ${requestKey.split(":")[1]}`, `\n This is likely a bug in your application code.`);
}
}
async request(config) {
this.validateAuth(config);
// Generate unique key for this request
const requestKey = this.getRequestKey(config);
// Track request patterns for development warnings
this.trackRequest(requestKey);
// Request deduplication: if identical request is in-flight, return existing promise
const pendingRequest = this.pendingRequests.get(requestKey);
if (pendingRequest) {
if (this.debug) {
console.log(`[Bookla SDK] 🔄 Deduplicating request: ${config.method} ${config.url}`, "\n Returning existing in-flight request instead of creating a duplicate.");
}
return pendingRequest;
}
// Create new request promise
const requestPromise = this.executeRequest(config);
// Store in pending requests
this.pendingRequests.set(requestKey, requestPromise);
// Clean up when done (success or failure)
requestPromise
.then(() => {
this.pendingRequests.delete(requestKey);
})
.catch(() => {
this.pendingRequests.delete(requestKey);
});
return requestPromise;
}
async executeRequest(config) {
var _a;
let attempt = 0;
while (attempt < this.retry.maxAttempts) {
try {
// Check for cancellation
if ((_a = config.cancelToken) === null || _a === void 0 ? void 0 : _a.cancelled) {
throw new BooklaError("Request cancelled", "REQUEST_CANCELLED");
}
// Apply request interceptors
const modifiedConfig = await this.interceptors.request.execute({
...config,
headers: this.getHeaders(config),
});
// Make request
if (this.debug) {
console.log(`[Bookla SDK] Making request:`, modifiedConfig);
}
const response = await fetch(`${this.baseURL}${config.url}`, {
method: config.method,
headers: modifiedConfig.headers,
body: config.data ? JSON.stringify(config.data) : undefined,
signal: AbortSignal.timeout(config.timeout || this.timeout),
});
// Handle response
if (!response.ok) {
throw new BooklaError(response.statusText, "API_ERROR", response.status);
}
const data = await response.json();
// Apply response interceptors
const modifiedResponse = await this.interceptors.response.execute(data);
if (this.debug) {
console.log(`[Bookla SDK] Response received:`, modifiedResponse);
}
return modifiedResponse;
}
catch (error) {
// Handle specific error cases
if (error instanceof BooklaError) {
if (error.code === "REQUEST_CANCELLED") {
throw error;
}
if (error.status === 401) {
const refreshed = await this.refreshToken();
if (!refreshed) {
throw new BooklaError("Token refresh failed", "TOKEN_REFRESH_FAILED");
}
// Retry with new token
attempt++;
continue;
}
if (!this.retry.statusCodesToRetry.includes(error.status || 0)) {
throw error;
}
}
// Retry logic
attempt++;
if (attempt === this.retry.maxAttempts) {
throw new BooklaError("Max retry attempts reached", "MAX_RETRIES_EXCEEDED");
}
await new Promise((resolve) => setTimeout(resolve, this.retry.delayMs * attempt));
}
}
throw new BooklaError("Request failed", "REQUEST_FAILED");
}
getHeaders(config) {
var _a;
const headers = {
"Content-Type": "application/json",
};
if ((config.auth === "bearer" || config.auth === "apiKeyOrBearer") &&
((_a = this.tokens) === null || _a === void 0 ? void 0 : _a.accessToken)) {
headers["Authorization"] = `Bearer ${this.tokens.accessToken}`;
}
else {
headers["X-API-Key"] = this.apiKey;
}
return headers;
}
async refreshToken() {
var _a;
try {
if (!((_a = this.tokens) === null || _a === void 0 ? void 0 : _a.refreshToken)) {
return false;
}
const response = await fetch(`${this.baseURL}${ENDPOINTS.auth.refresh.path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.tokens.refreshToken}`,
},
});
if (!response.ok) {
return false;
}
const newTokens = await response.json();
this.setTokens(newTokens);
return true;
}
catch (error) {
return false;
}
}
async get(endpoint, options) {
return this.request({
method: "GET",
url: endpoint.path,
auth: endpoint.auth,
...options,
});
}
async post(endpoint, data, options) {
return this.request({
method: "POST",
url: endpoint.path,
data,
auth: endpoint.auth,
...options,
});
}
async delete(endpoint, options) {
return this.request({
method: "DELETE",
url: endpoint.path,
auth: endpoint.auth,
...options,
});
}
createCancelToken() {
return new CancelToken();
}
isCancelledError(error) {
return error instanceof BooklaError && error.code === "REQUEST_CANCELLED";
}
}
class BookingsService {
constructor(client) {
this.client = client;
}
async list(options) {
return this.client.get(ENDPOINTS.bookings.list, options);
}
async get(id, options) {
return this.client.get({
...ENDPOINTS.bookings.get,
path: ENDPOINTS.bookings.get.path.replace("{id}", id),
}, options);
}
async request(data, options) {
this.validateCreateBookingRequest(data);
return this.client.post(ENDPOINTS.bookings.create, data, options);
}
async cancel(bookingId, reason, options) {
return this.client.post({
...ENDPOINTS.bookings.cancel,
path: ENDPOINTS.bookings.cancel.path.replace("{Id}", bookingId),
}, { reason }, options);
}
validateCreateBookingRequest(data) {
const schema = zod.z.object({
companyID: zod.z.string().min(1),
serviceID: zod.z.string().min(1),
resourceID: zod.z.string().optional(),
startTime: zod.z.string().datetime(),
spots: zod.z.number().optional(),
duration: zod.z.string().optional(),
metaData: zod.z.record(zod.z.unknown()).optional(),
pluginData: zod.z.record(zod.z.unknown()).optional(),
tickets: zod.z.record(zod.z.number()).optional(),
customPurchaseDescription: zod.z.string().optional(),
client: zod.z
.object({
id: zod.z.string().optional(),
booklaID: zod.z.string().optional(),
firstName: zod.z.string().optional(),
lastName: zod.z.string().optional(),
email: zod.z.string().email().optional(),
})
.nullish(),
});
return schema.parse(data);
}
}
class ServicesService {
constructor(client) {
this.client = client;
}
async list(companyId, options) {
return this.client.get({
...ENDPOINTS.services.list,
path: ENDPOINTS.services.list.path.replace("{companyId}", companyId),
}, options);
}
async get(companyId, serviceId, options) {
return this.client.get({
...ENDPOINTS.services.list,
path: ENDPOINTS.services.get.path
.replace("{companyId}", companyId)
.replace("{id}", serviceId),
}, options);
}
async getTimes(companyId, serviceId, data, options) {
this.validateGetTimesRequest(data);
return this.client.post({
...ENDPOINTS.services.getTimes,
path: ENDPOINTS.services.getTimes.path
.replace("{companyId}", companyId)
.replace("{id}", serviceId),
}, data, options);
}
async getDates(companyId, serviceId, data, options) {
this.validateGetDatesRequest(data);
return this.client.post({
...ENDPOINTS.services.getDates,
path: ENDPOINTS.services.getDates.path
.replace("{companyId}", companyId)
.replace("{id}", serviceId),
}, data, options);
}
validateGetTimesRequest(data) {
const schema = zod.z.object({
from: zod.z.string().datetime(),
to: zod.z.string().datetime(),
duration: zod.z.string().optional(),
spots: zod.z.number().optional(),
resourceIDs: zod.z.array(zod.z.string()).optional(),
tickets: zod.z.record(zod.z.number()).optional(),
});
return schema.parse(data);
}
validateGetDatesRequest(data) {
const schema = zod.z.object({
from: zod.z.string().datetime(),
to: zod.z.string().datetime(),
duration: zod.z.string().optional(),
spots: zod.z.number().optional(),
resourceIDs: zod.z.array(zod.z.string()).optional(),
tickets: zod.z.record(zod.z.number()).optional(),
});
return schema.parse(data);
}
}
class ResourcesService {
constructor(client) {
this.client = client;
}
async list(companyId, options) {
return this.client.get({
...ENDPOINTS.resources.list,
path: ENDPOINTS.resources.list.path.replace("{companyId}", companyId),
}, options);
}
async get(companyId, resourceId, options) {
return this.client.get({
...ENDPOINTS.resources.get,
path: ENDPOINTS.resources.get.path
.replace("{companyId}", companyId)
.replace("{id}", resourceId),
}, options);
}
}
class CodesService {
constructor(client) {
this.client = client;
}
async validateCode(code, data, options) {
this.validateCodeRequest(data);
return this.client.post({
...ENDPOINTS.codes.validate,
path: ENDPOINTS.codes.validate.path.replace("{code}", code),
}, data, options);
}
validateCodeRequest(data) {
const schema = zod.z.object({
companyID: zod.z.string().min(1),
serviceID: zod.z.string().min(1),
resourceID: zod.z.string().min(1),
startTime: zod.z.string().datetime(),
duration: zod.z.string().optional(),
spots: zod.z.number().min(1),
tickets: zod.z.record(zod.z.number()).optional(),
});
return schema.parse(data);
}
}
class ClientSubscriptionService {
constructor(client) {
this.client = client;
}
validateAddToCartRequest(data) {
const schema = zod.z.object({
items: zod.z.array(zod.z.object({
subscriptionID: zod.z.string().min(1),
metaData: zod.z.record(zod.z.unknown()).optional(),
})),
});
return schema.parse(data);
}
validateRenewRequest(data) {
const schema = zod.z.object({
ids: zod.z.array(zod.z.string().min(1)),
});
return schema.parse(data);
}
async getCart(companyId, options) {
return this.client.get({
...ENDPOINTS.subscriptions.cart.get,
path: ENDPOINTS.subscriptions.cart.get.path.replace("{companyId}", companyId),
}, options);
}
async addToCart(companyId, data, options) {
this.validateAddToCartRequest(data);
return this.client.post({
...ENDPOINTS.subscriptions.cart.add,
path: ENDPOINTS.subscriptions.cart.add.path.replace("{companyId}", companyId),
}, data, options);
}
async removeFromCart(companyId, itemId, options) {
return this.client.delete({
...ENDPOINTS.subscriptions.cart.remove,
path: ENDPOINTS.subscriptions.cart.remove.path
.replace("{companyId}", companyId)
.replace("{itemId}", itemId),
}, options);
}
async checkout(companyId, options) {
return this.client.post({
...ENDPOINTS.subscriptions.cart.checkout,
path: ENDPOINTS.subscriptions.cart.checkout.path.replace("{companyId}", companyId),
}, undefined, options);
}
async getPurchases(companyId, options) {
return this.client.get({
...ENDPOINTS.subscriptions.purchases.list,
path: ENDPOINTS.subscriptions.purchases.list.path.replace("{companyId}", companyId),
}, options);
}
async getPurchase(companyId, itemId, options) {
return this.client.get({
...ENDPOINTS.subscriptions.purchases.get,
path: ENDPOINTS.subscriptions.purchases.get.path
.replace("{companyId}", companyId)
.replace("{itemId}", itemId),
}, options);
}
async renewPurchases(companyId, data, options) {
this.validateRenewRequest(data);
return this.client.post({
...ENDPOINTS.subscriptions.purchases.renew,
path: ENDPOINTS.subscriptions.purchases.renew.path.replace("{companyId}", companyId),
}, data, options);
}
async getAvailableSubscriptions(companyId, ids, options) {
const queryParams = ids ? `?ids=${ids.join(",")}` : "";
return this.client.get({
...ENDPOINTS.subscriptions.available,
path: `${ENDPOINTS.subscriptions.available.path.replace("{companyId}", companyId)}${queryParams}`,
}, options);
}
async createPurchase(companyId, data, options) {
const schema = zod.z.object({
items: zod.z.array(zod.z.object({
subscriptionID: zod.z.string().min(1),
metaData: zod.z.record(zod.z.unknown()).optional(),
})),
client: zod.z
.object({
id: zod.z.string().optional(),
booklaID: zod.z.string().optional(),
firstName: zod.z.string().optional(),
lastName: zod.z.string().optional(),
email: zod.z.string().email().optional(),
})
.nullish(),
});
const validatedData = schema.parse(data);
return this.client.post({
...ENDPOINTS.subscriptions.purchases.create,
path: ENDPOINTS.subscriptions.purchases.create.path.replace("{companyId}", companyId),
}, validatedData, options);
}
}
class ClientGiftCardsService {
constructor(client) {
this.client = client;
}
async getPurchases(companyId, options) {
return this.client.get({
...ENDPOINTS.giftCards.purchases.list,
path: ENDPOINTS.giftCards.purchases.list.path.replace("{companyId}", companyId),
}, options);
}
async getPurchase(companyId, itemId, options) {
return this.client.get({
...ENDPOINTS.giftCards.purchases.get,
path: ENDPOINTS.giftCards.purchases.get.path
.replace("{companyId}", companyId)
.replace("{itemId}", itemId),
}, options);
}
async getAvailableGiftCards(companyId, ids, options) {
const queryParams = ids ? `?ids=${ids.join(",")}` : "";
return this.client.get({
...ENDPOINTS.giftCards.available,
path: `${ENDPOINTS.giftCards.available.path.replace("{companyId}", companyId)}${queryParams}`,
}, options);
}
async createPurchase(companyId, data, options) {
const schema = zod.z.object({
giftCardID: zod.z.string().min(1),
metaData: zod.z.record(zod.z.unknown()).optional(),
client: zod.z
.object({
id: zod.z.string().optional(),
booklaID: zod.z.string().optional(),
firstName: zod.z.string().optional(),
lastName: zod.z.string().optional(),
email: zod.z.string().email().optional(),
})
.nullish(),
});
const validatedData = schema.parse(data);
return this.client.post({
...ENDPOINTS.giftCards.purchases.create,
path: ENDPOINTS.giftCards.purchases.create.path.replace("{companyId}", companyId),
}, validatedData, options);
}
}
class ClientAddonsService {
constructor(client) {
this.client = client;
}
validateAddonsRequest(data) {
const schema = zod.z.object({
addons: zod.z.array(zod.z.object({
addonID: zod.z.string().min(1),
quantity: zod.z.number().min(1),
metadata: zod.z.record(zod.z.unknown()).optional(),
})),
});
return schema.parse(data);
}
async discoverAddons(companyId, serviceId, options) {
return this.client.get({
...ENDPOINTS.addons.discover,
path: ENDPOINTS.addons.discover.path
.replace("{companyId}", companyId)
.replace("{serviceId}", serviceId),
}, options);
}
async validateAddons(companyId, serviceId, data, options) {
this.validateAddonsRequest(data);
return this.client.post({
...ENDPOINTS.addons.validate,
path: ENDPOINTS.addons.validate.path
.replace("{companyId}", companyId)
.replace("{serviceId}", serviceId),
}, data, options);
}
}
class BooklaSDK {
constructor(config) {
this.client = new HttpClient(config);
this.bookings = new BookingsService(this.client);
this.services = new ServicesService(this.client);
this.resources = new ResourcesService(this.client);
this.codes = new CodesService(this.client);
this.subscriptions = new ClientSubscriptionService(this.client);
this.giftCards = new ClientGiftCardsService(this.client);
this.addons = new ClientAddonsService(this.client);
}
setAuthTokens(tokens) {
this.client.setTokens(tokens);
}
async isAuthenticated() {
return this.client.isAuthenticated();
}
clearAuth() {
return this.client.clearAuth();
}
get interceptors() {
return this.client.interceptors;
}
createCancelToken() {
return this.client.createCancelToken();
}
isCancelledError(error) {
return this.client.isCancelledError(error);
}
}
exports.BooklaError = BooklaError;
exports.BooklaSDK = BooklaSDK;
exports.BooklaValidationError = BooklaValidationError;
exports.CancelToken = CancelToken;
//# sourceMappingURL=index.js.map