@atzentis/booking-sdk 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1399 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2568 -159
- package/dist/index.d.ts +2568 -159
- package/dist/index.js +1386 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -35,35 +35,48 @@ __export(index_exports, {
|
|
|
35
35
|
BookingError: () => BookingError,
|
|
36
36
|
BookingsService: () => BookingsService,
|
|
37
37
|
CategoriesService: () => CategoriesService,
|
|
38
|
+
ConciergeService: () => ConciergeService,
|
|
38
39
|
ConflictError: () => ConflictError,
|
|
39
40
|
DEFAULT_MODULES: () => DEFAULT_MODULES,
|
|
40
41
|
DiscountsService: () => DiscountsService,
|
|
41
42
|
DistributionService: () => DistributionService,
|
|
42
43
|
FinanceService: () => FinanceService,
|
|
44
|
+
FiscalService: () => FiscalService,
|
|
43
45
|
ForbiddenError: () => ForbiddenError,
|
|
44
46
|
GroupsService: () => GroupsService,
|
|
45
47
|
GuestsService: () => GuestsService,
|
|
46
48
|
HousekeepingService: () => HousekeepingService,
|
|
47
49
|
HttpClient: () => HttpClient,
|
|
50
|
+
IdScanningService: () => IdScanningService,
|
|
51
|
+
IntelligenceService: () => IntelligenceService,
|
|
48
52
|
NightAuditService: () => NightAuditService,
|
|
49
53
|
NotFoundError: () => NotFoundError,
|
|
54
|
+
NotificationsService: () => NotificationsService,
|
|
50
55
|
PROPERTY_MODULES: () => PROPERTY_MODULES,
|
|
51
56
|
PaymentError: () => PaymentError,
|
|
52
57
|
PaymentsService: () => PaymentsService,
|
|
58
|
+
PortfoliosService: () => PortfoliosService,
|
|
53
59
|
PropertiesService: () => PropertiesService,
|
|
54
60
|
RateLimitError: () => RateLimitError,
|
|
55
61
|
RatePlansService: () => RatePlansService,
|
|
62
|
+
RevenueService: () => RevenueService,
|
|
56
63
|
ReviewsService: () => ReviewsService,
|
|
57
64
|
SPACE_STATUSES: () => SPACE_STATUSES,
|
|
58
65
|
SPACE_TYPES: () => SPACE_TYPES,
|
|
66
|
+
SSEClient: () => SSEClient,
|
|
59
67
|
ServerError: () => ServerError,
|
|
68
|
+
ServicesHelper: () => ServicesHelper,
|
|
69
|
+
SettingsService: () => SettingsService,
|
|
60
70
|
SpacesService: () => SpacesService,
|
|
61
71
|
StaffService: () => StaffService,
|
|
72
|
+
StaysHelper: () => StaysHelper,
|
|
62
73
|
SupplyService: () => SupplyService,
|
|
74
|
+
TablesHelper: () => TablesHelper,
|
|
63
75
|
TasksService: () => TasksService,
|
|
64
76
|
TimeoutError: () => TimeoutError,
|
|
65
77
|
VERSION: () => VERSION,
|
|
66
78
|
ValidationError: () => ValidationError,
|
|
79
|
+
WebhooksService: () => WebhooksService,
|
|
67
80
|
bookingClientConfigSchema: () => bookingClientConfigSchema,
|
|
68
81
|
createErrorFromResponse: () => createErrorFromResponse,
|
|
69
82
|
firstPage: () => firstPage,
|
|
@@ -1104,6 +1117,104 @@ var CategoriesService = class extends BaseService {
|
|
|
1104
1117
|
}
|
|
1105
1118
|
};
|
|
1106
1119
|
|
|
1120
|
+
// src/services/concierge.ts
|
|
1121
|
+
var ConciergeService = class extends BaseService {
|
|
1122
|
+
constructor() {
|
|
1123
|
+
super(...arguments);
|
|
1124
|
+
this.basePath = "/concierge/v1";
|
|
1125
|
+
}
|
|
1126
|
+
// ---------------------------------------------------------------------------
|
|
1127
|
+
// Conversations
|
|
1128
|
+
// ---------------------------------------------------------------------------
|
|
1129
|
+
/** Create a new guest conversation */
|
|
1130
|
+
createConversation(input) {
|
|
1131
|
+
return this._post(this._buildPath("conversations"), input);
|
|
1132
|
+
}
|
|
1133
|
+
/** Get a conversation by ID */
|
|
1134
|
+
getConversation(conversationId) {
|
|
1135
|
+
return this._get(this._buildPath("conversations", conversationId));
|
|
1136
|
+
}
|
|
1137
|
+
/** List conversations with optional filters */
|
|
1138
|
+
listConversations(params) {
|
|
1139
|
+
const query = {};
|
|
1140
|
+
query.propertyId = params.propertyId;
|
|
1141
|
+
if (params.guestId !== void 0) query.guestId = params.guestId;
|
|
1142
|
+
if (params.status !== void 0) query.status = params.status;
|
|
1143
|
+
if (params.channel !== void 0) query.channel = params.channel;
|
|
1144
|
+
if (params.from !== void 0) query.from = params.from;
|
|
1145
|
+
if (params.to !== void 0) query.to = params.to;
|
|
1146
|
+
if (params.sortBy !== void 0) query.sortBy = params.sortBy;
|
|
1147
|
+
if (params.sortOrder !== void 0) query.sortOrder = params.sortOrder;
|
|
1148
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
1149
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
1150
|
+
return this._get(this._buildPath("conversations"), query);
|
|
1151
|
+
}
|
|
1152
|
+
// ---------------------------------------------------------------------------
|
|
1153
|
+
// Messages
|
|
1154
|
+
// ---------------------------------------------------------------------------
|
|
1155
|
+
/** Send a message in a conversation */
|
|
1156
|
+
sendMessage(conversationId, input) {
|
|
1157
|
+
return this._post(
|
|
1158
|
+
this._buildPath("conversations", conversationId, "messages"),
|
|
1159
|
+
input
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
// ---------------------------------------------------------------------------
|
|
1163
|
+
// Context & Escalation
|
|
1164
|
+
// ---------------------------------------------------------------------------
|
|
1165
|
+
/** Get the full context for a conversation (property, guest, booking, FAQ) */
|
|
1166
|
+
getContext(conversationId) {
|
|
1167
|
+
return this._get(
|
|
1168
|
+
this._buildPath("conversations", conversationId, "context")
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
/** Escalate a conversation to staff */
|
|
1172
|
+
escalate(conversationId, input) {
|
|
1173
|
+
return this._post(
|
|
1174
|
+
this._buildPath("conversations", conversationId, "escalate"),
|
|
1175
|
+
input ?? {}
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
/** Resolve a conversation */
|
|
1179
|
+
resolve(conversationId, input) {
|
|
1180
|
+
return this._post(
|
|
1181
|
+
this._buildPath("conversations", conversationId, "resolve"),
|
|
1182
|
+
input ?? {}
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
// ---------------------------------------------------------------------------
|
|
1186
|
+
// Templates
|
|
1187
|
+
// ---------------------------------------------------------------------------
|
|
1188
|
+
/** Create a response template */
|
|
1189
|
+
createTemplate(input) {
|
|
1190
|
+
return this._post(this._buildPath("templates"), input);
|
|
1191
|
+
}
|
|
1192
|
+
/** Get a template by ID */
|
|
1193
|
+
getTemplate(templateId) {
|
|
1194
|
+
return this._get(this._buildPath("templates", templateId));
|
|
1195
|
+
}
|
|
1196
|
+
/** List templates with optional filters */
|
|
1197
|
+
listTemplates(params) {
|
|
1198
|
+
const query = {};
|
|
1199
|
+
query.propertyId = params.propertyId;
|
|
1200
|
+
if (params.category !== void 0) query.category = params.category;
|
|
1201
|
+
if (params.language !== void 0) query.language = params.language;
|
|
1202
|
+
if (params.sortBy !== void 0) query.sortBy = params.sortBy;
|
|
1203
|
+
if (params.sortOrder !== void 0) query.sortOrder = params.sortOrder;
|
|
1204
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
1205
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
1206
|
+
return this._get(this._buildPath("templates"), query);
|
|
1207
|
+
}
|
|
1208
|
+
/** Update a template */
|
|
1209
|
+
updateTemplate(templateId, input) {
|
|
1210
|
+
return this._patch(this._buildPath("templates", templateId), input);
|
|
1211
|
+
}
|
|
1212
|
+
/** Delete a template */
|
|
1213
|
+
deleteTemplate(templateId) {
|
|
1214
|
+
return this._delete(this._buildPath("templates", templateId));
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1107
1218
|
// src/services/discounts.ts
|
|
1108
1219
|
var DiscountsService = class extends BaseService {
|
|
1109
1220
|
constructor() {
|
|
@@ -1429,6 +1540,77 @@ var FinanceService = class extends BaseService {
|
|
|
1429
1540
|
}
|
|
1430
1541
|
};
|
|
1431
1542
|
|
|
1543
|
+
// src/services/fiscal.ts
|
|
1544
|
+
var FiscalService = class extends BaseService {
|
|
1545
|
+
constructor() {
|
|
1546
|
+
super(...arguments);
|
|
1547
|
+
this.basePath = "/fiscal/v1";
|
|
1548
|
+
}
|
|
1549
|
+
// ---------------------------------------------------------------------------
|
|
1550
|
+
// myDATA (Greek fiscal compliance)
|
|
1551
|
+
// ---------------------------------------------------------------------------
|
|
1552
|
+
/** Get the current myDATA integration status */
|
|
1553
|
+
getMyDataStatus() {
|
|
1554
|
+
return this._get(this._buildPath("mydata", "status"));
|
|
1555
|
+
}
|
|
1556
|
+
/** Submit an invoice to the Greek AADE myDATA platform */
|
|
1557
|
+
submitMyData(input) {
|
|
1558
|
+
return this._post(this._buildPath("mydata", "submit"), input);
|
|
1559
|
+
}
|
|
1560
|
+
/** List myDATA submissions with optional filters */
|
|
1561
|
+
listMyDataSubmissions(params) {
|
|
1562
|
+
if (!params) {
|
|
1563
|
+
return this._get(this._buildPath("mydata", "submissions"));
|
|
1564
|
+
}
|
|
1565
|
+
const query = {};
|
|
1566
|
+
if (params.status !== void 0) query.status = params.status;
|
|
1567
|
+
if (params.from !== void 0) query.from = params.from;
|
|
1568
|
+
if (params.to !== void 0) query.to = params.to;
|
|
1569
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
1570
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
1571
|
+
return this._get(this._buildPath("mydata", "submissions"), query);
|
|
1572
|
+
}
|
|
1573
|
+
/** Get a single myDATA submission by ID */
|
|
1574
|
+
getMyDataSubmission(submissionId) {
|
|
1575
|
+
return this._get(this._buildPath("mydata", "submissions", submissionId));
|
|
1576
|
+
}
|
|
1577
|
+
/** Generate a QR code for a myDATA transaction */
|
|
1578
|
+
getMyDataQr(transactionId) {
|
|
1579
|
+
return this._get(this._buildPath("mydata", "qr", transactionId));
|
|
1580
|
+
}
|
|
1581
|
+
// ---------------------------------------------------------------------------
|
|
1582
|
+
// TSE (German fiscal compliance)
|
|
1583
|
+
// ---------------------------------------------------------------------------
|
|
1584
|
+
/** Get the current TSE device status */
|
|
1585
|
+
getTseStatus() {
|
|
1586
|
+
return this._get(this._buildPath("tse", "status"));
|
|
1587
|
+
}
|
|
1588
|
+
/** List TSE transactions with optional filters */
|
|
1589
|
+
listTseTransactions(params) {
|
|
1590
|
+
if (!params) {
|
|
1591
|
+
return this._get(this._buildPath("tse", "transactions"));
|
|
1592
|
+
}
|
|
1593
|
+
const query = {};
|
|
1594
|
+
if (params.from !== void 0) query.from = params.from;
|
|
1595
|
+
if (params.to !== void 0) query.to = params.to;
|
|
1596
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
1597
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
1598
|
+
return this._get(this._buildPath("tse", "transactions"), query);
|
|
1599
|
+
}
|
|
1600
|
+
/** Trigger a TSE day closure */
|
|
1601
|
+
tseDayClosure(input) {
|
|
1602
|
+
return this._post(this._buildPath("tse", "day-closure"), input ?? {});
|
|
1603
|
+
}
|
|
1604
|
+
/** Export DSFinV-K data for a date range */
|
|
1605
|
+
tseExportDsfinvk(params) {
|
|
1606
|
+
const query = {};
|
|
1607
|
+
query.from = params.from;
|
|
1608
|
+
query.to = params.to;
|
|
1609
|
+
if (params.format !== void 0) query.format = params.format;
|
|
1610
|
+
return this._get(this._buildPath("tse", "export", "dsfinvk"), query);
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
|
|
1432
1614
|
// src/services/groups.ts
|
|
1433
1615
|
var GroupsService = class extends BaseService {
|
|
1434
1616
|
constructor() {
|
|
@@ -1825,6 +2007,77 @@ var HousekeepingService = class extends BaseService {
|
|
|
1825
2007
|
}
|
|
1826
2008
|
};
|
|
1827
2009
|
|
|
2010
|
+
// src/services/id-scanning.ts
|
|
2011
|
+
var IdScanningService = class extends BaseService {
|
|
2012
|
+
constructor() {
|
|
2013
|
+
super(...arguments);
|
|
2014
|
+
this.basePath = "/guest/v1/id-scans";
|
|
2015
|
+
}
|
|
2016
|
+
/** Upload a document image for OCR processing */
|
|
2017
|
+
upload(input) {
|
|
2018
|
+
const formData = new FormData();
|
|
2019
|
+
formData.append("guestId", input.guestId);
|
|
2020
|
+
formData.append(
|
|
2021
|
+
"file",
|
|
2022
|
+
input.file instanceof ArrayBuffer ? new Blob([input.file]) : input.file
|
|
2023
|
+
);
|
|
2024
|
+
formData.append("type", input.type);
|
|
2025
|
+
return this._post(this.basePath, formData);
|
|
2026
|
+
}
|
|
2027
|
+
/** Get the OCR result for a scan by ID */
|
|
2028
|
+
getResult(scanId) {
|
|
2029
|
+
return this._get(this._buildPath(scanId));
|
|
2030
|
+
}
|
|
2031
|
+
/** List ID scans with optional filters */
|
|
2032
|
+
list(params) {
|
|
2033
|
+
const query = {};
|
|
2034
|
+
query.guestId = params.guestId;
|
|
2035
|
+
if (params.status !== void 0) query.status = params.status;
|
|
2036
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2037
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2038
|
+
return this._get(this.basePath, query);
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
|
|
2042
|
+
// src/services/intelligence.ts
|
|
2043
|
+
var IntelligenceService = class extends BaseService {
|
|
2044
|
+
constructor() {
|
|
2045
|
+
super(...arguments);
|
|
2046
|
+
this.basePath = "/guest/v1";
|
|
2047
|
+
}
|
|
2048
|
+
// ---------------------------------------------------------------------------
|
|
2049
|
+
// Profile
|
|
2050
|
+
// ---------------------------------------------------------------------------
|
|
2051
|
+
/** Get the intelligence profile for a guest */
|
|
2052
|
+
getProfile(guestId) {
|
|
2053
|
+
return this._get(this._buildPath("profiles", guestId, "intelligence"));
|
|
2054
|
+
}
|
|
2055
|
+
/** Trigger a refresh of the intelligence profile for a guest */
|
|
2056
|
+
refresh(guestId) {
|
|
2057
|
+
return this._post(
|
|
2058
|
+
this._buildPath("profiles", guestId, "intelligence", "refresh"),
|
|
2059
|
+
{}
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
// ---------------------------------------------------------------------------
|
|
2063
|
+
// Events
|
|
2064
|
+
// ---------------------------------------------------------------------------
|
|
2065
|
+
/** Ingest a guest intelligence event for processing */
|
|
2066
|
+
ingestEvent(input) {
|
|
2067
|
+
return this._post(this._buildPath("intelligence", "events"), input);
|
|
2068
|
+
}
|
|
2069
|
+
/** List intelligence events for a guest with optional filters */
|
|
2070
|
+
listEvents(params) {
|
|
2071
|
+
const query = {};
|
|
2072
|
+
query.guestId = params.guestId;
|
|
2073
|
+
if (params.status !== void 0) query.status = params.status;
|
|
2074
|
+
if (params.type !== void 0) query.type = params.type;
|
|
2075
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2076
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2077
|
+
return this._get(this._buildPath("intelligence", "events"), query);
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
|
|
1828
2081
|
// src/services/night-audit.ts
|
|
1829
2082
|
var NightAuditService = class extends BaseService {
|
|
1830
2083
|
constructor() {
|
|
@@ -1859,6 +2112,26 @@ var NightAuditService = class extends BaseService {
|
|
|
1859
2112
|
}
|
|
1860
2113
|
};
|
|
1861
2114
|
|
|
2115
|
+
// src/services/notifications.ts
|
|
2116
|
+
var NotificationsService = class extends BaseService {
|
|
2117
|
+
constructor() {
|
|
2118
|
+
super(...arguments);
|
|
2119
|
+
this.basePath = "/notifications/v1";
|
|
2120
|
+
}
|
|
2121
|
+
/** Get notification preferences for a guest */
|
|
2122
|
+
getPreferences(guestId) {
|
|
2123
|
+
return this._get(this._buildPath("preferences", guestId));
|
|
2124
|
+
}
|
|
2125
|
+
/** Update notification preferences for a guest */
|
|
2126
|
+
updatePreferences(guestId, input) {
|
|
2127
|
+
return this._patch(this._buildPath("preferences", guestId), input);
|
|
2128
|
+
}
|
|
2129
|
+
/** Opt a guest out of notifications (all channels or specific ones) */
|
|
2130
|
+
optOut(input) {
|
|
2131
|
+
return this._post(this._buildPath("opt-out"), input);
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
|
|
1862
2135
|
// src/services/payments.ts
|
|
1863
2136
|
var PaymentsService = class extends BaseService {
|
|
1864
2137
|
constructor() {
|
|
@@ -1954,6 +2227,77 @@ var PaymentsService = class extends BaseService {
|
|
|
1954
2227
|
}
|
|
1955
2228
|
};
|
|
1956
2229
|
|
|
2230
|
+
// src/services/portfolios.ts
|
|
2231
|
+
var PortfoliosService = class extends BaseService {
|
|
2232
|
+
constructor() {
|
|
2233
|
+
super(...arguments);
|
|
2234
|
+
this.basePath = "/operations/v1/portfolios";
|
|
2235
|
+
}
|
|
2236
|
+
// ---------------------------------------------------------------------------
|
|
2237
|
+
// CRUD
|
|
2238
|
+
// ---------------------------------------------------------------------------
|
|
2239
|
+
/** Create a new portfolio */
|
|
2240
|
+
create(input) {
|
|
2241
|
+
return this._post(this.basePath, input);
|
|
2242
|
+
}
|
|
2243
|
+
/** Get a portfolio by ID including its properties */
|
|
2244
|
+
get(portfolioId) {
|
|
2245
|
+
return this._get(this._buildPath(portfolioId));
|
|
2246
|
+
}
|
|
2247
|
+
/** List portfolios with optional filters */
|
|
2248
|
+
list(params) {
|
|
2249
|
+
if (!params) return this._get(this.basePath);
|
|
2250
|
+
const query = {};
|
|
2251
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2252
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2253
|
+
if (params.search !== void 0) query.search = params.search;
|
|
2254
|
+
return this._get(this.basePath, query);
|
|
2255
|
+
}
|
|
2256
|
+
/** Update a portfolio */
|
|
2257
|
+
update(portfolioId, input) {
|
|
2258
|
+
return this._patch(this._buildPath(portfolioId), input);
|
|
2259
|
+
}
|
|
2260
|
+
/** Delete a portfolio */
|
|
2261
|
+
delete(portfolioId) {
|
|
2262
|
+
return this._delete(this._buildPath(portfolioId));
|
|
2263
|
+
}
|
|
2264
|
+
// ---------------------------------------------------------------------------
|
|
2265
|
+
// Membership
|
|
2266
|
+
// ---------------------------------------------------------------------------
|
|
2267
|
+
/** Add a property to a portfolio */
|
|
2268
|
+
addProperty(portfolioId, input) {
|
|
2269
|
+
return this._post(this._buildPath(portfolioId, "properties"), input);
|
|
2270
|
+
}
|
|
2271
|
+
/** Remove a property from a portfolio */
|
|
2272
|
+
removeProperty(portfolioId, propertyId) {
|
|
2273
|
+
return this._delete(this._buildPath(portfolioId, "properties", propertyId));
|
|
2274
|
+
}
|
|
2275
|
+
// ---------------------------------------------------------------------------
|
|
2276
|
+
// Dashboard
|
|
2277
|
+
// ---------------------------------------------------------------------------
|
|
2278
|
+
/** Get aggregated dashboard data for a portfolio */
|
|
2279
|
+
getDashboard(portfolioId, params) {
|
|
2280
|
+
const path = this._buildPath(portfolioId, "dashboard");
|
|
2281
|
+
if (!params) return this._get(path);
|
|
2282
|
+
const query = {};
|
|
2283
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2284
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2285
|
+
return this._get(path, query);
|
|
2286
|
+
}
|
|
2287
|
+
// ---------------------------------------------------------------------------
|
|
2288
|
+
// Search
|
|
2289
|
+
// ---------------------------------------------------------------------------
|
|
2290
|
+
/** Search for guests, bookings, or all records within a portfolio */
|
|
2291
|
+
search(portfolioId, params) {
|
|
2292
|
+
const query = {};
|
|
2293
|
+
query.query = params.query;
|
|
2294
|
+
if (params.type !== void 0) query.type = params.type;
|
|
2295
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2296
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2297
|
+
return this._get(this._buildPath(portfolioId, "search"), query);
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
|
|
1957
2301
|
// src/services/properties.ts
|
|
1958
2302
|
var PropertiesService = class extends BaseService {
|
|
1959
2303
|
constructor() {
|
|
@@ -2121,6 +2465,152 @@ var RatePlansService = class extends BaseService {
|
|
|
2121
2465
|
}
|
|
2122
2466
|
};
|
|
2123
2467
|
|
|
2468
|
+
// src/services/revenue.ts
|
|
2469
|
+
var RevenueService = class extends BaseService {
|
|
2470
|
+
constructor() {
|
|
2471
|
+
super(...arguments);
|
|
2472
|
+
this.basePath = "/revenue/v1";
|
|
2473
|
+
}
|
|
2474
|
+
// ---------------------------------------------------------------------------
|
|
2475
|
+
// Dashboard
|
|
2476
|
+
// ---------------------------------------------------------------------------
|
|
2477
|
+
/** Get the revenue dashboard with KPIs, occupancy, and revenue breakdown */
|
|
2478
|
+
getDashboard(params) {
|
|
2479
|
+
const query = {};
|
|
2480
|
+
query.propertyId = params.propertyId;
|
|
2481
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2482
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2483
|
+
if (params.compareWith !== void 0) query.compareWith = params.compareWith;
|
|
2484
|
+
return this._get(this._buildPath("dashboard"), query);
|
|
2485
|
+
}
|
|
2486
|
+
/** Get individual KPI metrics with optional granularity breakdown */
|
|
2487
|
+
getKPIs(params) {
|
|
2488
|
+
const query = {};
|
|
2489
|
+
query.propertyId = params.propertyId;
|
|
2490
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2491
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2492
|
+
if (params.metrics !== void 0) query.metrics = params.metrics.join(",");
|
|
2493
|
+
if (params.granularity !== void 0) query.granularity = params.granularity;
|
|
2494
|
+
return this._get(this._buildPath("kpis"), query);
|
|
2495
|
+
}
|
|
2496
|
+
// ---------------------------------------------------------------------------
|
|
2497
|
+
// Rate suggestions
|
|
2498
|
+
// ---------------------------------------------------------------------------
|
|
2499
|
+
/** List AI-generated rate suggestions with optional filters */
|
|
2500
|
+
listSuggestions(params) {
|
|
2501
|
+
const query = {};
|
|
2502
|
+
query.propertyId = params.propertyId;
|
|
2503
|
+
if (params.status !== void 0) query.status = params.status;
|
|
2504
|
+
if (params.ratePlanId !== void 0) query.ratePlanId = params.ratePlanId;
|
|
2505
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2506
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2507
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2508
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2509
|
+
return this._get(this._buildPath("suggestions"), query);
|
|
2510
|
+
}
|
|
2511
|
+
/** Apply a rate suggestion, updating the associated rate plan */
|
|
2512
|
+
applySuggestion(suggestionId) {
|
|
2513
|
+
return this._post(this._buildPath("suggestions", suggestionId, "apply"), {});
|
|
2514
|
+
}
|
|
2515
|
+
/** Dismiss a rate suggestion with an optional reason */
|
|
2516
|
+
dismissSuggestion(suggestionId, input) {
|
|
2517
|
+
return this._post(
|
|
2518
|
+
this._buildPath("suggestions", suggestionId, "dismiss"),
|
|
2519
|
+
input ?? {}
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
2522
|
+
// ---------------------------------------------------------------------------
|
|
2523
|
+
// Competitors
|
|
2524
|
+
// ---------------------------------------------------------------------------
|
|
2525
|
+
/** List competitors tracked for a property */
|
|
2526
|
+
listCompetitors(params) {
|
|
2527
|
+
const query = {};
|
|
2528
|
+
query.propertyId = params.propertyId;
|
|
2529
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
2530
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
2531
|
+
return this._get(this._buildPath("competitors"), query);
|
|
2532
|
+
}
|
|
2533
|
+
/** Get rate data scraped for a competitor over a date range */
|
|
2534
|
+
getCompetitorRates(competitorId, params) {
|
|
2535
|
+
const query = {};
|
|
2536
|
+
if (params !== void 0) {
|
|
2537
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2538
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2539
|
+
if (params.source !== void 0) query.source = params.source;
|
|
2540
|
+
}
|
|
2541
|
+
return this._get(
|
|
2542
|
+
this._buildPath("competitors", competitorId, "rates"),
|
|
2543
|
+
query
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
/** Add a competitor to track for a property */
|
|
2547
|
+
addCompetitor(input) {
|
|
2548
|
+
return this._post(this._buildPath("competitors"), input);
|
|
2549
|
+
}
|
|
2550
|
+
// ---------------------------------------------------------------------------
|
|
2551
|
+
// Reports
|
|
2552
|
+
// ---------------------------------------------------------------------------
|
|
2553
|
+
/** Get a detailed revenue report with optional comparison period */
|
|
2554
|
+
getRevenueReport(params) {
|
|
2555
|
+
const query = {};
|
|
2556
|
+
query.propertyId = params.propertyId;
|
|
2557
|
+
query.from = params.from;
|
|
2558
|
+
query.to = params.to;
|
|
2559
|
+
if (params.groupBy !== void 0) query.groupBy = params.groupBy;
|
|
2560
|
+
if (params.compareWith !== void 0) query.compareWith = params.compareWith;
|
|
2561
|
+
return this._get(this._buildPath("reports", "revenue"), query);
|
|
2562
|
+
}
|
|
2563
|
+
/** Get a revenue forecast for a future date range */
|
|
2564
|
+
getForecast(params) {
|
|
2565
|
+
const query = {};
|
|
2566
|
+
query.propertyId = params.propertyId;
|
|
2567
|
+
query.from = params.from;
|
|
2568
|
+
query.to = params.to;
|
|
2569
|
+
if (params.model !== void 0) query.model = params.model;
|
|
2570
|
+
return this._get(this._buildPath("reports", "forecast"), query);
|
|
2571
|
+
}
|
|
2572
|
+
/** Get a pace report comparing current bookings against a prior period */
|
|
2573
|
+
getPaceReport(params) {
|
|
2574
|
+
const query = {};
|
|
2575
|
+
query.propertyId = params.propertyId;
|
|
2576
|
+
query.from = params.from;
|
|
2577
|
+
query.to = params.to;
|
|
2578
|
+
if (params.compareWith !== void 0) query.compareWith = params.compareWith;
|
|
2579
|
+
if (params.granularity !== void 0) query.granularity = params.granularity;
|
|
2580
|
+
return this._get(this._buildPath("reports", "pace"), query);
|
|
2581
|
+
}
|
|
2582
|
+
// ---------------------------------------------------------------------------
|
|
2583
|
+
// Portfolio
|
|
2584
|
+
// ---------------------------------------------------------------------------
|
|
2585
|
+
/** Get a portfolio-level dashboard with aggregated KPIs */
|
|
2586
|
+
getPortfolioDashboard(params) {
|
|
2587
|
+
const query = {};
|
|
2588
|
+
query.portfolioId = params.portfolioId;
|
|
2589
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2590
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2591
|
+
return this._get(this._buildPath("portfolio", "dashboard"), query);
|
|
2592
|
+
}
|
|
2593
|
+
/** Get a per-property breakdown of revenue metrics across a portfolio */
|
|
2594
|
+
getPortfolioBreakdown(params) {
|
|
2595
|
+
const query = {};
|
|
2596
|
+
query.portfolioId = params.portfolioId;
|
|
2597
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2598
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2599
|
+
if (params.sortBy !== void 0) query.sortBy = params.sortBy;
|
|
2600
|
+
if (params.sortOrder !== void 0) query.sortOrder = params.sortOrder;
|
|
2601
|
+
return this._get(this._buildPath("portfolio", "breakdown"), query);
|
|
2602
|
+
}
|
|
2603
|
+
/** Get a revenue forecast for all properties in a portfolio */
|
|
2604
|
+
getPortfolioForecast(params) {
|
|
2605
|
+
const query = {};
|
|
2606
|
+
query.portfolioId = params.portfolioId;
|
|
2607
|
+
if (params.from !== void 0) query.from = params.from;
|
|
2608
|
+
if (params.to !== void 0) query.to = params.to;
|
|
2609
|
+
if (params.model !== void 0) query.model = params.model;
|
|
2610
|
+
return this._get(this._buildPath("portfolio", "forecast"), query);
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
|
|
2124
2614
|
// src/services/reviews.ts
|
|
2125
2615
|
var ReviewsService = class extends BaseService {
|
|
2126
2616
|
constructor() {
|
|
@@ -2206,6 +2696,195 @@ var ReviewsService = class extends BaseService {
|
|
|
2206
2696
|
}
|
|
2207
2697
|
};
|
|
2208
2698
|
|
|
2699
|
+
// src/services/services-helper.ts
|
|
2700
|
+
var ServicesHelper = class {
|
|
2701
|
+
constructor(client) {
|
|
2702
|
+
this.client = client;
|
|
2703
|
+
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Book a service appointment in a single call.
|
|
2706
|
+
*
|
|
2707
|
+
* Orchestrates three steps:
|
|
2708
|
+
* 1. `client.availability.getServiceSlots()` — verify available slots for the date
|
|
2709
|
+
* 2. `client.guests.create()` — create the guest if inline data was provided
|
|
2710
|
+
* 3. `client.bookings.create()` — create and auto-confirm the appointment booking
|
|
2711
|
+
*
|
|
2712
|
+
* @throws {Error} if no appointment slots are available for the requested date
|
|
2713
|
+
*/
|
|
2714
|
+
async bookAppointment(input) {
|
|
2715
|
+
const slots = await this.client.availability.getServiceSlots({
|
|
2716
|
+
propertyId: input.propertyId,
|
|
2717
|
+
date: input.date,
|
|
2718
|
+
providerId: input.providerId,
|
|
2719
|
+
categoryId: input.categoryId,
|
|
2720
|
+
duration: input.duration ?? 60
|
|
2721
|
+
});
|
|
2722
|
+
if (slots.length === 0) {
|
|
2723
|
+
throw new Error("No appointment slots available");
|
|
2724
|
+
}
|
|
2725
|
+
const guestId = typeof input.guest === "string" ? input.guest : (await this.client.guests.create(input.guest)).id;
|
|
2726
|
+
const duration = input.duration ?? 60;
|
|
2727
|
+
const checkIn = toISODateTime(input.date, input.time);
|
|
2728
|
+
const checkOut = toISODateTime(input.date, addMinutes(input.time, duration));
|
|
2729
|
+
const booking = await this.client.bookings.create({
|
|
2730
|
+
propertyId: input.propertyId,
|
|
2731
|
+
categoryId: input.categoryId,
|
|
2732
|
+
guestId,
|
|
2733
|
+
checkIn,
|
|
2734
|
+
checkOut,
|
|
2735
|
+
type: "service",
|
|
2736
|
+
notes: input.notes,
|
|
2737
|
+
autoConfirm: true
|
|
2738
|
+
});
|
|
2739
|
+
return { booking, guestId };
|
|
2740
|
+
}
|
|
2741
|
+
/**
|
|
2742
|
+
* Get available service appointment slots for a specific date.
|
|
2743
|
+
*
|
|
2744
|
+
* Delegates to `client.availability.getServiceSlots()`.
|
|
2745
|
+
*/
|
|
2746
|
+
async getSlots(params) {
|
|
2747
|
+
return this.client.availability.getServiceSlots(params);
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Get the service menu for a property — all bookable service categories with
|
|
2751
|
+
* their base price and default duration.
|
|
2752
|
+
*
|
|
2753
|
+
* Calls `client.categories.list()` and maps each `SpaceCategory` to a
|
|
2754
|
+
* `ServiceMenuItem`. The `maxOccupancy` field on categories is used as the
|
|
2755
|
+
* default appointment duration for the Services vertical.
|
|
2756
|
+
*/
|
|
2757
|
+
async getMenu(propertyId) {
|
|
2758
|
+
const result = await this.client.categories.list({ propertyId });
|
|
2759
|
+
return result.data.map((cat) => ({
|
|
2760
|
+
categoryId: cat.id,
|
|
2761
|
+
name: cat.name,
|
|
2762
|
+
description: cat.description ?? null,
|
|
2763
|
+
basePrice: cat.basePrice,
|
|
2764
|
+
// For the Services vertical, maxOccupancy stores the default duration
|
|
2765
|
+
duration: cat.maxOccupancy ?? null
|
|
2766
|
+
}));
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Get a provider's availability schedule across a date range.
|
|
2770
|
+
*
|
|
2771
|
+
* Calls `client.availability.getServiceSlots()` once per day from `from` to
|
|
2772
|
+
* `to` (exclusive), capped at 31 days. Each day is returned as a
|
|
2773
|
+
* `ProviderScheduleDay` with its individual time slots.
|
|
2774
|
+
*
|
|
2775
|
+
* @param params.from - Start date in YYYY-MM-DD format (inclusive)
|
|
2776
|
+
* @param params.to - End date in YYYY-MM-DD format (exclusive, max 31 days from `from`)
|
|
2777
|
+
*/
|
|
2778
|
+
async getProviderSchedule(params) {
|
|
2779
|
+
const days = dateDiffDays2(params.from, params.to);
|
|
2780
|
+
const cappedDays = Math.min(days, 31);
|
|
2781
|
+
const schedule = [];
|
|
2782
|
+
for (let i = 0; i < cappedDays; i++) {
|
|
2783
|
+
const date = addDaysToDate(params.from, i);
|
|
2784
|
+
const slots = await this.client.availability.getServiceSlots({
|
|
2785
|
+
propertyId: params.propertyId,
|
|
2786
|
+
date,
|
|
2787
|
+
providerId: params.providerId
|
|
2788
|
+
});
|
|
2789
|
+
schedule.push({
|
|
2790
|
+
date,
|
|
2791
|
+
slots: slots.map((s) => ({
|
|
2792
|
+
startTime: s.startTime,
|
|
2793
|
+
endTime: s.endTime,
|
|
2794
|
+
available: s.available
|
|
2795
|
+
}))
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
return schedule;
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Reschedule an existing appointment to a new date and time.
|
|
2802
|
+
*
|
|
2803
|
+
* Verifies slot availability for the new datetime via
|
|
2804
|
+
* `client.availability.getServiceSlots()`, then updates the booking via
|
|
2805
|
+
* `client.bookings.update()`.
|
|
2806
|
+
*
|
|
2807
|
+
* @throws {Error} if no slots are available for the new date/time
|
|
2808
|
+
*/
|
|
2809
|
+
async reschedule(input) {
|
|
2810
|
+
const current = await this.client.bookings.get(input.bookingId);
|
|
2811
|
+
const duration = input.duration ?? 60;
|
|
2812
|
+
const slots = await this.client.availability.getServiceSlots({
|
|
2813
|
+
propertyId: current.propertyId,
|
|
2814
|
+
date: input.newDate,
|
|
2815
|
+
categoryId: current.categoryId ?? void 0,
|
|
2816
|
+
duration
|
|
2817
|
+
});
|
|
2818
|
+
if (slots.length === 0) {
|
|
2819
|
+
throw new Error("No slots available for new time");
|
|
2820
|
+
}
|
|
2821
|
+
const checkIn = toISODateTime(input.newDate, input.newTime);
|
|
2822
|
+
const checkOut = toISODateTime(input.newDate, addMinutes(input.newTime, duration));
|
|
2823
|
+
return this.client.bookings.update(input.bookingId, {
|
|
2824
|
+
checkIn,
|
|
2825
|
+
checkOut
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
function toDateString(date) {
|
|
2830
|
+
const y = date.getUTCFullYear();
|
|
2831
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
2832
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
2833
|
+
return `${y}-${m}-${d}`;
|
|
2834
|
+
}
|
|
2835
|
+
function toISODateTime(date, time) {
|
|
2836
|
+
return `${date}T${time}:00.000Z`;
|
|
2837
|
+
}
|
|
2838
|
+
function addMinutes(time, mins) {
|
|
2839
|
+
const [hourStr, minuteStr] = time.split(":");
|
|
2840
|
+
const totalMinutes = Number(hourStr) * 60 + Number(minuteStr) + mins;
|
|
2841
|
+
const h = Math.floor(totalMinutes / 60) % 24;
|
|
2842
|
+
const m = totalMinutes % 60;
|
|
2843
|
+
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
2844
|
+
}
|
|
2845
|
+
function dateDiffDays2(start, end) {
|
|
2846
|
+
const parseDateStr2 = (s) => {
|
|
2847
|
+
const parts = s.split("-");
|
|
2848
|
+
return [Number(parts[0]), Number(parts[1]), Number(parts[2])];
|
|
2849
|
+
};
|
|
2850
|
+
const [sy, sm, sd] = parseDateStr2(start);
|
|
2851
|
+
const [ey, em, ed] = parseDateStr2(end);
|
|
2852
|
+
const startMs = Date.UTC(sy, sm - 1, sd);
|
|
2853
|
+
const endMs = Date.UTC(ey, em - 1, ed);
|
|
2854
|
+
return Math.max(0, Math.floor((endMs - startMs) / 864e5));
|
|
2855
|
+
}
|
|
2856
|
+
function addDaysToDate(dateStr, days) {
|
|
2857
|
+
const parts = dateStr.split("-");
|
|
2858
|
+
const year = Number(parts[0]);
|
|
2859
|
+
const month = Number(parts[1]);
|
|
2860
|
+
const day = Number(parts[2]);
|
|
2861
|
+
return toDateString(new Date(Date.UTC(year, month - 1, day + days)));
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
// src/services/settings.ts
|
|
2865
|
+
var SettingsService = class extends BaseService {
|
|
2866
|
+
constructor() {
|
|
2867
|
+
super(...arguments);
|
|
2868
|
+
this.basePath = "/settings/v1";
|
|
2869
|
+
}
|
|
2870
|
+
/** Get property-level settings */
|
|
2871
|
+
getPropertySettings() {
|
|
2872
|
+
return this._get(this._buildPath("property"));
|
|
2873
|
+
}
|
|
2874
|
+
/** Update property-level settings */
|
|
2875
|
+
updatePropertySettings(input) {
|
|
2876
|
+
return this._patch(this._buildPath("property"), input);
|
|
2877
|
+
}
|
|
2878
|
+
/** Get fiscal compliance settings (myDATA and TSE) */
|
|
2879
|
+
getFiscalSettings() {
|
|
2880
|
+
return this._get(this._buildPath("fiscal"));
|
|
2881
|
+
}
|
|
2882
|
+
/** Update fiscal compliance settings (myDATA and TSE) */
|
|
2883
|
+
updateFiscalSettings(input) {
|
|
2884
|
+
return this._patch(this._buildPath("fiscal"), input);
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
|
|
2209
2888
|
// src/services/spaces.ts
|
|
2210
2889
|
var SpacesService = class extends BaseService {
|
|
2211
2890
|
constructor() {
|
|
@@ -2398,6 +3077,157 @@ var StaffService = class extends BaseService {
|
|
|
2398
3077
|
}
|
|
2399
3078
|
};
|
|
2400
3079
|
|
|
3080
|
+
// src/services/stays.ts
|
|
3081
|
+
var StaysHelper = class {
|
|
3082
|
+
constructor(client) {
|
|
3083
|
+
this.client = client;
|
|
3084
|
+
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Book a stay in a single call.
|
|
3087
|
+
*
|
|
3088
|
+
* Orchestrates four steps:
|
|
3089
|
+
* 1. `client.availability.check()` — verify availability for the date range
|
|
3090
|
+
* 2. `client.guests.create()` — create the guest if inline data was provided
|
|
3091
|
+
* 3. `client.bookings.create()` — create the booking
|
|
3092
|
+
* 4. `client.bookings.confirm()` — confirm if `autoConfirm` is true
|
|
3093
|
+
*
|
|
3094
|
+
* @throws {Error} if no availability exists for the requested dates
|
|
3095
|
+
*/
|
|
3096
|
+
async book(input) {
|
|
3097
|
+
const avail = await this.client.availability.check({
|
|
3098
|
+
propertyId: input.propertyId,
|
|
3099
|
+
categoryId: input.categoryId,
|
|
3100
|
+
checkIn: input.checkIn,
|
|
3101
|
+
checkOut: input.checkOut,
|
|
3102
|
+
guests: input.guests,
|
|
3103
|
+
spaceId: input.spaceId
|
|
3104
|
+
});
|
|
3105
|
+
if (!avail.available) {
|
|
3106
|
+
throw new Error("No availability for requested dates");
|
|
3107
|
+
}
|
|
3108
|
+
const guestId = typeof input.guest === "string" ? input.guest : (await this.client.guests.create(input.guest)).id;
|
|
3109
|
+
const booking = await this.client.bookings.create({
|
|
3110
|
+
propertyId: input.propertyId,
|
|
3111
|
+
categoryId: input.categoryId,
|
|
3112
|
+
guestId,
|
|
3113
|
+
checkIn: input.checkIn,
|
|
3114
|
+
checkOut: input.checkOut,
|
|
3115
|
+
type: "stay",
|
|
3116
|
+
guests: input.guests,
|
|
3117
|
+
spaceId: input.spaceId,
|
|
3118
|
+
notes: input.notes,
|
|
3119
|
+
source: input.source,
|
|
3120
|
+
rateId: input.rateId
|
|
3121
|
+
});
|
|
3122
|
+
let confirmed = false;
|
|
3123
|
+
if (input.autoConfirm) {
|
|
3124
|
+
await this.client.bookings.confirm(booking.id);
|
|
3125
|
+
confirmed = true;
|
|
3126
|
+
}
|
|
3127
|
+
return { booking, guestId, confirmed };
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* Extend the checkout date of an existing stay.
|
|
3131
|
+
*
|
|
3132
|
+
* Verifies availability for the extension period (original checkout → new
|
|
3133
|
+
* checkout) via `client.availability.check()` before updating the booking
|
|
3134
|
+
* via `client.bookings.update()`.
|
|
3135
|
+
*
|
|
3136
|
+
* @throws {Error} if no availability exists for the extension period
|
|
3137
|
+
*/
|
|
3138
|
+
async extend(input) {
|
|
3139
|
+
const current = await this.client.bookings.get(input.bookingId);
|
|
3140
|
+
const avail = await this.client.availability.check({
|
|
3141
|
+
propertyId: current.propertyId,
|
|
3142
|
+
checkIn: current.checkOut,
|
|
3143
|
+
checkOut: input.newCheckOut,
|
|
3144
|
+
categoryId: current.categoryId ?? void 0,
|
|
3145
|
+
spaceId: current.spaceId ?? void 0
|
|
3146
|
+
});
|
|
3147
|
+
if (!avail.available) {
|
|
3148
|
+
throw new Error("No availability for extension period");
|
|
3149
|
+
}
|
|
3150
|
+
return this.client.bookings.update(input.bookingId, {
|
|
3151
|
+
checkOut: input.newCheckOut
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* Check a guest out early.
|
|
3156
|
+
*
|
|
3157
|
+
* Updates the checkout date to today via `client.bookings.update()`, then
|
|
3158
|
+
* performs the check-out lifecycle transition via `client.bookings.checkOut()`.
|
|
3159
|
+
*/
|
|
3160
|
+
async earlyCheckout(input) {
|
|
3161
|
+
const today = toDateString2(/* @__PURE__ */ new Date());
|
|
3162
|
+
await this.client.bookings.update(input.bookingId, { checkOut: today });
|
|
3163
|
+
return this.client.bookings.checkOut(input.bookingId, {
|
|
3164
|
+
actualDeparture: input.actualDeparture,
|
|
3165
|
+
notes: input.notes
|
|
3166
|
+
});
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* Get all confirmed bookings arriving on a given date.
|
|
3170
|
+
*
|
|
3171
|
+
* Calls `client.bookings.list()` filtered by `status: "confirmed"`,
|
|
3172
|
+
* `type: "stay"`, and `checkInFrom/checkInTo` equal to the target date.
|
|
3173
|
+
*
|
|
3174
|
+
* @param params.date - Defaults to today if not provided
|
|
3175
|
+
*/
|
|
3176
|
+
async getArrivals(params) {
|
|
3177
|
+
const date = params.date ?? toDateString2(/* @__PURE__ */ new Date());
|
|
3178
|
+
const result = await this.client.bookings.list({
|
|
3179
|
+
propertyId: params.propertyId,
|
|
3180
|
+
status: "confirmed",
|
|
3181
|
+
type: "stay",
|
|
3182
|
+
checkInFrom: date,
|
|
3183
|
+
checkInTo: date
|
|
3184
|
+
});
|
|
3185
|
+
return result.data;
|
|
3186
|
+
}
|
|
3187
|
+
/**
|
|
3188
|
+
* Get all checked-in bookings departing on a given date.
|
|
3189
|
+
*
|
|
3190
|
+
* Calls `client.bookings.list()` filtered by `status: "checked_in"`,
|
|
3191
|
+
* `type: "stay"`, and `checkOutFrom/checkOutTo` equal to the target date.
|
|
3192
|
+
*
|
|
3193
|
+
* @param params.date - Defaults to today if not provided
|
|
3194
|
+
*/
|
|
3195
|
+
async getDepartures(params) {
|
|
3196
|
+
const date = params.date ?? toDateString2(/* @__PURE__ */ new Date());
|
|
3197
|
+
const result = await this.client.bookings.list({
|
|
3198
|
+
propertyId: params.propertyId,
|
|
3199
|
+
status: "checked_in",
|
|
3200
|
+
type: "stay",
|
|
3201
|
+
checkOutFrom: date,
|
|
3202
|
+
checkOutTo: date
|
|
3203
|
+
});
|
|
3204
|
+
return result.data;
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Get a composite summary for a booking — booking record plus its folio.
|
|
3208
|
+
*
|
|
3209
|
+
* Calls `client.bookings.get()` and `client.finance.listFolios()`. If the
|
|
3210
|
+
* finance service fails (e.g., folio not found), the folio is set to `null`
|
|
3211
|
+
* and no error is propagated.
|
|
3212
|
+
*/
|
|
3213
|
+
async getSummary(bookingId) {
|
|
3214
|
+
const booking = await this.client.bookings.get(bookingId);
|
|
3215
|
+
let folio = null;
|
|
3216
|
+
try {
|
|
3217
|
+
const folios = await this.client.finance.listFolios({ bookingId });
|
|
3218
|
+
folio = folios.data[0] ?? null;
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
return { booking, folio };
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
function toDateString2(date) {
|
|
3225
|
+
const y = date.getUTCFullYear();
|
|
3226
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
3227
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
3228
|
+
return `${y}-${m}-${d}`;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
2401
3231
|
// src/services/supply.ts
|
|
2402
3232
|
var SupplyService = class extends BaseService {
|
|
2403
3233
|
constructor() {
|
|
@@ -2488,6 +3318,169 @@ var SupplyService = class extends BaseService {
|
|
|
2488
3318
|
}
|
|
2489
3319
|
};
|
|
2490
3320
|
|
|
3321
|
+
// src/services/tables.ts
|
|
3322
|
+
var TablesHelper = class {
|
|
3323
|
+
constructor(client) {
|
|
3324
|
+
this.client = client;
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Reserve a table in a single call.
|
|
3328
|
+
*
|
|
3329
|
+
* Orchestrates three steps:
|
|
3330
|
+
* 1. `client.availability.getTableSlots()` — verify available slots for the date
|
|
3331
|
+
* 2. `client.guests.create()` — create the guest if inline data was provided
|
|
3332
|
+
* 3. `client.bookings.create()` — create and auto-confirm the booking
|
|
3333
|
+
*
|
|
3334
|
+
* @throws {Error} if no table slots are available for the requested date
|
|
3335
|
+
*/
|
|
3336
|
+
async reserve(input) {
|
|
3337
|
+
const slots = await this.client.availability.getTableSlots({
|
|
3338
|
+
propertyId: input.propertyId,
|
|
3339
|
+
date: input.date,
|
|
3340
|
+
guests: input.guests,
|
|
3341
|
+
categoryId: input.categoryId,
|
|
3342
|
+
duration: input.duration ?? 90
|
|
3343
|
+
});
|
|
3344
|
+
if (slots.length === 0) {
|
|
3345
|
+
throw new Error("No table slots available");
|
|
3346
|
+
}
|
|
3347
|
+
const guestId = typeof input.guest === "string" ? input.guest : (await this.client.guests.create(input.guest)).id;
|
|
3348
|
+
const duration = input.duration ?? 90;
|
|
3349
|
+
const checkIn = toISODateTime2(input.date, input.time);
|
|
3350
|
+
const checkOut = toISODateTime2(input.date, addMinutes2(input.time, duration));
|
|
3351
|
+
const booking = await this.client.bookings.create({
|
|
3352
|
+
propertyId: input.propertyId,
|
|
3353
|
+
categoryId: input.categoryId ?? "default",
|
|
3354
|
+
guestId,
|
|
3355
|
+
checkIn,
|
|
3356
|
+
checkOut,
|
|
3357
|
+
type: "table",
|
|
3358
|
+
guests: input.guests,
|
|
3359
|
+
spaceId: input.spaceId,
|
|
3360
|
+
notes: input.notes,
|
|
3361
|
+
autoConfirm: true
|
|
3362
|
+
});
|
|
3363
|
+
return { booking, guestId };
|
|
3364
|
+
}
|
|
3365
|
+
/**
|
|
3366
|
+
* Get available table time slots for a specific date.
|
|
3367
|
+
*
|
|
3368
|
+
* Delegates to `client.availability.getTableSlots()`.
|
|
3369
|
+
*/
|
|
3370
|
+
async getSlots(params) {
|
|
3371
|
+
return this.client.availability.getTableSlots(params);
|
|
3372
|
+
}
|
|
3373
|
+
/**
|
|
3374
|
+
* Build a floor plan showing every table and its current reservation status.
|
|
3375
|
+
*
|
|
3376
|
+
* Calls `client.spaces.list()` to retrieve all table spaces, then
|
|
3377
|
+
* `client.bookings.list()` to retrieve active bookings for the date,
|
|
3378
|
+
* and maps each space to a `FloorPlanTable` with its computed status.
|
|
3379
|
+
*
|
|
3380
|
+
* Status rules:
|
|
3381
|
+
* - `occupied` — booking with status `checked_in`
|
|
3382
|
+
* - `reserved` — booking with status `confirmed`
|
|
3383
|
+
* - `available` — no active booking found for the space
|
|
3384
|
+
*/
|
|
3385
|
+
async getFloorPlan(params) {
|
|
3386
|
+
const spacesResult = await this.client.spaces.list({
|
|
3387
|
+
propertyId: params.propertyId,
|
|
3388
|
+
type: "table"
|
|
3389
|
+
});
|
|
3390
|
+
const date = params.date ?? toDateString3(/* @__PURE__ */ new Date());
|
|
3391
|
+
const bookingsResult = await this.client.bookings.list({
|
|
3392
|
+
propertyId: params.propertyId,
|
|
3393
|
+
type: "table",
|
|
3394
|
+
status: ["confirmed", "checked_in"],
|
|
3395
|
+
checkInFrom: date,
|
|
3396
|
+
checkInTo: date
|
|
3397
|
+
});
|
|
3398
|
+
const bookingBySpace = /* @__PURE__ */ new Map();
|
|
3399
|
+
for (const bk of bookingsResult.data) {
|
|
3400
|
+
if (bk.spaceId) {
|
|
3401
|
+
bookingBySpace.set(bk.spaceId, bk.id);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
return spacesResult.data.map((space) => {
|
|
3405
|
+
const bookingId = bookingBySpace.get(space.id) ?? null;
|
|
3406
|
+
let status = "available";
|
|
3407
|
+
if (bookingId) {
|
|
3408
|
+
const bk = bookingsResult.data.find((b) => b.id === bookingId);
|
|
3409
|
+
status = bk?.status === "checked_in" ? "occupied" : "reserved";
|
|
3410
|
+
}
|
|
3411
|
+
return {
|
|
3412
|
+
spaceId: space.id,
|
|
3413
|
+
name: space.name,
|
|
3414
|
+
capacity: space.capacity ?? 0,
|
|
3415
|
+
floor: space.floor ?? null,
|
|
3416
|
+
status,
|
|
3417
|
+
bookingId
|
|
3418
|
+
};
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
/**
|
|
3422
|
+
* Get all waitlist bookings for a property, ordered FIFO (oldest first).
|
|
3423
|
+
*
|
|
3424
|
+
* Calls `client.bookings.list()` filtered by `status: "waitlist"` and
|
|
3425
|
+
* `type: "table"`, sorted by `createdAt` ascending.
|
|
3426
|
+
*/
|
|
3427
|
+
async getWaitlist(propertyId) {
|
|
3428
|
+
const result = await this.client.bookings.list({
|
|
3429
|
+
propertyId,
|
|
3430
|
+
status: "waitlist",
|
|
3431
|
+
type: "table",
|
|
3432
|
+
sort: { field: "createdAt", direction: "asc" }
|
|
3433
|
+
});
|
|
3434
|
+
return result.data;
|
|
3435
|
+
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Add a guest to the waitlist for a date and time.
|
|
3438
|
+
*
|
|
3439
|
+
* Resolves or creates the guest via `client.guests.create()` if inline data
|
|
3440
|
+
* is provided, then creates a booking via `client.bookings.create()` with
|
|
3441
|
+
* `metadata.waitlist: true`. The booking starts in `pending` status.
|
|
3442
|
+
*/
|
|
3443
|
+
async addToWaitlist(input) {
|
|
3444
|
+
const guestId = typeof input.guest === "string" ? input.guest : (await this.client.guests.create(input.guest)).id;
|
|
3445
|
+
const checkIn = toISODateTime2(input.date, input.time);
|
|
3446
|
+
return this.client.bookings.create({
|
|
3447
|
+
propertyId: input.propertyId,
|
|
3448
|
+
categoryId: "default",
|
|
3449
|
+
guestId,
|
|
3450
|
+
checkIn,
|
|
3451
|
+
checkOut: checkIn,
|
|
3452
|
+
type: "table",
|
|
3453
|
+
guests: input.guests,
|
|
3454
|
+
notes: input.notes,
|
|
3455
|
+
metadata: { waitlist: true }
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
/**
|
|
3459
|
+
* Remove a booking from the waitlist.
|
|
3460
|
+
*
|
|
3461
|
+
* Calls `client.bookings.delete()` on the given booking ID.
|
|
3462
|
+
*/
|
|
3463
|
+
async removeFromWaitlist(bookingId) {
|
|
3464
|
+
await this.client.bookings.delete(bookingId);
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
function toDateString3(date) {
|
|
3468
|
+
const y = date.getUTCFullYear();
|
|
3469
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
3470
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
3471
|
+
return `${y}-${m}-${d}`;
|
|
3472
|
+
}
|
|
3473
|
+
function toISODateTime2(date, time) {
|
|
3474
|
+
return `${date}T${time}:00.000Z`;
|
|
3475
|
+
}
|
|
3476
|
+
function addMinutes2(time, mins) {
|
|
3477
|
+
const [hourStr, minuteStr] = time.split(":");
|
|
3478
|
+
const totalMinutes = Number(hourStr) * 60 + Number(minuteStr) + mins;
|
|
3479
|
+
const h = Math.floor(totalMinutes / 60) % 24;
|
|
3480
|
+
const m = totalMinutes % 60;
|
|
3481
|
+
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
3482
|
+
}
|
|
3483
|
+
|
|
2491
3484
|
// src/services/tasks.ts
|
|
2492
3485
|
var TasksService = class extends BaseService {
|
|
2493
3486
|
constructor() {
|
|
@@ -2519,10 +3512,338 @@ var TasksService = class extends BaseService {
|
|
|
2519
3512
|
}
|
|
2520
3513
|
};
|
|
2521
3514
|
|
|
3515
|
+
// src/services/webhooks.ts
|
|
3516
|
+
var WebhooksService = class extends BaseService {
|
|
3517
|
+
constructor() {
|
|
3518
|
+
super(...arguments);
|
|
3519
|
+
this.basePath = "/webhooks/v1";
|
|
3520
|
+
}
|
|
3521
|
+
/** Create a new webhook subscription */
|
|
3522
|
+
createSubscription(input) {
|
|
3523
|
+
return this._post(this._buildPath("subscriptions"), input);
|
|
3524
|
+
}
|
|
3525
|
+
/** List webhook subscriptions with optional filters */
|
|
3526
|
+
listSubscriptions(params) {
|
|
3527
|
+
if (!params) {
|
|
3528
|
+
return this._get(this._buildPath("subscriptions"));
|
|
3529
|
+
}
|
|
3530
|
+
const query = {};
|
|
3531
|
+
if (params.active !== void 0) query.active = params.active;
|
|
3532
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
3533
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
3534
|
+
return this._get(this._buildPath("subscriptions"), query);
|
|
3535
|
+
}
|
|
3536
|
+
/** Update a webhook subscription */
|
|
3537
|
+
updateSubscription(subscriptionId, input) {
|
|
3538
|
+
return this._patch(
|
|
3539
|
+
this._buildPath("subscriptions", subscriptionId),
|
|
3540
|
+
input
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
/** List webhook delivery attempts with optional filters */
|
|
3544
|
+
listDeliveries(params) {
|
|
3545
|
+
if (!params) {
|
|
3546
|
+
return this._get(this._buildPath("deliveries"));
|
|
3547
|
+
}
|
|
3548
|
+
const query = {};
|
|
3549
|
+
if (params.subscriptionId !== void 0) query.subscriptionId = params.subscriptionId;
|
|
3550
|
+
if (params.status !== void 0) query.status = params.status;
|
|
3551
|
+
if (params.from !== void 0) query.from = params.from;
|
|
3552
|
+
if (params.to !== void 0) query.to = params.to;
|
|
3553
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
3554
|
+
if (params.cursor !== void 0) query.cursor = params.cursor;
|
|
3555
|
+
return this._get(this._buildPath("deliveries"), query);
|
|
3556
|
+
}
|
|
3557
|
+
};
|
|
3558
|
+
|
|
3559
|
+
// src/sse.ts
|
|
3560
|
+
var SSEClient = class {
|
|
3561
|
+
/**
|
|
3562
|
+
* Create a new `SSEClient`.
|
|
3563
|
+
*
|
|
3564
|
+
* @param baseUrl - Base URL of the booking platform API (e.g., `"https://api.atzentis.io"`).
|
|
3565
|
+
* @param apiKey - API key used to authenticate the SSE stream request.
|
|
3566
|
+
* @param config - Optional client configuration for reconnect behaviour and custom `EventSource`.
|
|
3567
|
+
*/
|
|
3568
|
+
constructor(baseUrl, apiKey, config) {
|
|
3569
|
+
// Connection state
|
|
3570
|
+
this.eventSource = null;
|
|
3571
|
+
this._state = "disconnected";
|
|
3572
|
+
this._connectedAt = null;
|
|
3573
|
+
this._lastEventAt = null;
|
|
3574
|
+
this._lastEventId = null;
|
|
3575
|
+
// Reconnect state
|
|
3576
|
+
this._reconnectAttempts = 0;
|
|
3577
|
+
this._reconnectTimer = null;
|
|
3578
|
+
this._isDisconnecting = false;
|
|
3579
|
+
/**
|
|
3580
|
+
* Internal handler registry.
|
|
3581
|
+
*
|
|
3582
|
+
* Keys are event type strings (including wildcards). The value type is
|
|
3583
|
+
* intentionally `SSEEventHandler<any>` here because the Map must hold
|
|
3584
|
+
* handlers for many different event types — the public API enforces correct
|
|
3585
|
+
* typing through the overloaded `on()` and `off()` signatures.
|
|
3586
|
+
*/
|
|
3587
|
+
// biome-ignore lint/suspicious/noExplicitAny: registry holds mixed handler types
|
|
3588
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
3589
|
+
this.baseUrl = baseUrl;
|
|
3590
|
+
this.apiKey = apiKey;
|
|
3591
|
+
this.config = {
|
|
3592
|
+
initialReconnectDelay: config?.initialReconnectDelay ?? 1e3,
|
|
3593
|
+
maxReconnectDelay: config?.maxReconnectDelay ?? 3e4,
|
|
3594
|
+
maxReconnectAttempts: config?.maxReconnectAttempts ?? Number.POSITIVE_INFINITY
|
|
3595
|
+
};
|
|
3596
|
+
this.EventSourceImpl = config?.EventSource ?? globalThis.EventSource;
|
|
3597
|
+
}
|
|
3598
|
+
// -------------------------------------------------------------------------
|
|
3599
|
+
// Public API
|
|
3600
|
+
// -------------------------------------------------------------------------
|
|
3601
|
+
/**
|
|
3602
|
+
* Open the SSE connection to the platform stream.
|
|
3603
|
+
*
|
|
3604
|
+
* If a connection is already active it is closed and a new one is opened
|
|
3605
|
+
* with the supplied options. The `lastEventId` tracking from a previous
|
|
3606
|
+
* session is preserved for automatic replay unless explicitly overridden in
|
|
3607
|
+
* `options.lastEventId`.
|
|
3608
|
+
*
|
|
3609
|
+
* @param options - Optional filters for channels, property, and replay cursor.
|
|
3610
|
+
*/
|
|
3611
|
+
connect(options) {
|
|
3612
|
+
if (this.eventSource) {
|
|
3613
|
+
this._closeEventSource();
|
|
3614
|
+
}
|
|
3615
|
+
this._isDisconnecting = false;
|
|
3616
|
+
this._connectOptions = options;
|
|
3617
|
+
this._state = "connecting";
|
|
3618
|
+
const url = this._buildUrl(options);
|
|
3619
|
+
this.eventSource = new this.EventSourceImpl(url);
|
|
3620
|
+
this.eventSource.onopen = () => {
|
|
3621
|
+
this._state = "connected";
|
|
3622
|
+
this._connectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3623
|
+
this._reconnectAttempts = 0;
|
|
3624
|
+
this._emit("connection.opened", {
|
|
3625
|
+
type: "connection.opened",
|
|
3626
|
+
id: this._generateId("conn"),
|
|
3627
|
+
timestamp: this._connectedAt,
|
|
3628
|
+
propertyId: options?.propertyId ?? "",
|
|
3629
|
+
data: { connectedAt: this._connectedAt }
|
|
3630
|
+
});
|
|
3631
|
+
};
|
|
3632
|
+
this.eventSource.onerror = () => {
|
|
3633
|
+
if (this._isDisconnecting) return;
|
|
3634
|
+
this._emit("connection.error", {
|
|
3635
|
+
type: "connection.error",
|
|
3636
|
+
id: this._generateId("err"),
|
|
3637
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3638
|
+
propertyId: this._connectOptions?.propertyId ?? "",
|
|
3639
|
+
data: { error: "Connection lost", recoverable: true }
|
|
3640
|
+
});
|
|
3641
|
+
this._scheduleReconnect();
|
|
3642
|
+
};
|
|
3643
|
+
this.eventSource.onmessage = (event) => {
|
|
3644
|
+
this._handleMessage(event);
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
/**
|
|
3648
|
+
* Close the SSE connection and cancel any pending reconnect timer.
|
|
3649
|
+
*
|
|
3650
|
+
* Emits a `connection.closed` lifecycle event with `wasClean: true` if the
|
|
3651
|
+
* client was connected or connecting at the time of the call.
|
|
3652
|
+
*/
|
|
3653
|
+
disconnect() {
|
|
3654
|
+
this._isDisconnecting = true;
|
|
3655
|
+
this._clearReconnectTimer();
|
|
3656
|
+
this._closeEventSource();
|
|
3657
|
+
const wasConnected = this._state !== "disconnected";
|
|
3658
|
+
this._state = "disconnected";
|
|
3659
|
+
this._reconnectAttempts = 0;
|
|
3660
|
+
if (wasConnected) {
|
|
3661
|
+
this._emit("connection.closed", {
|
|
3662
|
+
type: "connection.closed",
|
|
3663
|
+
id: this._generateId("close"),
|
|
3664
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3665
|
+
propertyId: this._connectOptions?.propertyId ?? "",
|
|
3666
|
+
data: { reason: "Client disconnected", wasClean: true }
|
|
3667
|
+
});
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
on(eventType, handler) {
|
|
3671
|
+
if (!this.handlers.has(eventType)) {
|
|
3672
|
+
this.handlers.set(eventType, /* @__PURE__ */ new Set());
|
|
3673
|
+
}
|
|
3674
|
+
this.handlers.get(eventType)?.add(handler);
|
|
3675
|
+
}
|
|
3676
|
+
off(eventType, handler) {
|
|
3677
|
+
const set = this.handlers.get(eventType);
|
|
3678
|
+
if (set) {
|
|
3679
|
+
set.delete(handler);
|
|
3680
|
+
if (set.size === 0) this.handlers.delete(eventType);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
/**
|
|
3684
|
+
* Return a snapshot of the current connection status.
|
|
3685
|
+
*
|
|
3686
|
+
* This is a pure read — calling it has no side effects.
|
|
3687
|
+
*/
|
|
3688
|
+
getStatus() {
|
|
3689
|
+
return {
|
|
3690
|
+
state: this._state,
|
|
3691
|
+
reconnectAttempts: this._reconnectAttempts,
|
|
3692
|
+
connectedAt: this._connectedAt,
|
|
3693
|
+
lastEventAt: this._lastEventAt,
|
|
3694
|
+
lastEventId: this._lastEventId
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
// -------------------------------------------------------------------------
|
|
3698
|
+
// Internal methods
|
|
3699
|
+
// -------------------------------------------------------------------------
|
|
3700
|
+
/**
|
|
3701
|
+
* Build the full SSE stream URL including query parameters.
|
|
3702
|
+
*
|
|
3703
|
+
* The API key is always appended. Optional filters for channels, property ID,
|
|
3704
|
+
* and last-event-id for replay are appended when present.
|
|
3705
|
+
*/
|
|
3706
|
+
_buildUrl(options) {
|
|
3707
|
+
const url = new URL("/sse/v1/stream", this.baseUrl);
|
|
3708
|
+
url.searchParams.set("apiKey", this.apiKey);
|
|
3709
|
+
if (options?.channels && options.channels.length > 0) {
|
|
3710
|
+
url.searchParams.set("channels", options.channels.join(","));
|
|
3711
|
+
}
|
|
3712
|
+
if (options?.propertyId) {
|
|
3713
|
+
url.searchParams.set("propertyId", options.propertyId);
|
|
3714
|
+
}
|
|
3715
|
+
const lastEventId = options?.lastEventId ?? this._lastEventId;
|
|
3716
|
+
if (lastEventId) {
|
|
3717
|
+
url.searchParams.set("lastEventId", lastEventId);
|
|
3718
|
+
}
|
|
3719
|
+
return url.toString();
|
|
3720
|
+
}
|
|
3721
|
+
/**
|
|
3722
|
+
* Parse and dispatch an incoming `MessageEvent` from the `EventSource`.
|
|
3723
|
+
*
|
|
3724
|
+
* Malformed JSON is silently ignored to keep the connection stable.
|
|
3725
|
+
*/
|
|
3726
|
+
_handleMessage(event) {
|
|
3727
|
+
try {
|
|
3728
|
+
const parsed = JSON.parse(event.data);
|
|
3729
|
+
this._lastEventAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3730
|
+
if (parsed.id) this._lastEventId = parsed.id;
|
|
3731
|
+
this._emit(parsed.type, parsed);
|
|
3732
|
+
} catch {
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
/**
|
|
3736
|
+
* Dispatch an event to all matching handlers in the registry.
|
|
3737
|
+
*
|
|
3738
|
+
* Dispatch order:
|
|
3739
|
+
* 1. Handlers registered for the exact event type string.
|
|
3740
|
+
* 2. Handlers registered for the channel wildcard (e.g., `"booking.*"`).
|
|
3741
|
+
* 3. Handlers registered for the global wildcard (`"*"`).
|
|
3742
|
+
*/
|
|
3743
|
+
_emit(eventType, event) {
|
|
3744
|
+
const specific = this.handlers.get(eventType);
|
|
3745
|
+
if (specific) {
|
|
3746
|
+
for (const handler of specific) handler(event);
|
|
3747
|
+
}
|
|
3748
|
+
const channel = eventType.split(".")[0];
|
|
3749
|
+
const channelWildcard = this.handlers.get(`${channel}.*`);
|
|
3750
|
+
if (channelWildcard && eventType !== `${channel}.*`) {
|
|
3751
|
+
for (const handler of channelWildcard) handler(event);
|
|
3752
|
+
}
|
|
3753
|
+
const global = this.handlers.get("*");
|
|
3754
|
+
if (global && eventType !== "*") {
|
|
3755
|
+
for (const handler of global) handler(event);
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Schedule a reconnect attempt using exponential backoff with jitter.
|
|
3760
|
+
*
|
|
3761
|
+
* Closes the current `EventSource`, increments the attempt counter, emits
|
|
3762
|
+
* a `connection.reconnecting` lifecycle event, then calls `connect()` after
|
|
3763
|
+
* the computed delay.
|
|
3764
|
+
*
|
|
3765
|
+
* If `maxReconnectAttempts` is exceeded the client enters `disconnected`
|
|
3766
|
+
* state and emits a final `connection.closed` lifecycle event.
|
|
3767
|
+
*/
|
|
3768
|
+
_scheduleReconnect() {
|
|
3769
|
+
this._closeEventSource();
|
|
3770
|
+
if (this._isDisconnecting) return;
|
|
3771
|
+
if (this._reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
3772
|
+
this._state = "disconnected";
|
|
3773
|
+
this._emit("connection.closed", {
|
|
3774
|
+
type: "connection.closed",
|
|
3775
|
+
id: this._generateId("close"),
|
|
3776
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3777
|
+
propertyId: this._connectOptions?.propertyId ?? "",
|
|
3778
|
+
data: { reason: "Max reconnect attempts exceeded", wasClean: false }
|
|
3779
|
+
});
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
this._state = "reconnecting";
|
|
3783
|
+
this._reconnectAttempts++;
|
|
3784
|
+
const base = Math.min(
|
|
3785
|
+
this.config.initialReconnectDelay * 2 ** (this._reconnectAttempts - 1),
|
|
3786
|
+
this.config.maxReconnectDelay
|
|
3787
|
+
);
|
|
3788
|
+
const delay = base + Math.random() * 1e3;
|
|
3789
|
+
this._emit("connection.reconnecting", {
|
|
3790
|
+
type: "connection.reconnecting",
|
|
3791
|
+
id: this._generateId("reconn"),
|
|
3792
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3793
|
+
propertyId: this._connectOptions?.propertyId ?? "",
|
|
3794
|
+
data: { attempt: this._reconnectAttempts, delay: Math.round(delay) }
|
|
3795
|
+
});
|
|
3796
|
+
this._reconnectTimer = setTimeout(() => {
|
|
3797
|
+
if (!this._isDisconnecting) {
|
|
3798
|
+
this.connect(this._connectOptions);
|
|
3799
|
+
}
|
|
3800
|
+
}, delay);
|
|
3801
|
+
}
|
|
3802
|
+
/**
|
|
3803
|
+
* Detach all event listeners and close the underlying `EventSource`.
|
|
3804
|
+
*
|
|
3805
|
+
* Listeners are removed before calling `close()` to prevent the `onerror`
|
|
3806
|
+
* callback from firing during a deliberate teardown.
|
|
3807
|
+
*/
|
|
3808
|
+
_closeEventSource() {
|
|
3809
|
+
if (this.eventSource) {
|
|
3810
|
+
this.eventSource.onopen = null;
|
|
3811
|
+
this.eventSource.onerror = null;
|
|
3812
|
+
this.eventSource.onmessage = null;
|
|
3813
|
+
this.eventSource.close();
|
|
3814
|
+
this.eventSource = null;
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Cancel the pending reconnect timer if one is scheduled.
|
|
3819
|
+
*/
|
|
3820
|
+
_clearReconnectTimer() {
|
|
3821
|
+
if (this._reconnectTimer !== null) {
|
|
3822
|
+
clearTimeout(this._reconnectTimer);
|
|
3823
|
+
this._reconnectTimer = null;
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
/**
|
|
3827
|
+
* Generate a lightweight unique ID for synthetic lifecycle events.
|
|
3828
|
+
*
|
|
3829
|
+
* Uses `crypto.randomUUID()` when available (browsers, Node 19+, Bun).
|
|
3830
|
+
* Falls back to a timestamp-based string in older environments.
|
|
3831
|
+
*
|
|
3832
|
+
* @param prefix - Short label for readability (e.g., `"conn"`, `"err"`).
|
|
3833
|
+
*/
|
|
3834
|
+
_generateId(prefix) {
|
|
3835
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
3836
|
+
return crypto.randomUUID();
|
|
3837
|
+
}
|
|
3838
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
3839
|
+
}
|
|
3840
|
+
};
|
|
3841
|
+
|
|
2522
3842
|
// src/client.ts
|
|
2523
3843
|
var BookingClient = class {
|
|
2524
3844
|
constructor(config) {
|
|
2525
3845
|
const validated = bookingClientConfigSchema.parse(config);
|
|
3846
|
+
this._config = validated;
|
|
2526
3847
|
this.httpClient = new HttpClient({
|
|
2527
3848
|
baseUrl: validated.baseUrl,
|
|
2528
3849
|
apiKey: validated.apiKey,
|
|
@@ -2546,6 +3867,11 @@ var BookingClient = class {
|
|
|
2546
3867
|
this._bookings ?? (this._bookings = new BookingsService(this.httpClient));
|
|
2547
3868
|
return this._bookings;
|
|
2548
3869
|
}
|
|
3870
|
+
/** Concierge service — lazy-initialized on first access */
|
|
3871
|
+
get concierge() {
|
|
3872
|
+
this._concierge ?? (this._concierge = new ConciergeService(this.httpClient));
|
|
3873
|
+
return this._concierge;
|
|
3874
|
+
}
|
|
2549
3875
|
/** Distribution service — lazy-initialized on first access */
|
|
2550
3876
|
get distribution() {
|
|
2551
3877
|
this._distribution ?? (this._distribution = new DistributionService(this.httpClient));
|
|
@@ -2556,6 +3882,11 @@ var BookingClient = class {
|
|
|
2556
3882
|
this._finance ?? (this._finance = new FinanceService(this.httpClient));
|
|
2557
3883
|
return this._finance;
|
|
2558
3884
|
}
|
|
3885
|
+
/** Fiscal service — lazy-initialized on first access */
|
|
3886
|
+
get fiscal() {
|
|
3887
|
+
this._fiscal ?? (this._fiscal = new FiscalService(this.httpClient));
|
|
3888
|
+
return this._fiscal;
|
|
3889
|
+
}
|
|
2559
3890
|
/** Groups service — lazy-initialized on first access */
|
|
2560
3891
|
get groups() {
|
|
2561
3892
|
this._groups ?? (this._groups = new GroupsService(this.httpClient));
|
|
@@ -2571,16 +3902,36 @@ var BookingClient = class {
|
|
|
2571
3902
|
this._housekeeping ?? (this._housekeeping = new HousekeepingService(this.httpClient));
|
|
2572
3903
|
return this._housekeeping;
|
|
2573
3904
|
}
|
|
3905
|
+
/** ID scanning service — lazy-initialized on first access */
|
|
3906
|
+
get idScanning() {
|
|
3907
|
+
this._idScanning ?? (this._idScanning = new IdScanningService(this.httpClient));
|
|
3908
|
+
return this._idScanning;
|
|
3909
|
+
}
|
|
3910
|
+
/** Intelligence service — lazy-initialized on first access */
|
|
3911
|
+
get intelligence() {
|
|
3912
|
+
this._intelligence ?? (this._intelligence = new IntelligenceService(this.httpClient));
|
|
3913
|
+
return this._intelligence;
|
|
3914
|
+
}
|
|
2574
3915
|
/** Night audit service — lazy-initialized on first access */
|
|
2575
3916
|
get nightAudit() {
|
|
2576
3917
|
this._nightAudit ?? (this._nightAudit = new NightAuditService(this.httpClient));
|
|
2577
3918
|
return this._nightAudit;
|
|
2578
3919
|
}
|
|
3920
|
+
/** Notifications service — lazy-initialized on first access */
|
|
3921
|
+
get notifications() {
|
|
3922
|
+
this._notifications ?? (this._notifications = new NotificationsService(this.httpClient));
|
|
3923
|
+
return this._notifications;
|
|
3924
|
+
}
|
|
2579
3925
|
/** Payments service — lazy-initialized on first access */
|
|
2580
3926
|
get payments() {
|
|
2581
3927
|
this._payments ?? (this._payments = new PaymentsService(this.httpClient));
|
|
2582
3928
|
return this._payments;
|
|
2583
3929
|
}
|
|
3930
|
+
/** Portfolios service — lazy-initialized on first access */
|
|
3931
|
+
get portfolios() {
|
|
3932
|
+
this._portfolios ?? (this._portfolios = new PortfoliosService(this.httpClient));
|
|
3933
|
+
return this._portfolios;
|
|
3934
|
+
}
|
|
2584
3935
|
/** Properties service — lazy-initialized on first access */
|
|
2585
3936
|
get properties() {
|
|
2586
3937
|
this._properties ?? (this._properties = new PropertiesService(this.httpClient));
|
|
@@ -2591,6 +3942,11 @@ var BookingClient = class {
|
|
|
2591
3942
|
this._ratePlans ?? (this._ratePlans = new RatePlansService(this.httpClient));
|
|
2592
3943
|
return this._ratePlans;
|
|
2593
3944
|
}
|
|
3945
|
+
/** Revenue service — lazy-initialized on first access */
|
|
3946
|
+
get revenue() {
|
|
3947
|
+
this._revenue ?? (this._revenue = new RevenueService(this.httpClient));
|
|
3948
|
+
return this._revenue;
|
|
3949
|
+
}
|
|
2594
3950
|
/** Categories service — lazy-initialized on first access */
|
|
2595
3951
|
get categories() {
|
|
2596
3952
|
this._categories ?? (this._categories = new CategoriesService(this.httpClient));
|
|
@@ -2606,6 +3962,11 @@ var BookingClient = class {
|
|
|
2606
3962
|
this._reviews ?? (this._reviews = new ReviewsService(this.httpClient));
|
|
2607
3963
|
return this._reviews;
|
|
2608
3964
|
}
|
|
3965
|
+
/** Settings service — lazy-initialized on first access */
|
|
3966
|
+
get settings() {
|
|
3967
|
+
this._settings ?? (this._settings = new SettingsService(this.httpClient));
|
|
3968
|
+
return this._settings;
|
|
3969
|
+
}
|
|
2609
3970
|
/** Spaces service — lazy-initialized on first access */
|
|
2610
3971
|
get spaces() {
|
|
2611
3972
|
this._spaces ?? (this._spaces = new SpacesService(this.httpClient));
|
|
@@ -2626,6 +3987,31 @@ var BookingClient = class {
|
|
|
2626
3987
|
this._tasks ?? (this._tasks = new TasksService(this.httpClient));
|
|
2627
3988
|
return this._tasks;
|
|
2628
3989
|
}
|
|
3990
|
+
/** Webhooks service — lazy-initialized on first access */
|
|
3991
|
+
get webhooks() {
|
|
3992
|
+
this._webhooks ?? (this._webhooks = new WebhooksService(this.httpClient));
|
|
3993
|
+
return this._webhooks;
|
|
3994
|
+
}
|
|
3995
|
+
/** Stays vertical helper — lazy-initialized on first access */
|
|
3996
|
+
get stays() {
|
|
3997
|
+
this._stays ?? (this._stays = new StaysHelper(this));
|
|
3998
|
+
return this._stays;
|
|
3999
|
+
}
|
|
4000
|
+
/** Tables vertical helper — lazy-initialized on first access */
|
|
4001
|
+
get tables() {
|
|
4002
|
+
this._tables ?? (this._tables = new TablesHelper(this));
|
|
4003
|
+
return this._tables;
|
|
4004
|
+
}
|
|
4005
|
+
/** Services vertical helper — lazy-initialized on first access */
|
|
4006
|
+
get services() {
|
|
4007
|
+
this._services ?? (this._services = new ServicesHelper(this));
|
|
4008
|
+
return this._services;
|
|
4009
|
+
}
|
|
4010
|
+
/** SSE client — lazy-initialized on first access */
|
|
4011
|
+
get sse() {
|
|
4012
|
+
this._sse ?? (this._sse = new SSEClient(this._config.baseUrl, this._config.apiKey));
|
|
4013
|
+
return this._sse;
|
|
4014
|
+
}
|
|
2629
4015
|
setApiKey(key) {
|
|
2630
4016
|
if (!key || key.trim().length === 0) {
|
|
2631
4017
|
throw new Error("apiKey must be a non-empty string");
|
|
@@ -2742,35 +4128,48 @@ var VERSION = "0.1.0";
|
|
|
2742
4128
|
BookingError,
|
|
2743
4129
|
BookingsService,
|
|
2744
4130
|
CategoriesService,
|
|
4131
|
+
ConciergeService,
|
|
2745
4132
|
ConflictError,
|
|
2746
4133
|
DEFAULT_MODULES,
|
|
2747
4134
|
DiscountsService,
|
|
2748
4135
|
DistributionService,
|
|
2749
4136
|
FinanceService,
|
|
4137
|
+
FiscalService,
|
|
2750
4138
|
ForbiddenError,
|
|
2751
4139
|
GroupsService,
|
|
2752
4140
|
GuestsService,
|
|
2753
4141
|
HousekeepingService,
|
|
2754
4142
|
HttpClient,
|
|
4143
|
+
IdScanningService,
|
|
4144
|
+
IntelligenceService,
|
|
2755
4145
|
NightAuditService,
|
|
2756
4146
|
NotFoundError,
|
|
4147
|
+
NotificationsService,
|
|
2757
4148
|
PROPERTY_MODULES,
|
|
2758
4149
|
PaymentError,
|
|
2759
4150
|
PaymentsService,
|
|
4151
|
+
PortfoliosService,
|
|
2760
4152
|
PropertiesService,
|
|
2761
4153
|
RateLimitError,
|
|
2762
4154
|
RatePlansService,
|
|
4155
|
+
RevenueService,
|
|
2763
4156
|
ReviewsService,
|
|
2764
4157
|
SPACE_STATUSES,
|
|
2765
4158
|
SPACE_TYPES,
|
|
4159
|
+
SSEClient,
|
|
2766
4160
|
ServerError,
|
|
4161
|
+
ServicesHelper,
|
|
4162
|
+
SettingsService,
|
|
2767
4163
|
SpacesService,
|
|
2768
4164
|
StaffService,
|
|
4165
|
+
StaysHelper,
|
|
2769
4166
|
SupplyService,
|
|
4167
|
+
TablesHelper,
|
|
2770
4168
|
TasksService,
|
|
2771
4169
|
TimeoutError,
|
|
2772
4170
|
VERSION,
|
|
2773
4171
|
ValidationError,
|
|
4172
|
+
WebhooksService,
|
|
2774
4173
|
bookingClientConfigSchema,
|
|
2775
4174
|
createErrorFromResponse,
|
|
2776
4175
|
firstPage,
|