@voyantjs/connect-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js ADDED
@@ -0,0 +1,644 @@
1
+ import { VoyantApiError, VoyantTransport } from "@voyant-sdk/sdk-core";
2
+ function withIdempotency(idempotencyKey) {
3
+ if (!idempotencyKey)
4
+ return undefined;
5
+ return { "idempotency-key": idempotencyKey };
6
+ }
7
+ function scopeQuery(filter) {
8
+ if (!filter)
9
+ return {};
10
+ const out = {};
11
+ if (filter.connectionId !== undefined)
12
+ out.connectionId = filter.connectionId;
13
+ if (filter.providerKey !== undefined)
14
+ out.providerKey = filter.providerKey;
15
+ return out;
16
+ }
17
+ export class VoyantConnectClient {
18
+ transport;
19
+ defaultOperatorId;
20
+ constructor(options) {
21
+ this.transport = new VoyantTransport(options);
22
+ this.defaultOperatorId = options.operatorId ?? null;
23
+ }
24
+ resolveOperatorId(scope) {
25
+ const operatorId = scope?.operatorId ?? this.defaultOperatorId;
26
+ if (!operatorId) {
27
+ throw new VoyantApiError("operatorId is required: pass it via the call's OperatorScope or set VoyantConnectClientOptions.operatorId", { status: 0, body: null, requestId: null });
28
+ }
29
+ return operatorId;
30
+ }
31
+ // ── OAuth ─────────────────────────────────────────────────────────────
32
+ oauth = {
33
+ issueToken: (input) => this.transport.request("/connect/v1/oauth/token", {
34
+ body: {
35
+ client_id: input.clientId,
36
+ client_secret: input.clientSecret,
37
+ grant_type: input.grantType ?? "client_credentials",
38
+ scope: input.scope,
39
+ },
40
+ method: "POST",
41
+ unwrapData: false,
42
+ }),
43
+ };
44
+ // ── Operators (control plane) ────────────────────────────────────────
45
+ operators = {
46
+ list: () => this.transport.request("/connect/v1/operators"),
47
+ get: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}`),
48
+ create: (input) => this.transport.request("/connect/v1/operators", {
49
+ body: input,
50
+ method: "POST",
51
+ }),
52
+ update: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}`, {
53
+ body: input,
54
+ method: "PATCH",
55
+ }),
56
+ deactivate: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}`, {
57
+ method: "DELETE",
58
+ }),
59
+ getUsage: (operatorId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/usage`, {
60
+ query: query,
61
+ }),
62
+ listSearchDocuments: (operatorId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/search-documents`, { query: query }),
63
+ listSearchProjectionChanges: (operatorId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/search-projection-changes`, {
64
+ query: query,
65
+ unwrapData: false,
66
+ }),
67
+ };
68
+ // ── Connector providers ──────────────────────────────────────────────
69
+ connectorProviders = {
70
+ list: () => this.transport.request("/connect/v1/connector-providers"),
71
+ update: (providerKey, input) => this.transport.request(`/connect/v1/connector-providers/${providerKey}`, { body: input, method: "PATCH" }),
72
+ listApplications: (providerKey) => this.transport.request(`/connect/v1/connector-providers/${providerKey}/applications`),
73
+ listRegistrations: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations`),
74
+ getRegistration: (operatorId, registrationId) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations/${registrationId}`),
75
+ upsertRegistration: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations`, { body: input, method: "POST" }),
76
+ updateRegistration: (operatorId, registrationId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations/${registrationId}`, { body: input, method: "PATCH" }),
77
+ updateTuiSettings: (operatorId, registrationId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations/${registrationId}/tui-settings`, { body: input, method: "PATCH" }),
78
+ revalidateRegistration: (operatorId, registrationId) => this.transport.request(`/connect/v1/operators/${operatorId}/provider-registrations/${registrationId}/revalidate`, { method: "POST" }),
79
+ };
80
+ // ── Connections (control plane) ──────────────────────────────────────
81
+ connections = {
82
+ list: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}/connections`),
83
+ get: (operatorId, connectionId) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}`),
84
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/connections`, { body: input, method: "POST" }),
85
+ update: (operatorId, connectionId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}`, { body: input, method: "PATCH" }),
86
+ delete: (operatorId, connectionId) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}`, { method: "DELETE" }),
87
+ rotateWebhookSecret: (operatorId, connectionId) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/webhook-secret/rotate`, { method: "POST" }),
88
+ listProjectionSyncs: (operatorId, connectionId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/search-projection-syncs`, { query: query }),
89
+ triggerProjectionSync: (operatorId, connectionId) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/search-projection-syncs`, { method: "POST" }),
90
+ listWebhookEvents: (operatorId, connectionId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/webhook-events`, { query: query }),
91
+ listHealthEvents: (operatorId, connectionId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/health-events`, { query: query }),
92
+ listRequestLogs: (operatorId, connectionId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/connections/${connectionId}/request-logs`, {
93
+ query: {
94
+ ...query,
95
+ from: query?.from instanceof Date ? query.from.toISOString() : query?.from,
96
+ to: query?.to instanceof Date ? query.to.toISOString() : query?.to,
97
+ },
98
+ }),
99
+ };
100
+ // ── Links ────────────────────────────────────────────────────────────
101
+ links = {
102
+ list: (operatorId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/links`, {
103
+ query: query,
104
+ }),
105
+ get: (operatorId, linkId) => this.transport.request(`/connect/v1/operators/${operatorId}/links/${linkId}`),
106
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/links`, {
107
+ body: input,
108
+ method: "POST",
109
+ }),
110
+ update: (operatorId, linkId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/links/${linkId}`, { body: input, method: "PATCH" }),
111
+ updateCapability: (operatorId, linkId, capabilityId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/links/${linkId}/capabilities/${capabilityId}`, { body: input, method: "PATCH" }),
112
+ };
113
+ // ── OAuth clients ────────────────────────────────────────────────────
114
+ oauthClients = {
115
+ list: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}/oauth-clients`),
116
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/oauth-clients`, { body: input, method: "POST" }),
117
+ revoke: (operatorId, clientId) => this.transport.request(`/connect/v1/operators/${operatorId}/oauth-clients/${clientId}`, { method: "DELETE" }),
118
+ };
119
+ // ── Grants ───────────────────────────────────────────────────────────
120
+ grants = {
121
+ listForOperator: (operatorId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/grants`, {
122
+ query: query,
123
+ }),
124
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/grants`, {
125
+ body: input,
126
+ method: "POST",
127
+ }),
128
+ update: (operatorId, grantId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/grants/${grantId}`, { body: input, method: "PATCH" }),
129
+ revoke: (operatorId, grantId) => this.transport.request(`/connect/v1/operators/${operatorId}/grants/${grantId}`, { method: "DELETE" }),
130
+ listReceived: (query) => this.transport.request("/connect/v1/grants/received", {
131
+ query: query,
132
+ }),
133
+ get: (grantId) => this.transport.request(`/connect/v1/grants/${grantId}`),
134
+ };
135
+ // ── Audit logs ───────────────────────────────────────────────────────
136
+ auditLogs = {
137
+ list: (query) => this.transport.request("/connect/v1/audit-logs", {
138
+ query: query,
139
+ unwrapData: false,
140
+ }),
141
+ };
142
+ // ── Invite tokens ────────────────────────────────────────────────────
143
+ inviteTokens = {
144
+ list: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}/invite-tokens`),
145
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/invite-tokens`, {
146
+ body: {
147
+ ...input,
148
+ expiresAt: input.expiresAt instanceof Date
149
+ ? input.expiresAt.toISOString()
150
+ : input.expiresAt,
151
+ },
152
+ method: "POST",
153
+ }),
154
+ revoke: (operatorId, inviteId) => this.transport.request(`/connect/v1/operators/${operatorId}/invite-tokens/${inviteId}`, { method: "DELETE" }),
155
+ lookup: (token) => this.transport.request(`/connect/v1/invites/${token}`),
156
+ redeem: (token) => this.transport.request(`/connect/v1/invites/${token}/redeem`, {
157
+ method: "POST",
158
+ }),
159
+ };
160
+ // ── Webhook subscriptions ────────────────────────────────────────────
161
+ webhookSubscriptions = {
162
+ list: (operatorId) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions`),
163
+ create: (operatorId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions`, { body: input, method: "POST" }),
164
+ update: (operatorId, subscriptionId, input) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions/${subscriptionId}`, { body: input, method: "PATCH" }),
165
+ delete: (operatorId, subscriptionId) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions/${subscriptionId}`, { method: "DELETE" }),
166
+ listDeliveries: (operatorId, subscriptionId, query) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions/${subscriptionId}/deliveries`, { query: query }),
167
+ sendTestEvent: (operatorId, subscriptionId) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions/${subscriptionId}/test`, { method: "POST" }),
168
+ replayDelivery: (operatorId, subscriptionId, deliveryId) => this.transport.request(`/connect/v1/operators/${operatorId}/webhook-subscriptions/${subscriptionId}/deliveries/${deliveryId}/replay`, { method: "POST" }),
169
+ };
170
+ // ── Custom connection requests ───────────────────────────────────────
171
+ customConnectionRequests = {
172
+ list: (organizationId) => this.transport.request(`/connect/v1/organizations/${organizationId}/custom-connection-requests`),
173
+ create: (organizationId, input) => this.transport.request(`/connect/v1/organizations/${organizationId}/custom-connection-requests`, { body: input, method: "POST" }),
174
+ };
175
+ // ── Products ─────────────────────────────────────────────────────────
176
+ products = {
177
+ /**
178
+ * List products across all connections in the operator's catalog.
179
+ * Optionally filter by `connectionId` and/or `providerKey` (single value
180
+ * or array). Falls back to the client's default `operatorId` when omitted.
181
+ */
182
+ list: async (filter) => {
183
+ const operatorId = this.resolveOperatorId(filter);
184
+ return this.transport.request(`/connect/v1/operators/${operatorId}/products`, { query: scopeQuery(filter) });
185
+ },
186
+ /** Look up a single product in the operator's catalog. */
187
+ get: async (productId, scope) => {
188
+ const operatorId = this.resolveOperatorId(scope);
189
+ return this.transport.request(`/connect/v1/operators/${operatorId}/products/${productId}`);
190
+ },
191
+ /** Per-connection list (Connect-normalized). Optional `supplierId` filter. */
192
+ listOnConnection: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/products`, {
193
+ query: options?.supplierId ? { supplierId: options.supplierId } : undefined,
194
+ unwrapData: false,
195
+ }),
196
+ /** Per-connection product lookup (Connect-normalized). */
197
+ getOnConnection: (connectionId, productId) => this.transport.request(`/connect/v1/connections/${connectionId}/products/${productId}`, { unwrapData: false }),
198
+ /** List options for a product on a connection. */
199
+ listOptions: (connectionId, productId) => this.transport.request(`/connect/v1/connections/${connectionId}/products/${productId}/options`, { unwrapData: false }),
200
+ /** List extras (add-ons) for a product on a connection. */
201
+ listExtras: (connectionId, productId) => this.transport.request(`/connect/v1/connections/${connectionId}/products/${productId}/extras`, { unwrapData: false }),
202
+ };
203
+ // ── Options ──────────────────────────────────────────────────────────
204
+ options = {
205
+ /** List units (pricing/capacity buckets) for an option. */
206
+ listUnits: (connectionId, optionId) => this.transport.request(`/connect/v1/connections/${connectionId}/options/${optionId}/units`, { unwrapData: false }),
207
+ /** List per-option extras configuration. */
208
+ listExtraConfigs: (connectionId, optionId) => this.transport.request(`/connect/v1/connections/${connectionId}/options/${optionId}/extra-configs`, { unwrapData: false }),
209
+ };
210
+ // ── Suppliers ────────────────────────────────────────────────────────
211
+ suppliers = {
212
+ /**
213
+ * List suppliers across all connections in the operator's catalog.
214
+ * Optionally filter by `connectionId` and/or `providerKey`.
215
+ */
216
+ list: async (filter) => {
217
+ const operatorId = this.resolveOperatorId(filter);
218
+ return this.transport.request(`/connect/v1/operators/${operatorId}/suppliers`, { query: scopeQuery(filter) });
219
+ },
220
+ /** Per-connection list (Connect-normalized). */
221
+ listOnConnection: (connectionId) => this.transport.request(`/connect/v1/connections/${connectionId}/suppliers`, { unwrapData: false }),
222
+ };
223
+ // ── Availability ─────────────────────────────────────────────────────
224
+ availability = {
225
+ /** Per-connection availability slots (Connect-normalized). */
226
+ list: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/availability`, {
227
+ query: query,
228
+ unwrapData: false,
229
+ }),
230
+ /** Per-connection calendar query (Connect-normalized). */
231
+ calendar: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/availability/calendar`, { body: input, method: "POST", unwrapData: false }),
232
+ };
233
+ // ── Bookings ─────────────────────────────────────────────────────────
234
+ bookings = {
235
+ /**
236
+ * List bookings across all connections in the operator's catalog.
237
+ * Filter by `connectionId`, `providerKey`, status, and/or date range.
238
+ */
239
+ listAll: async (filter) => {
240
+ const operatorId = this.resolveOperatorId(filter);
241
+ const query = {};
242
+ if (filter?.connectionId !== undefined)
243
+ query.connectionId = filter.connectionId;
244
+ if (filter?.providerKey !== undefined)
245
+ query.providerKey = filter.providerKey;
246
+ if (filter?.status !== undefined)
247
+ query.status = filter.status;
248
+ if (filter?.localDateStart !== undefined)
249
+ query.localDateStart = filter.localDateStart;
250
+ if (filter?.localDateEnd !== undefined)
251
+ query.localDateEnd = filter.localDateEnd;
252
+ if (filter?.limit !== undefined)
253
+ query.limit = filter.limit;
254
+ return this.transport.request(`/connect/v1/operators/${operatorId}/bookings`, { query });
255
+ },
256
+ /** Per-connection booking list (Connect-normalized). */
257
+ list: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings`, {
258
+ query: query,
259
+ unwrapData: false,
260
+ }),
261
+ get: (connectionId, bookingId) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings/${bookingId}`, { unwrapData: false }),
262
+ create: (connectionId, input, options) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings`, {
263
+ body: input,
264
+ headers: withIdempotency(options?.idempotencyKey),
265
+ method: "POST",
266
+ unwrapData: false,
267
+ }),
268
+ confirm: (connectionId, bookingId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings/${bookingId}/confirm`, { body: input ?? {}, method: "POST", unwrapData: false }),
269
+ cancel: (connectionId, bookingId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings/${bookingId}`, { body: input ?? {}, method: "DELETE", unwrapData: false }),
270
+ /** Booking activity log (Connect-normalized). */
271
+ listActivities: (connectionId, bookingId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/bookings/${bookingId}/activities`, {
272
+ query: {
273
+ from: query?.from instanceof Date ? query.from.toISOString() : query?.from,
274
+ to: query?.to instanceof Date ? query.to.toISOString() : query?.to,
275
+ type: query?.type,
276
+ limit: query?.limit,
277
+ },
278
+ unwrapData: false,
279
+ }),
280
+ };
281
+ // ── Health ───────────────────────────────────────────────────────────
282
+ health = {
283
+ /** Per-connection sync health. */
284
+ get: (connectionId) => this.transport.request(`/connect/v1/connections/${connectionId}/health`, { unwrapData: false }),
285
+ };
286
+ // ── Accommodations (hospitality data plane) ──────────────────────────
287
+ accommodations = {
288
+ /**
289
+ * List accommodations across all connections in the operator's catalog.
290
+ * Filter by `connectionId`, `providerKey`, `category`, `countryCode`,
291
+ * `city`, `minStars`, `locale`. Falls back to client.operatorId.
292
+ */
293
+ list: async (filter) => {
294
+ const operatorId = this.resolveOperatorId(filter);
295
+ const query = {};
296
+ if (filter?.connectionId !== undefined)
297
+ query.connectionId = filter.connectionId;
298
+ if (filter?.providerKey !== undefined)
299
+ query.providerKey = filter.providerKey;
300
+ if (filter?.category !== undefined)
301
+ query.category = filter.category;
302
+ if (filter?.countryCode !== undefined)
303
+ query.countryCode = filter.countryCode;
304
+ if (filter?.city !== undefined)
305
+ query.city = filter.city;
306
+ if (filter?.minStars !== undefined)
307
+ query.minStars = filter.minStars;
308
+ if (filter?.maxPriceFromAmountMinor !== undefined)
309
+ query.maxPriceFromAmountMinor = filter.maxPriceFromAmountMinor;
310
+ if (filter?.locale !== undefined)
311
+ query.locale = filter.locale;
312
+ if (filter?.limit !== undefined)
313
+ query.limit = filter.limit;
314
+ return this.transport.request(`/connect/v1/operators/${operatorId}/accommodations`, { query });
315
+ },
316
+ /** Look up an accommodation in the operator's catalog. */
317
+ get: async (accommodationId, scope) => {
318
+ const operatorId = this.resolveOperatorId(scope);
319
+ return this.transport.request(`/connect/v1/operators/${operatorId}/accommodations/${accommodationId}`, {
320
+ query: scope?.locale ? { locale: scope.locale } : undefined,
321
+ });
322
+ },
323
+ /** Per-connection list (Connect-normalized). */
324
+ listOnConnection: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/accommodations`, {
325
+ query: options,
326
+ unwrapData: false,
327
+ }),
328
+ /** Per-connection accommodation lookup. */
329
+ getOnConnection: (connectionId, accommodationId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/accommodations/${accommodationId}`, {
330
+ query: options,
331
+ unwrapData: false,
332
+ }),
333
+ /** List room types for an accommodation on a connection. */
334
+ listRoomTypes: (connectionId, accommodationExternalId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/accommodations/${accommodationExternalId}/room-types`, {
335
+ query: options,
336
+ unwrapData: false,
337
+ }),
338
+ /** List rate plans for an accommodation, optionally filtered by room type. */
339
+ listRatePlans: (connectionId, accommodationExternalId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/accommodations/${accommodationExternalId}/rate-plans`, {
340
+ query: options,
341
+ unwrapData: false,
342
+ }),
343
+ };
344
+ // ── Stays (search + booking lifecycle) ───────────────────────────────
345
+ stays = {
346
+ /** Per-connection search; delegates to the adapter. Returns offers + diagnostics. */
347
+ search: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/search`, { body: query, method: "POST", unwrapData: false }),
348
+ /**
349
+ * Cross-connection search. Fans out across the operator's accessible
350
+ * connections in parallel, merges offers, returns a unified response with
351
+ * `connectionDiagnostics` per connection.
352
+ */
353
+ searchAcrossProviders: async (query, filter) => {
354
+ const operatorId = this.resolveOperatorId(filter);
355
+ const body = { ...query };
356
+ if (filter && (filter.connectionId !== undefined || filter.providerKey !== undefined)) {
357
+ body.filter = {
358
+ connectionId: filter.connectionId,
359
+ providerKey: filter.providerKey,
360
+ };
361
+ }
362
+ return this.transport.request(`/connect/v1/operators/${operatorId}/stays/search`, { body, method: "POST", unwrapData: false });
363
+ },
364
+ /**
365
+ * Lock an offer. Pass the StayOffer returned from search; Connect creates a
366
+ * server-side hold whose TTL caps any provider-native lock that exists.
367
+ */
368
+ lock: (connectionId, offer, options) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/lock`, {
369
+ body: { offerId: offer.id, offer, ttlMinutes: options?.ttlMinutes },
370
+ method: "POST",
371
+ unwrapData: false,
372
+ }),
373
+ releaseLock: (connectionId, holdId) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/holds/${holdId}`, { method: "DELETE", unwrapData: false }),
374
+ getHold: (connectionId, holdId) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/holds/${holdId}`, { unwrapData: false }),
375
+ /**
376
+ * Confirm a held offer into a booking. `idempotencyKey` becomes
377
+ * `Idempotency-Key` and is enforced server-side against the hold.
378
+ */
379
+ confirm: (connectionId, input, options) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/bookings`, {
380
+ body: input,
381
+ headers: withIdempotency(options?.idempotencyKey),
382
+ method: "POST",
383
+ unwrapData: false,
384
+ }),
385
+ cancel: (connectionId, bookingId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/bookings/${bookingId}`, {
386
+ body: options?.reason ? { reason: options.reason } : {},
387
+ method: "DELETE",
388
+ unwrapData: false,
389
+ }),
390
+ get: (connectionId, bookingId) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/bookings/${bookingId}`, { unwrapData: false }),
391
+ list: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/stays/bookings`, {
392
+ query: query,
393
+ unwrapData: false,
394
+ }),
395
+ /** Cross-connection booking list scoped to the operator. */
396
+ listAll: async (filter) => {
397
+ const operatorId = this.resolveOperatorId(filter);
398
+ const query = {};
399
+ if (filter?.connectionId !== undefined)
400
+ query.connectionId = filter.connectionId;
401
+ if (filter?.providerKey !== undefined)
402
+ query.providerKey = filter.providerKey;
403
+ if (filter?.status !== undefined)
404
+ query.status = filter.status;
405
+ if (filter?.checkInFrom !== undefined)
406
+ query.checkInFrom = filter.checkInFrom;
407
+ if (filter?.checkInTo !== undefined)
408
+ query.checkInTo = filter.checkInTo;
409
+ if (filter?.limit !== undefined)
410
+ query.limit = filter.limit;
411
+ return this.transport.request(`/connect/v1/operators/${operatorId}/stays/bookings`, { query });
412
+ },
413
+ };
414
+ // ── Cruises (cruise data plane — read API) ───────────────────────────
415
+ cruises = {
416
+ /**
417
+ * List cruises across all of the operator's accessible connections.
418
+ * Filter by `connectionId`, `providerKey`, `cruiseType`, line/ship,
419
+ * `minNights`/`maxNights`, `locale`. Falls back to client.operatorId.
420
+ */
421
+ list: async (filter) => {
422
+ const operatorId = this.resolveOperatorId(filter);
423
+ const query = {};
424
+ if (filter?.connectionId !== undefined)
425
+ query.connectionId = filter.connectionId;
426
+ if (filter?.providerKey !== undefined)
427
+ query.providerKey = filter.providerKey;
428
+ if (filter?.cruiseType !== undefined)
429
+ query.cruiseType = filter.cruiseType;
430
+ if (filter?.cruiseLineExternalId !== undefined)
431
+ query.cruiseLineExternalId = filter.cruiseLineExternalId;
432
+ if (filter?.shipExternalId !== undefined)
433
+ query.shipExternalId = filter.shipExternalId;
434
+ if (filter?.minNights !== undefined)
435
+ query.minNights = filter.minNights;
436
+ if (filter?.maxNights !== undefined)
437
+ query.maxNights = filter.maxNights;
438
+ if (filter?.locale !== undefined)
439
+ query.locale = filter.locale;
440
+ if (filter?.limit !== undefined)
441
+ query.limit = filter.limit;
442
+ return this.transport.request(`/connect/v1/operators/${operatorId}/cruises`, { query });
443
+ },
444
+ /** Look up a cruise by id in the operator's catalog. */
445
+ get: async (cruiseId, scope) => {
446
+ const operatorId = this.resolveOperatorId(scope);
447
+ return this.transport.request(`/connect/v1/operators/${operatorId}/cruises/${cruiseId}`, {
448
+ query: scope?.locale ? { locale: scope.locale } : undefined,
449
+ });
450
+ },
451
+ /** Per-connection list (Connect-normalized). */
452
+ listOnConnection: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruises`, {
453
+ query: options,
454
+ unwrapData: false,
455
+ }),
456
+ /** Per-connection cruise lookup. */
457
+ getOnConnection: (connectionId, cruiseId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruises/${cruiseId}`, {
458
+ query: options,
459
+ unwrapData: false,
460
+ }),
461
+ /**
462
+ * Cross-connection sailings list — primary search axis is departure
463
+ * window. Filter by cruise/ship/sales status.
464
+ */
465
+ listSailings: async (filter) => {
466
+ const operatorId = this.resolveOperatorId(filter);
467
+ const query = {};
468
+ if (filter?.connectionId !== undefined)
469
+ query.connectionId = filter.connectionId;
470
+ if (filter?.providerKey !== undefined)
471
+ query.providerKey = filter.providerKey;
472
+ if (filter?.cruiseExternalId !== undefined)
473
+ query.cruiseExternalId = filter.cruiseExternalId;
474
+ if (filter?.shipExternalId !== undefined)
475
+ query.shipExternalId = filter.shipExternalId;
476
+ if (filter?.salesStatus !== undefined)
477
+ query.salesStatus = filter.salesStatus;
478
+ if (filter?.departureFrom !== undefined)
479
+ query.departureFrom = filter.departureFrom;
480
+ if (filter?.departureTo !== undefined)
481
+ query.departureTo = filter.departureTo;
482
+ if (filter?.limit !== undefined)
483
+ query.limit = filter.limit;
484
+ return this.transport.request(`/connect/v1/operators/${operatorId}/sailings`, { query });
485
+ },
486
+ /** Look up a sailing by id in the operator's catalog. */
487
+ getSailing: async (sailingId, scope) => {
488
+ const operatorId = this.resolveOperatorId(scope);
489
+ return this.transport.request(`/connect/v1/operators/${operatorId}/sailings/${sailingId}`);
490
+ },
491
+ /** Per-connection sailings list. */
492
+ listSailingsOnConnection: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/sailings`, {
493
+ query: options,
494
+ unwrapData: false,
495
+ }),
496
+ /** Per-connection sailing lookup. */
497
+ getSailingOnConnection: (connectionId, sailingId) => this.transport.request(`/connect/v1/connections/${connectionId}/sailings/${sailingId}`, { unwrapData: false }),
498
+ /** Itinerary days for a sailing on a connection (port calls + sea days). */
499
+ listItinerary: (connectionId, sailingExternalId) => this.transport.request(`/connect/v1/connections/${connectionId}/sailings/${sailingExternalId}/itinerary`, { unwrapData: false }),
500
+ /** Cabin pricing grid for a sailing on a connection. */
501
+ listSailingPricing: (connectionId, sailingExternalId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/sailings/${sailingExternalId}/pricing`, {
502
+ query: query,
503
+ unwrapData: false,
504
+ }),
505
+ /** Cruise lines available on a connection. */
506
+ listCruiseLines: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-lines`, {
507
+ query: options,
508
+ unwrapData: false,
509
+ }),
510
+ /** Ships on a connection — optionally filter by cruise line. */
511
+ listShips: (connectionId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/ships`, {
512
+ query: options,
513
+ unwrapData: false,
514
+ }),
515
+ /** Per-connection ship lookup. */
516
+ getShip: (connectionId, shipId) => this.transport.request(`/connect/v1/connections/${connectionId}/ships/${shipId}`, { unwrapData: false }),
517
+ /** Cabin categories for a ship on a connection. */
518
+ listCabinCategories: (connectionId, shipExternalId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/ships/${shipExternalId}/cabin-categories`, {
519
+ query: options,
520
+ unwrapData: false,
521
+ }),
522
+ /** Per-connection cruise search; reads the local cache. */
523
+ search: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/cruises/search`, { body: query, method: "POST", unwrapData: false }),
524
+ /**
525
+ * Cross-connection cruise search. Fans out across the operator's
526
+ * accessible connections, merges offers, returns a unified response with
527
+ * `connectionDiagnostics` per connection.
528
+ */
529
+ searchAcrossProviders: async (query, filter) => {
530
+ const operatorId = this.resolveOperatorId(filter);
531
+ const body = { ...query };
532
+ if (filter && (filter.connectionId !== undefined || filter.providerKey !== undefined)) {
533
+ body.filter = {
534
+ connectionId: filter.connectionId,
535
+ providerKey: filter.providerKey,
536
+ };
537
+ }
538
+ return this.transport.request(`/connect/v1/operators/${operatorId}/cruises/search`, { body, method: "POST", unwrapData: false });
539
+ },
540
+ };
541
+ // ── Cruise bookings (lock + confirm + lifecycle) ─────────────────────
542
+ cruiseBookings = {
543
+ /**
544
+ * Inquiry-mode booking: creates a sales lead, no provider hold. The
545
+ * reseller follows up out-of-band. Returns the booking with status
546
+ * `inquiry`.
547
+ */
548
+ inquire: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-bookings/inquiries`, { body: input, method: "POST", unwrapData: false }),
549
+ /**
550
+ * Lock a cruise offer (reserve mode). Connect creates a server-side
551
+ * quote with a 24h default TTL; if the adapter has a native option API
552
+ * (e.g. Viking), Connect calls it under the hood.
553
+ */
554
+ lock: (connectionId, offer, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruises/lock`, {
555
+ body: { offer, ttlHours: options?.ttlHours },
556
+ method: "POST",
557
+ unwrapData: false,
558
+ }),
559
+ /**
560
+ * Lock a concrete cruise selection. Connect resolves the current
561
+ * normalized pricing row server-side, builds the canonical offer snapshot,
562
+ * optionally calls the provider lock API, and returns the quote.
563
+ */
564
+ lockSelection: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/cruises/lock-selection`, { body: input, method: "POST", unwrapData: false }),
565
+ releaseLock: (connectionId, quoteId) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-quotes/${quoteId}`, { method: "DELETE", unwrapData: false }),
566
+ getQuote: (connectionId, quoteId) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-quotes/${quoteId}`, { unwrapData: false }),
567
+ /**
568
+ * Confirm a held offer into a booking (reserve mode). `idempotencyKey`
569
+ * becomes `Idempotency-Key` and is enforced server-side against the
570
+ * quote.
571
+ */
572
+ confirm: (connectionId, input, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-bookings`, {
573
+ body: input,
574
+ headers: withIdempotency(options?.idempotencyKey),
575
+ method: "POST",
576
+ unwrapData: false,
577
+ }),
578
+ cancel: (connectionId, bookingId, options) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-bookings/${bookingId}/cancel`, {
579
+ body: options?.reason ? { reason: options.reason } : {},
580
+ method: "POST",
581
+ unwrapData: false,
582
+ }),
583
+ get: (connectionId, bookingId) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-bookings/${bookingId}`, { unwrapData: false }),
584
+ list: (connectionId, query) => this.transport.request(`/connect/v1/connections/${connectionId}/cruise-bookings`, {
585
+ query: query,
586
+ unwrapData: false,
587
+ }),
588
+ /** Cross-connection booking list scoped to the operator. */
589
+ listAll: async (filter) => {
590
+ const operatorId = this.resolveOperatorId(filter);
591
+ const query = {};
592
+ if (filter?.connectionId !== undefined)
593
+ query.connectionId = filter.connectionId;
594
+ if (filter?.providerKey !== undefined)
595
+ query.providerKey = filter.providerKey;
596
+ if (filter?.status !== undefined)
597
+ query.status = filter.status;
598
+ if (filter?.mode !== undefined)
599
+ query.mode = filter.mode;
600
+ if (filter?.departureFrom !== undefined)
601
+ query.departureFrom = filter.departureFrom;
602
+ if (filter?.departureTo !== undefined)
603
+ query.departureTo = filter.departureTo;
604
+ if (filter?.limit !== undefined)
605
+ query.limit = filter.limit;
606
+ return this.transport.request(`/connect/v1/operators/${operatorId}/cruise-bookings`, { query });
607
+ },
608
+ };
609
+ // ── Flights ──────────────────────────────────────────────────────────
610
+ flights = {
611
+ search: (input) => this.transport.request("/connect/v1/flights/search", {
612
+ body: input,
613
+ method: "POST",
614
+ }),
615
+ /**
616
+ * Server-Sent Events variant of multi-connection search. Returns the raw
617
+ * `Response` so callers can stream events with their preferred parser.
618
+ */
619
+ searchStream: (input, options) => this.transport.fetchRaw("/connect/v1/flights/search-stream", {
620
+ body: input,
621
+ headers: { accept: "text/event-stream" },
622
+ method: "POST",
623
+ signal: options?.signal,
624
+ }),
625
+ searchOnConnection: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/search`, { body: input, method: "POST" }),
626
+ price: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/price`, { body: input, method: "POST" }),
627
+ book: (connectionId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders`, { body: input, method: "POST" }),
628
+ getOrder: (connectionId, orderId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}`),
629
+ cancelOrder: (connectionId, orderId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}`, { method: "DELETE" }),
630
+ ticketOrder: (connectionId, orderId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/ticket`, { method: "POST" }),
631
+ getSeatMap: (connectionId, orderId, segmentId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/seats/${segmentId}`),
632
+ selectSeats: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/seats`, { body: input, method: "POST" }),
633
+ getAncillaries: (connectionId, orderId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/ancillaries`),
634
+ addAncillary: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/ancillaries`, { body: input, method: "POST" }),
635
+ checkIn: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/checkin`, { body: input, method: "POST" }),
636
+ exchange: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/exchange`, { body: input, method: "POST" }),
637
+ refund: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/refund`, { body: input, method: "POST" }),
638
+ voidOrder: (connectionId, orderId) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/void`, { method: "POST" }),
639
+ addServiceRequest: (connectionId, orderId, input) => this.transport.request(`/connect/v1/connections/${connectionId}/flights/orders/${orderId}/ssr`, { body: input, method: "POST" }),
640
+ };
641
+ }
642
+ export function createVoyantConnectClient(options) {
643
+ return new VoyantConnectClient(options);
644
+ }